Διαδιεργασιακή επικοινωνία

POSIX

Η διαδιεργασιακή επικοινωνία στα λειτουργικά συστήματα τύπου UNIX καθορίζεται από το πρότυπο POSIX.

Σωληνώσεις

Η παλαιότερη μέθοδος διαδιεργασιακής επικοινωνίας στο Unix είναι οι σωληνώσεις (pipes). Πρόκειται για δομές δεδομένων του πυρήνα που επιτρέπουν σε δύο συγγενείς διεργασίες να ανταλλάσσουν αμφίδρομα δεδομένα. Εμφανίζονται στον χώρο του χρήστη ως ζεύγη περιγραφέων αρχείων, χωρίς να αντιστοιχούν σε πραγματικά αρχεία, όπου ο ένας περιγραφέας είναι άκρο εγγραφής και ο άλλος άκρο ανάγνωσης. Οι συνήθεις κλήσεις συστήματος για Είσοδο / Έξοδο σε αρχεία (read(), write(), close()) βρίσκουν εφαρμογή και εδώ. Σε κάθε σωλήνωση μπορούν να αντιστοιχούν πολλαπλά ζεύγη περιγραφέων σε διαφορετικές διεργασίες, υλοποιώντας έτσι τη διαδιεργασιακή επικοινωνία. Αυτή η πολλαπλότητα γίνεται εφικτή μέσω της κατασκευής νέων διεργασιών (μοντέλο fork(), όπου η θυγατρική κληρονομεί όλους τους ανοικτούς περιγραφείς της γονικής διεργασίας). Είναι ευθύνη του προγραμματιστή να κλείσει τα άκρα ανάγνωσης ή εγγραφής στις αντίστοιχες διεργασίες ώστε να επιτύχει την επιθυμητή συμπεριφορά (π.χ. μία διεργασία να γράφει και η γονική της να διαβάζει τη σωλήνωση). Το μειονέκτημα είναι ότι με σωληνώσεις μόνο συγγενείς διεργασίες μπορούν να επικοινωνήσουν (γονική με θυγατρική ή αδελφές διεργασίες μεταξύ τους). Μία σωλήνωση δημιουργείται με την κλήση συστήματος pipe(). Η ανάγνωση από σωλήνωση με όλα τα άκρα εγγραφής κλειστά, είτε διαβάζει όλα τα δεδομένα που τοποθετήθηκαν στη σωλήνωση προτού κλείσει κάποιο άκρο εγγραφής και δεν αναγνώστηκαν ακόμη, είτε (αν δεν ισχύει αυτή η περίπτωση) η read() επιστρέφει αμέσως 0. Η εγγραφή σε σωληνώσεις με όλα τα άκρα ανάγνωσης κλειστά οδηγεί στην αποστολή του σήματος SIGPIPE από τον πυρήνα στην καλούσα διεργασία και στην αποτυχία της κλήσης write(). Σε σχέση με τις σωληνώσεις ενδιαφέρον παρουσιάζει η κλήση συστήματος dup(), η οποία δέχεται ως όρισμα έναν περιγραφέα αρχείου και τον κλωνοποιεί αναθέτοντας στο αντίγραφο τον πρώτο ελεύθερο αναγνωριστικό αριθμό (έτσι π.χ. μπορεί να αντικατασταθεί με κάποια σωλήνωση η τυπική είσοδος ή η τυπική έξοδος μίας διεργασίας).
Ακολουθεί ένα παράδειγμα χειρισμού σωληνώσεων σε γλώσσα προγραμματισμού C:
#include <stdio.h>#include <unistd.h>#include <errno.h>#include <stdlib.h>#define MAXLINE 512int main(void){
int n, fd[2];
pid_t pid;
char line[MAXLINE];
if (pipe(fd) < 0)
{perror("creating pipe"); exit(1); } /* Αποτυχία κατασκευής της σωλήνωσης */
if ((pid = fork()) < 0)
{perror("cannot fork"); exit(1); } /* Αποτυχία κατασκευής της θυγατρικής διεργασίας */
else if (pid > 0) /* γονική διεργασία */
{
close(fd[0]); /* κλείσιμο του άκρου ανάγνωσης */
write(fd[1], "message through pipe\n", 21);  /* κλήση εγγραφής σε αρχείο - εδώ χρησιμοποιείται για εγγραφή στο ανοιχτό άκρο της σωλήνωσης */
close(fd[1]);
}
else
{ /* θυγατρική διεργασία */
close(fd[1]); /* κλείσιμο του άκρου εγγραφής */
n = read(fd[0], line, MAXLINE); /* κλήση ανάγνωσης από αρχείο - εδώ χρησιμοποιείται για ανάγνωση από το ανοιχτό άκρο της σωλήνωσης */
write(STDOUT_FILENO, line, n); close(fd[0]);
}
exit(0);}
Εναλλακτικά μπορούν να χρησιμοποιηθούν κατονομασμένες σωληνώσεις (named pipes ή FIFOs). Αυτές κατά τη δημιουργία τους αντιστοιχίζονται σε οντότητες του συστήματος αρχείων, εικονικά αρχεία, ώστε να μπορούν να προσπελαστούν από μη συγγενείς διεργασίες που γνωρίζουν το μονοπάτι του εικονικού αρχείου. Ο χειρισμός τους γίνεται όπως των κοινών αρχείων, οπότε κάθε διεργασία για κάθε FIFO διατηρεί μόνο έναν περιγραφέα αρχείου ο οποίος επιστρέφεται από μία κοινή κλήση open(). Το μειονέκτημα είναι ότι ως αποτέλεσμα μία κατονομασμένη σωλήνωση λειτουργεί μονόδρομα, με κάθε διεργασία να μπορεί μόνο να γράφει σε αυτήν ή μόνο να διαβάζει από αυτήν, όπως συμβαίνει πάντα στο POSIX με τα αρχεία. Ένα FIFO δημιουργείται με την κλήση συστήματος mkfifo() και η διεργασία που το δημιούργησε πρέπει, αφού το κλείσει με την close(), να το διαγράψει από το σύστημα αρχείων με την κλήση συστήματος unlink(). Συνήθως πριν από την κλήση της close() η διεργασία αυτή διαβάζει δεδομένα από το FIFO, μέσω της κλήσης read(), τα οποία έχει γράψει εκεί κάποια άλλη διεργασία που γνώριζε το μονοπάτι του FIFO στο σύστημα αρχείων και το έχει ανοίξει για εγγραφή. Φυσιολογικά, κάθε κλήση open() για ανάγνωση μίας κατονομασμένης σωλήνωσης μπλοκάρει την καλούσα διεργασία μέχρι η σωλήνωση να ανοιχτεί από κάποια άλλη διεργασία για εγγραφή (και αντίστροφα). Αυτή η συμπεριφορά μπορεί να τροποποιηθεί με κατάλληλα ορίσματα (μη ανασταλτική open()) και σε αυτήν την περίπτωση το άνοιγμα για εγγραφή σε FIFO που δεν έχει ανοιχτεί για ανάγνωση αποτυγχάνει, ενώ το άνοιγμα για ανάγνωση σε FIFO που δεν έχει ανοιχτεί για εγγραφή επιστρέφει αμέσως. Oι αναγνώσεις από το FIFO διατηρούν το μέγεθος των αιτήσεων εγγραφής που προηγήθηκαν· αυτό σημαίνει πως αν μία διεργασία Α1 γράψει Ν byte, στη συνέχεια η ίδια γράψει Μ byte και τελικώς μία διεργασία Α2 διαβάσει το FIFO, η πρώτη αίτηση ανάγνωσης θα επιστρέψει τα αρχικά Ν byte και όχι τα ολικά Ν+Μ byte.
Πολλές διεργασίες μπορούν ταυτόχρονα να προσπελαύνουν το ίδιο αρχείο ή FIFO, πιθανώς για να ανταλλάξουν δεδομένα, με αποτέλεσμα να εμφανίζονται συνθήκες συναγωνισμού. Το πρόβλημα επιλύεται με τα κλειδώματα αρχείων, δηλαδή εξειδικευμένα mutex που παρέχει ο πυρήνας για αμοιβαίο αποκλεισμό. Τα κλειδώματα είναι είτε κοινόχρηστα είτε αποκλειστικά (με πολλαπλές διεργασίες να μπορούν να κατέχουν ταυτόχρονα ένα κοινόχρηστο κλείδωμα για το ίδιο αρχείο αλλά όχι ένα αποκλειστικό κλείδωμα, ή παράλληλα ένα κοινόχρηστο κι ένα αποκλειστικό κλείδωμα) και αφορούν ένα αρχείο και όχι έναν περιγραφέα αρχείου: αν ο τελευταίος κλωνοποιηθεί, με μία κλήση dup() ή fork(), δεν υπάρχουν πλέον δύο κλειδώματα αλλά δύο αναφορές στο ίδιο κλείδωμα. Τα κλειδώματα αρχείων είναι προσπελάσιμα μέσω της εξειδικευμένης κλήσης συστήματος flock() ή στο πλαίσιο της fcntl(), μίας γενικής χρήσης κλήσης χειρισμού αρχείων. Όταν μία διεργασία ανοίγει ένα αρχείο για ανάγνωση ζητά ένα κοινόχρηστο κλείδωμα, ενώ όταν το ανοίγει για εγγραφή ζητά ένα αποκλειστικό κλείδωμα. Στην τελευταία περίπτωση, αν το κλείδωμα του αρχείου το κατέχει κάποια άλλη διεργασία τότε η καλούσα μπλοκάρει μέχρι το κλείδωμα να ελευθερωθεί.

Διαδιεργασιακή επικοινωνία του System V

Μία άλλη οικογένεια μεθόδων διαδιεργασιακής επικοινωνίας στο Unix είναι οι μέθοδοι IPC του Unix System V, οι οποίες διαφέρουν από τις προηγούμενες (μη αντιστοιχίζοντας κάποια οντότητα σε περιγραφείς αρχείων) αλλά μεταξύ τους έχουν κοινά χαρακτηριστικά.