Διεργασίες

pipe#

Ανοίγει ένα ζεύγος συνδεδεμένων filehandles - ένα για ανάγνωση, ένα για εγγραφή.

Η pipe περιτυλίσσει την κλήση συστήματος pipe(2). Δημιουργεί ένα ανώνυμο, μονόδρομο κανάλι bytes στον πυρήνα και σας παραδίδει δύο filehandles: το READHANDLE, από το οποίο μπορούν να διαβαστούν bytes, και το WRITEHANDLE, στο οποίο μπορούν να γραφτούν bytes. Οτιδήποτε γράφεται στο WRITEHANDLE γίνεται διαθέσιμο στο READHANDLE με την ίδια σειρά. Η κλασική χρήση είναι η επικοινωνία μεταξύ γονικής διεργασίας και παιδιού που παράχθηκε με fork: η μία πλευρά γράφει, η άλλη διαβάζει.

Σύνοψη#

pipe READHANDLE, WRITEHANDLE

Τι επιστρέφεται#

Αληθές σε επιτυχία, ψευδές σε αποτυχία (με το $! ρυθμισμένο στον λόγο - τυπικά EMFILE ή ENFILE όταν ο πίνακας αρχείων της διεργασίας ή του συστήματος είναι γεμάτος). Και τα δύο handles δημιουργούνται ως παρενέργειες στα ορίσματα· δεν υπάρχει τιμή επιστροφής τύπου «αντικείμενο pipe».

Σε επιτυχία κρατάτε πλέον δύο ανοιχτούς περιγραφείς αρχείων. Κάθε περιγραφέας που δημιουργείτε είναι περιγραφέας που πρέπει να closeνετε - ειδικά το άκρο που κάθε διεργασία δεν χρησιμοποιεί μετά από fork (δείτε Οριακές περιπτώσεις).

Καθολική κατάσταση που επηρεάζει#

  • $! - τίθεται σε αποτυχία στο errno από την υποκείμενη κλήση pipe(2).

  • $| - autoflush στο handle εγγραφής. Η print ενταμιεύει μέσω PerlIO, οπότε χωρίς autoflush ο αναγνώστης μπορεί να μη δει τίποτα μέχρι ο εγγραφέας να εξέλθει ή να γεμίσει ο ενταμιευτής. Ρυθμίστε το $| στο handle εγγραφής (τυπικά μέσω select) πριν την εγγραφή, αλλιώς το πρωτόκολλό σας θα μπλοκάρει σε deadlock.

  • $^F - το μέγιστο filehandle συστήματος. Νέοι περιγραφείς με fileno μεγαλύτερο από $^F (προεπιλογή 2, δηλαδή πάνω από STDERR) λαμβάνουν αυτόματα τη σημαία close-on-exec, οπότε δεν διαρρέουν σε προγράμματα που ξεκινούν με exec. Χαμηλώστε το $^F για να κρατήσετε το pipe ανοιχτό κατά την exec· ανεβάστε το, ή κάντε dup σε χαμηλόαριθμο fd, για να επιβάλετε κληρονόμηση.

Παραδείγματα#

Ένας γονέας γράφει μια γραμμή· ένα forked παιδί τη διαβάζει:

pipe my $reader, my $writer or die "pipe: $!";

my $pid = fork // die "fork: $!";
if ($pid == 0) {
    # child: reads only
    close $writer;
    my $line = <$reader>;
    print "child saw: $line";
    close $reader;
    exit 0;
}

# parent: writes only
close $reader;
$writer->autoflush(1);
print $writer "hello from parent\n";
close $writer;
waitpid $pid, 0;

Το παιδί αναφέρει την άλλη κατεύθυνση - ένα παιδί παράγει δεδομένα, ο γονέας τα καταναλώνει:

pipe my $reader, my $writer or die "pipe: $!";

my $pid = fork // die "fork: $!";
if ($pid == 0) {
    close $reader;
    $writer->autoflush(1);
    print $writer "$_\n" for 1 .. 5;
    close $writer;
    exit 0;
}

close $writer;
while (my $line = <$reader>) {
    print "got: $line";
}
close $reader;
waitpid $pid, 0;

Autoflush από την πλευρά επιλεγμένου handle, για κώδικα που προηγείται των κλήσεων μεθόδων IO::Handle:

pipe my $reader, my $writer or die "pipe: $!";
my $old = select $writer; $| = 1; select $old;

Για τις περισσότερες χρήσεις, η open με pipe mode είναι απλούστερη - κάνει fork, pipe και exec για εσάς:

open my $fh, '|-', 'gzip', '-c' or die $!;   # write to gzip's stdin
print $fh "data\n";
close $fh;

open my $fh, '-|', 'ls', '-l'   or die $!;   # read from ls's stdout
while (my $line = <$fh>) { print $line }
close $fh;

Καταφύγετε στη σκέτη pipe όταν χρειάζεστε και τις δύο κατευθύνσεις, χρειάζεται να διατηρήσετε το παιδί χωρίς exec, ή χτίζετε κάτι που οι δύο pipe-μορφές της open δεν καλύπτουν - τότε τα IPC::Open2 / IPC::Open3 περιτυλίγουν τον χορό pipe + fork + exec για εσάς.

Οριακές περιπτώσεις#

  • Κλείστε το άκρο που δεν χρησιμοποιείτε. Μετά από fork, και οι δύο διεργασίες κρατούν και τους δύο περιγραφείς. Ο αναγνώστης πρέπει να κλείσει το άκρο εγγραφής, και ο εγγραφέας πρέπει να κλείσει το άκρο ανάγνωσης. Αν το παραλείψετε λαμβάνετε το κλασικό bug του pipe: το <$reader> του αναγνώστη δεν επιστρέφει ποτέ EOF, επειδή ο πυρήνας εξακολουθεί να βλέπει ανοιχτό έναν εγγραφέα (το ίδιο, αδρανές, αντίγραφο του $writer του αναγνώστη). Το πρόγραμμα κρεμάει.

  • Η εγγραφή σε pipe χωρίς αναγνώστη εγείρει SIGPIPE. Μόλις κλείσει κάθε αντίγραφο του άκρου ανάγνωσης, η επόμενη print στην πλευρά εγγραφής παραδίδει SIGPIPE, του οποίου η προεπιλεγμένη ενέργεια είναι ο τερματισμός της διεργασίας. Εγκαταστήστε $SIG{PIPE} = 'IGNORE' (ή χειριστή) για να το μετατρέψετε σε κανονική αποτυχία εγγραφής με το $! ρυθμισμένο σε EPIPE, που μπορείτε στη συνέχεια να ελέγξετε.

  • Deadlock σε βρόχους pipe. Δύο διεργασίες που γράφουν η μία στην άλλη μέσω pipes θα μπλοκάρουν για πάντα αν γεμίσουν και οι δύο ενταμιευτές εγγραφής πριν διαβάσει οποιαδήποτε από τις δύο. Οι ενταμιευτές pipe του πυρήνα είναι μικροί (τυπικά 64 KiB). Αποστραγγίστε τη μία πλευρά, χρησιμοποιήστε I/O χωρίς μπλοκάρισμα, ή χρησιμοποιήστε select / IO::Select για πολυπλεξία.

  • Το buffering κρύβει εγγραφές. Χωρίς $|$writer->autoflush(1)) στην πλευρά εγγραφής, τα δεδομένα κάθονται στον ενταμιευτή του PerlIO μέχρι να γεμίσει ο ενταμιευτής ή να κλείσει το handle. Ένας αναγνώστης μπλοκαρισμένος σε <$reader> θα περιμένει - συχνά επ” αόριστον - δεδομένα που έχουν ήδη «γραφτεί» από τη σκοπιά του εγγραφέα.

  • Μόνο μονόδρομη. Η pipe παράγει μονόδρομο κανάλι: εγγραφή στο WRITEHANDLE, ανάγνωση από το READHANDLE, όχι το αντίστροφο. Για αμφίδρομη επικοινωνία χρησιμοποιήστε socketpair ή δύο pipes.

  • Δυαδικά δεδομένα. Τα handles που επιστρέφει η pipe ξεκινούν στην προεπιλεγμένη στοίβα στρωμάτων PerlIO. Αν μεταφέρετε ωμά bytes διαμέσου του pipe, εφαρμόστε binmode και στα δύο άκρα· διαφορετικά στρώματα CRLF ή κωδικοποίησης που κληρονομούνται από τις προεπιλογές της διεργασίας μπορούν να ξαναγράψουν τα δεδομένα σας.

  • Close-on-exec και fileno. Σε συστήματα με FD_CLOEXEC, κάθε νέος περιγραφέας του οποίου το fileno υπερβαίνει το $^F δημιουργείται με τη σημαία close-on-exec ενεργοποιημένη. Οι περιγραφείς pipe τυπικά προσγειώνονται αρκετά πάνω από το 2, οπότε εξ ορισμού δεν επιβιώνουν της exec - που είναι κανονικά αυτό που θέλετε. Χαμηλώστε το $^F πριν καλέσετε την pipe αν προτίθεστε να περάσετε τους περιγραφείς σε πρόγραμμα που εκτελείται μέσω exec.

Διαφορές από το upstream#

Πλήρως συμβατό με το upstream Perl 5.42.

Δείτε επίσης#

  • fork - το άλλο μισό του ιδιώματος pipe· η pipe σχεδόν πάντα καλείται ακριβώς πριν από μια fork

  • open - οι μορφές '|-' και '-|' κάνουν fork, pipe και exec παιδιού σε μία κλήση· προτιμήστε τες όταν χρειάζεστε ακριβώς μία κατεύθυνση προς μία εξωτερική εντολή

  • close - το κλείσιμο του άκρου που δεν χρησιμοποιείται σε κάθε διεργασία είναι υποχρεωτικό, όχι προαιρετικό

  • read - αναγνώσεις προσανατολισμένες σε bytes από το READHANDLE όταν η προσανατολισμένη σε γραμμές μορφή <$fh> είναι λάθος σχήμα

  • print - ο κανονικός τρόπος για να σπρώξετε δεδομένα στο WRITEHANDLE· θυμηθείτε το $|

  • fileno - ανακτά τον υποκείμενο περιγραφέα αρχείου, π.χ. για να τον παραδώσετε σε select / IO::Select ή σε κλήση συστήματος χαμηλότερου επιπέδου