Διεργασίες

wait#

Μπλοκάρει μέχρι να τερματιστεί οποιαδήποτε διεργασία παιδί και τη συγκομίζει.

Η wait είναι το απλούστερο front end για την υποκείμενη κλήση συστήματος wait(2). Αναστέλλει την τρέχουσα διεργασία μέχρι να τερματιστεί ένα από τα παιδιά της, συλλέγει την κατάσταση εξόδου αυτού του παιδιού στα $? και ${^CHILD_ERROR_NATIVE}, και επιστρέφει το PID του συγκομισμένου παιδιού. Αν δεν υπάρχουν παιδιά για αναμονή, επιστρέφει αμέσως -1. Είναι ακριβώς ισοδύναμη με την waitpid(-1, 0).

Σύνοψη#

my $pid = wait;

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

Το PID του παιδιού που συγκομίστηκε (θετικός ακέραιος), ή -1 αν δεν έχουν απομείνει ασυγκόμιστα παιδιά σε αναμονή. Η κατάσταση του παιδιού πηγαίνει στο $?· η ακατέργαστη, εξειδικευμένη για το OS λέξη κατάστασης πηγαίνει στο ${^CHILD_ERROR_NATIVE}.

Για να αποκωδικοποιήσετε το $? μετά από επιτυχή wait:

my $exit   = $? >> 8;       # normal exit code (0..255)
my $signal = $? & 0x7f;     # termination signal, if any
my $core   = $? & 0x80;     # core-dump flag

Η επιστροφή -1 δεν σημαίνει πάντα «δεν είχα ποτέ παιδιά» - μπορεί επίσης να σημαίνει «κάτι τα συγκόμισε ήδη για μένα» (δείτε παρακάτω).

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

  • $? - τίθεται στην 16-bit κατάσταση αναμονής του συγκομισμένου παιδιού (υψηλό byte: κωδικός εξόδου, χαμηλό byte: σήμα τερματισμού και σημαία core). Επίσης αναγνώσιμο ως $CHILD_ERROR υπό use English.

  • ${^CHILD_ERROR_NATIVE} - η ακατέργαστη λέξη κατάστασης της πλατφόρμας, για καλούντες που θέλουν να χρησιμοποιήσουν τις POSIX μακροεντολές (WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG) από το POSIX.

  • %SIG - αν το $SIG{CHLD} είναι ρυθμισμένο σε 'IGNORE', ή χειριστής εντός του καλεί ο ίδιος wait, το αποτέλεσμα αυτής της κλήσης αλλάζει. Δείτε Οριακές περιπτώσεις.

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

Δημιουργήστε ένα worker με fork, κάντε κάτι, και μετά συγκομίστε το:

my $pid = fork // die "fork: $!";
if ($pid == 0) {
    # child
    exec "/usr/bin/true" or die "exec: $!";
}
my $reaped = wait;              # blocks until the child exits
die "unexpected pid" if $reaped != $pid;
printf "child %d exited %d\n", $reaped, $? >> 8;

Συγκομιδή κάθε εκκρεμούς παιδιού σε βρόχο. Η wait επιστρέφει -1 μόλις η διεργασία δεν έχει άλλο:

while ((my $kid = wait) > 0) {
    warn "reaped $kid, status " . ($? >> 8) . "\n";
}

Διάκριση μεταξύ φυσιολογικής εξόδου και τερματισμού από σήμα:

my $pid = wait;
if ($pid > 0) {
    if    ($? == 0)       { say "clean exit" }
    elsif ($? & 0x7f)     { say "killed by signal " . ($? & 0x7f) }
    else                  { say "exit " . ($? >> 8) }
}

Αποκωδικοποίηση της εγγενούς λέξης κατάστασης με μακροεντολές POSIX, που είναι πιο φορητό από χειροκίνητη ολίσθηση bits:

use POSIX ":sys_wait_h";
my $pid = wait;
if ($pid > 0 && WIFEXITED(${^CHILD_ERROR_NATIVE})) {
    say "exit code: ", WEXITSTATUS(${^CHILD_ERROR_NATIVE});
}

Συγκομιδή χωρίς μπλοκάρισμα κάθε εκκρεμούς zombie - χρησιμοποιήστε waitpid με WNOHANG, όχι wait:

use POSIX ":sys_wait_h";
1 while waitpid(-1, WNOHANG) > 0;

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

  • Κανένα παιδί για αναμονή: επιστρέφει -1 αμέσως. Η wait δεν μπλοκάρει σε αυτή την περίπτωση - μπλοκάρει μόνο όταν τουλάχιστον ένα παιδί είναι ακόμη ζωντανό και ασυγκόμιστο.

  • $SIG{CHLD} = 'IGNORE': σε συστήματα POSIX αυτό ζητά από τον πυρήνα να συγκομίζει αυτόματα τα παιδιά. Υπό αυτή τη ρύθμιση, η wait σχεδόν πάντα επιστρέφει -1 επειδή τα παιδιά έχουν φύγει προτού ο κώδικας χρήστη μπορέσει να την καλέσει. Το $? δεν τίθεται. Αν θέλετε και «fire-and-forget» παιδιά και αναφορά κατάστασης, χρησιμοποιήστε πραγματικό χειριστή, όχι 'IGNORE'.

  • Προσαρμοσμένος χειριστής $SIG{CHLD}: αν ο χειριστής καλεί ο ίδιος waitwaitpid), μια μετέπειτα wait στην κύρια ροή δεν θα βρει τίποτα και θα επιστρέψει -1. Αποφασίστε εκ των προτέρων ποια διαδρομή κώδικα κάνει τη συγκομιδή.

  • Αλληλεπίδραση με qx// και system: και τα δύο εσωτερικά κάνουν fork, exec, και αναμονή στο δικό τους παιδί. Ένας χειριστής $SIG{CHLD} που καλεί τυφλά την wait μπορεί κατά λάθος να συγκομίσει αυτό το εσωτερικό παιδί κάτω από τη μύτη της qx// ή της system, αφήνοντάς τες ανίκανες να συλλέξουν την κατάσταση και επιστρέφοντας τυπικά -1 με το $! ρυθμισμένο σε ECHILD. Οι χειριστές θα πρέπει να επαναλαμβάνουν με waitpid(-1, WNOHANG) και να ενεργούν μόνο σε PIDs που αναγνωρίζουν.

  • Το $? γράφεται μόνο σε επιτυχημένη συγκομιδή. Μετά από επιστροφή -1, το $? διατηρεί ό,τι είχε πριν την κλήση. Μην το επιθεωρείτε εκτός αν η wait επέστρεψε θετικό PID.

  • Διακοπή από σήμα: αν παραδοθεί σήμα ενώ η wait είναι μπλοκαρισμένη και ο χειριστής επιστρέψει φυσιολογικά, η Perl επανεκκινεί την αναμονή - δεν βλέπετε EINTR σε επίπεδο Perl. Αν ο χειριστής κάνει die ή exit, ο έλεγχος εγκαταλείπει τη wait με τον συνηθισμένο τρόπο για αυτόν τον μηχανισμό.

  • Threaded builds: η wait περιμένει παιδιά της τρέχουσας διεργασίας, όχι παιδιά συγκεκριμένου νήματος. Σε πρόγραμμα πολλαπλών νημάτων, το νήμα που κάνει τη συγκομιδή είναι όποιο κάλεσε πρώτο τη wait.

  • Η τιμή επιστροφής είναι PID, όχι λογική τιμή: το if (wait) { ... } είναι αληθές τόσο για επιτυχημένες συγκομιδές όσο και για -1. Συγκρίνετε πάντα με 0 ή ελέγχετε ρητά > 0, όπως στη μορφή βρόχου παραπάνω.

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

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

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

  • waitpid - αναμονή συγκεκριμένου παιδιού, ή poll χωρίς μπλοκάρισμα με WNOHANG· η wait είναι η μορφή waitpid(-1, 0)

  • fork - δημιουργεί τα παιδιά που συγκομίζει η wait

  • exec - αυτό που τυπικά καλεί ένα forked παιδί για να γίνει διαφορετικό πρόγραμμα

  • system - fork + exec + wait σε μία κλήση· χρησιμοποιεί τη δική της εσωτερική wait και ρυθμίζει το $? για εσάς

  • kill - στέλνει σήμα σε παιδί που δεν θέλετε πλέον να περιμένετε

  • $? - εκεί όπου τοποθετείται η κατάσταση του συγκομισμένου παιδιού, κωδικοποιημένη ως υψηλό byte κωδικού εξόδου συν χαμηλό byte σήματος/core