# Υποδιεργασίες και συνεργαζόμενες διεργασίες Η πιο συνηθισμένη εργασία IPC είναι η απλούστερη: εκτέλεση μιας εξωτερικής εντολής και επικοινωνία μαζί της. Αυτή η σελίδα καλύπτει το φάσμα, από ένα μονόδρομο άνοιγμα σωλήνα μέχρι μια αμφίδρομη συνεργαζόμενη διεργασία - ένα παιδί στο οποίο τόσο γράφετε όσο και διαβάζετε από αυτό. Οι μηχανισμοί ενός μονόδρομου ανοίγματος σωλήνα (`"-|"` και `"|-"`) καλύπτονται πλήρως στη σελίδα [Σωλήνες](../io-streams/pipes.md). Αυτή η σελίδα συνεχίζει εκεί όπου σταματά εκείνη: όταν μία κατεύθυνση δεν αρκεί, και όταν χρειάζεστε ένα δικό σας χαμηλού επιπέδου [`pipe`](../../p5/core/perlfunc/pipe.md) και [`fork`](../../p5/core/perlfunc/fork.md). ## Μία κατεύθυνση: το άνοιγμα σωλήνα Για να διαβάσετε την έξοδο μιας εντολής, ή για να τροφοδοτήσετε μια εντολή με την είσοδό της, ένα άνοιγμα σωλήνα είναι το μόνο που χρειάζεστε: ```perl open my $fh, "-|", "sort", "-u", "names.txt" or die "pipe: $!"; while (my $line = <$fh>) { ... } close $fh; ``` Αυτή είναι όλη η ιστορία για τη μονόδρομη εργασία, και αφηγείται στη σελίδα [Σωλήνες](../io-streams/pipes.md) - συμπεριλαμβανομένης της μορφής λίστας έναντι συμβολοσειράς, του γιατί η μορφή λίστας παρακάμπτει το κέλυφος, και του πώς να αποκωδικοποιήσετε την κατάσταση εξόδου του παιδιού. Ξεκινήστε από εκεί αν μία κατεύθυνση αρκεί. ## Και οι δύο κατευθύνσεις: `IPC::Open2` Όταν χρειάζεται να γράψετε σε μια εντολή *και* να διαβάσετε την απάντησή της - ένα φίλτρο που οδηγείτε διαδραστικά, μια συνεργαζόμενη διεργασία - ένας σωλήνας δεν αρκεί. Το `IPC::Open2` ανοίγει δύο: έναν προς το `stdin` του παιδιού, έναν από το `stdout` του. ```perl use IPC::Open2; my $pid = open2(my $from_child, my $to_child, "tr", "a-z", "A-Z"); print $to_child "hello\n"; close $to_child; # signal end-of-input to the child my $reply = <$from_child>; print "got: $reply"; # got: HELLO waitpid($pid, 0); ``` Η `open2` επιστρέφει το αναγνωριστικό διεργασίας του παιδιού. Η σειρά των ορισμάτων είναι η παγίδα που αξίζει να απομνημονεύσετε: **πρώτα ο περιγραφέας ανάγνωσης, δεύτερος ο περιγραφέας εγγραφής**, μετά η εντολή. `open2(OUT, IN, ...)` - ο περιγραφέας από τον οποίο διαβάζετε προηγείται του περιγραφέα στον οποίο γράφετε. ### Το αδιέξοδο γύρω από το οποίο πρέπει να σχεδιάσετε Μια συνεργαζόμενη διεργασία μπορεί να φτάσει σε αδιέξοδο, και το `IPC::Open2` δεν κάνει τίποτα για να το αποτρέψει. Το σχήμα της παγίδας: - Γράφετε ένα μεγάλο μπλοκ στο παιδί. - Το παιδί διαβάζει ένα μέρος, παράγει έξοδο, και αυτή η έξοδος γεμίζει τον σωλήνα πίσω προς εσάς. - Το παιδί μπλοκάρει γράφοντας σε έναν γεμάτο σωλήνα· εσείς μπλοκάρετε γράφοντας σε ένα παιδί που έχει σταματήσει να διαβάζει. Κανείς δεν προχωρά. Η παραπάνω συνταγή το παρακάμπτει κλείνοντας το `$to_child` πριν από την ανάγνωση - στέλνοντας ολόκληρη την είσοδο, μετά σηματοδοτώντας το τέλος αρχείου, μετά αποστραγγίζοντας την απάντηση. Αυτό λειτουργεί όταν η είσοδος χωρά άνετα στον ενταμιευτή του σωλήνα. Για απεριόριστη ροή και στις δύο κατευθύνσεις ταυτόχρονα, χρειάζεστε I/O χωρίς μπλοκάρισμα ή έναν βρόχο `select`· το σχήμα των δύο σωλήνων από μόνο του δεν αρκεί. ## Σύλληψη και του τυπικού σφάλματος: `IPC::Open3` Το `IPC::Open2` σας συνδέει με το `stdin` και το `stdout`. Όταν χρειάζεστε επίσης το `stderr` του παιδιού σε δικό του περιγραφέα, το `IPC::Open3` προσθέτει έναν τρίτο: ```perl use IPC::Open3; use Symbol qw(gensym); my $err = gensym; # a fresh anonymous handle for stderr my $pid = open3(my $to_child, my $from_child, $err, "sort"); print $to_child "banana\napple\ncherry\n"; close $to_child; my @sorted = <$from_child>; # ("apple\n", "banana\n", "cherry\n") print @sorted; # apple / banana / cherry, one per line waitpid($pid, 0); ``` Δύο διαφορές από την `open2` που πρέπει να σημειώσετε: - **Η σειρά των ορισμάτων αντιστρέφεται.** Η `open3` παίρνει πρώτα τον περιγραφέα εγγραφής, μετά τον αναγνώστη, μετά τον περιγραφέα σφάλματος: `open3(IN, OUT, ERR, ...)`. Αυτό είναι το αντίθετο της σειράς αναγνώστη-πρώτα της `open2` - μια μακροχρόνια ασυνέπεια που δαγκώνει τον καθένα μία φορά. - **Ο περιγραφέας σφάλματος χρειάζεται `gensym`.** Περάστε έναν φρέσκο ανώνυμο περιγραφέα από την `Symbol::gensym` για το `stderr`· ένα bareword ή μια απροσδιόριστη λεξιλογική μεταβλητή δεν θα το συλλέξει καθαρά. ## Φτιάχνοντας το δικό σας: `pipe` και `fork` Όταν η εντολή δεν είναι ένα εξωτερικό πρόγραμμα αλλά κώδικας Perl που θέλετε να εκτελέσετε σε μια ξεχωριστή διεργασία, χτίστε το κανάλι μόνοι σας. Ένα χαμηλού επιπέδου [`pipe`](../../p5/core/perlfunc/pipe.md) δίνει ένα συνδεδεμένο ζεύγος αναγνώστη/εγγραφέα πριν από το fork· μετά το [`fork`](../../p5/core/perlfunc/fork.md) κάθε πλευρά κρατά το άκρο που χρειάζεται και κλείνει το άλλο: ```perl pipe(my $reader, my $writer) or die "pipe: $!"; my $kid = fork() // die "fork: $!"; if ($kid == 0) { # Child writes, then exits. close $reader; # child has no use for the read end $writer->autoflush(1); print $writer "down-the-pipe\n"; close $writer; exit 0; } # Parent reads. close $writer; # parent has no use for the write end my $line = <$reader>; print "parent read: $line"; # parent read: down-the-pipe close $reader; waitpid($kid, 0); ``` Η πειθαρχία είναι ίδια με του διακομιστή που κάνει fork στη σελίδα [Υποδοχές TCP](sockets-tcp.md): **κάθε πλευρά κλείνει το άκρο που δεν χρησιμοποιεί.** Αν ο γονέας κρατήσει το άκρο εγγραφής ανοιχτό, το `close` του παιδιού δεν παράγει ποτέ τέλος αρχείου στο άκρο ανάγνωσης, και η ανάγνωση του γονέα μπλοκάρει για πάντα. Το ρητό `fork()` δημιουργεί μια ξεχωριστή διεργασία λειτουργικού συστήματος· η αυτόματη παραλληλοποίηση της PetaPerl είναι μια δεξαμενή νημάτων εντός της διεργασίας. Ένα παιδί που δημιουργήθηκε με fork εκτελεί κανονικά τις δικές του αυτόματα παραλληλοποιημένες αριθμητικές αναγωγές μέσα του, και ο παραλληλισμός του γονέα και του παιδιού δεν παρεμβαίνουν μεταξύ τους - είναι ανεξάρτητες διεργασίες με ανεξάρτητες δεξαμενές νημάτων. Για την εκτέλεση εργασίας σε πολλούς πυρήνες CPU *μέσα* σε μία διεργασία αντί σε ξεχωριστές διεργασίες, δείτε τον οδηγό [Ταυτόχρονη εκτέλεση](../../guide/concurrent-execution/index.md). ## Διασταυρούμενοι σύνδεσμοι αναφοράς - [`open`](../../p5/core/perlfunc/open.md) - το μονόδρομο άνοιγμα σωλήνα (`"-|"`, `"|-"`) - [`pipe`](../../p5/core/perlfunc/pipe.md) - ένα χαμηλού επιπέδου συνδεδεμένο ζεύγος περιγραφέων - [`fork`](../../p5/core/perlfunc/fork.md) - διασπά σε δύο διεργασίες - [`waitpid`](../../p5/core/perlfunc/waitpid.md) - συγκομίζει το παιδί και ανακτά την κατάστασή του - [`exec`](../../p5/core/perlfunc/exec.md) - αντικαθιστά την εικόνα του παιδιού με μια εντολή ## Επόμενο Για δύο μη σχετιζόμενα προγράμματα που συναντιούνται σε μια διαδρομή του συστήματος αρχείων αντί μέσω κοινού γονέα, δείτε [FIFO](fifos.md).