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σχεδόν πάντα καλείται ακριβώς πριν από μιαforkopen- οι μορφές'|-'και'-|'κάνουν fork, pipe καιexecπαιδιού σε μία κλήση· προτιμήστε τες όταν χρειάζεστε ακριβώς μία κατεύθυνση προς μία εξωτερική εντολήclose- το κλείσιμο του άκρου που δεν χρησιμοποιείται σε κάθε διεργασία είναι υποχρεωτικό, όχι προαιρετικόread- αναγνώσεις προσανατολισμένες σε bytes από τοREADHANDLEόταν η προσανατολισμένη σε γραμμές μορφή<$fh>είναι λάθος σχήμαprint- ο κανονικός τρόπος για να σπρώξετε δεδομένα στοWRITEHANDLE· θυμηθείτε το$|fileno- ανακτά τον υποκείμενο περιγραφέα αρχείου, π.χ. για να τον παραδώσετε σεselect/IO::Selectή σε κλήση συστήματος χαμηλότερου επιπέδου