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

Διομήδης Σπινέλλης
Τμήμα Διοικητικής Επιστήμης και Τεχνολογίας
Οικονομικό Πανεπιστήμιο Αθηνών
dds@aueb.gr

Η κλήση fork

Η κλήση fork() διασπά την τρέχουσα διεργασία σε δύο: το γονέα, και το παιδί. Και οι δύο διεργασίες αποτελούνται από τον ίδιο κώδικα, τα ίδια δεδομένα, και βρίσκονται στο ίδιο στάδιο της εκτέλεσής τους. Το παιδί κληρονομεί από το γονέα διάφορα στοιχεία όπως τους περιγραφείς των ανοικτών αρχείων, και το περιβάλλον εκτέλεσης. Ο διαχωρισμός του γονέα από το παιδί γίνεται με βάση την τιμή που επιστρέφει η fork: σε περίπτωση επιτυχίας, στο γονέα επιστρέφει τον κωδικό της διεργασίας του παιδιού, ενώ στο παιδί επιστρέφει 0. (Το παιδί μπορεί να βρει τον κωδικό της διεργασίας του γονέα με την κλήση getppid() (get parent process id)). Τυπικά η κλήση της fork() γίνεται με βάση το παρακάτω μοντέλο:
	int pid;

	pid = fork();
	if (pid == 0) {
		/* I am the child process */
		...
	} else {
		/* I am the parent process */
		...
	}

Παράδειγμα

Υλοποιήστε δύο διεργασίες που εκτελούνται παράλληλα τυπώνοντας διαφορετικά στοιχεία (π.χ. αυξανόμενους αριθμούς). Παρατηρήστε το αποτέλεσμα του χρονοπρογραμματισμού στην αλλαγή της διεργασίας που εκτελείται. (Επιτρέψτε σε κάθε διεργασία να τυπώσει τουλάχιστον 10000 γραμμές).

Η οικογένεια κλήσεων exec

Η οικογένεια κλήσεων exec() αντικαθιστά τη διεργασία που εκτελείται με μια νέα διεργασία που βασίζεται στο πρόγραμμα που δίδεται σαν παράμετρος στην exec. Ένα απλό μέλος της οικογένειας, η execlp(), λαμβάνει ως παραμέτρους, το όνομα ή το μονοπάτι της εντολής, τη μηδενική παράμετρο του πίνακα argv της εντολής (τυπικά, πάλι το όνομά της) και, τις υπόλοιπες παραμέτρους της εντολής τερματισμένες με 0. Αν ως πρώτη παράμετρος δε δοθεί μονοπάτι, τότε η εντολή εντοπίζεται με βάση μονοπάτι των εντολών που έχει καθορίσει ο χρήστης στη μεταβλητή PATH. Το παρακάτω παράδειγμα εκτελεί την εντολή ls -la:
	execlp("/bin/ls", "ls", "-la", 0);
Για να μπορέσει να συνεχίσει η εκτέλεση της αρχικής διεργασίας η exec() συνδιάζεται συχνά με τη fork(). Στοιχεία όπως σωληνώσεις και αλλαγή της εισόδου και εξόδου δεν μπορούν να προσδιοριστούν στην exec μια και αυτά τα χειρίζεται ο φλοιός και όχι ο πυρήνας.

Παράδειγμα

Υλοποιήστε μια διεργασία η οποία ξεκινά ένα παιδί το οποίο εκτελεί την εντολή ls.

Διαδιεργασιακή επικοινωνία με σωληνώσεις

Η κλήση pipe() λαμβάνει ως παράμετρο έναν πίνακα fd δύο ακεραίων και θέτει τις τιμές τους σε περιγραφείς δύο άκρων μιας σωλήνωσης. Ο περιγραφέας fd[0] προσδιορίζει το άκρο της σωλήνωσης από το οποίο μπορούμε να διαβάσουμε ενώ ο περιγραφέας fd[1] προσδιορίζει το άκρο της σωλήνωσης στο οποίο γράφουμε. Τα στοιχεία που γράφονται στο ένα άκρο της σωλήνωσης μπορούν να διαβαστούν από το άλλο. Επειδή οι περιγραφείς κληρονομούνται ανάμεσα σε διεργασίες η σωλήνωση μπορεί να χρησιμοποιηθεί για διαδιεργασιακή επικοινωνία. Ένα απλό παράδειγμα χρήσης της pipe() είναι το παρακάτω:
	int fd[2], n;
	char buff[100];

	pipe(fd);
	write(fd[1], "hello", 5);
	n = read(fd[0], buff, 100);
	write(1, buff, n);

Παράδειγμα

Με τη χρήση της pipe() στείλτε ένα μήνυμα από μια διεργασία σε άλλη την οποία δημιουργήσατε με τη fork().

Βιβλιογραφία