Διεργασίες

alarm#

Προγραμματίζει την παράδοση ενός SIGALRM στην τρέχουσα διεργασία μετά από έναν ακέραιο αριθμό δευτερολέπτων πραγματικού χρόνου.

Η alarm οπλίζει έναν μοναδικό χρονομετρητή αντίστροφης μέτρησης ανά διεργασία. Όταν λήξει ο χρονομετρητής, ο πυρήνας παραδίδει το SIGALRM· τι θα συμβεί στη συνέχεια εξαρτάται εξ ολοκλήρου από τον εγκατεστημένο χειριστή σήματος στο %SIG. Ο χρονομετρητής τρέχει σε πραγματικό χρόνο, όχι σε χρόνο CPU, οπότε μια διεργασία που κοιμάται ή είναι μπλοκαρισμένη λαμβάνει το σήμα στην ώρα της (με επιφύλαξη μικρών αποκλίσεων χρονοπρογραμματισμού).

Σύνοψη#

alarm SECONDS
alarm

Χωρίς όρισμα, χρησιμοποιείται η τιμή του $_.

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

Ο αριθμός των δευτερολέπτων που απέμεναν στον προηγούμενο χρονομετρητή, ή 0 αν δεν εκκρεμούσε χρονομετρητής. Αυτό επιτρέπει να αποθηκεύσετε και να επαναφέρετε το alarm ενός καλούντος γύρω από μια εμφωλευμένη χρονομετρημένη λειτουργία:

my $prev = alarm 10;
# ... do something that must finish in 10s ...
alarm $prev;                        # restore caller's timer

Η alarm 0 ακυρώνει κάθε εκκρεμή χρονομετρητή και επιστρέφει τα δευτερόλεπτα που είχαν απομείνει σε αυτόν.

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

  • %SIG — η θυρίδα ALRM ονοματίζει τον χειριστή που καλείται όταν ενεργοποιείται ο χρονομετρητής. Χωρίς χειριστή, η προεπιλεγμένη συμπεριφορά του SIGALRM είναι ο τερματισμός της διεργασίας.

  • $_ — διαβάζεται όταν η alarm καλείται χωρίς όρισμα.

  • $@ — μεταφέρει το μήνυμα die έξω από το κανονικό ιδίωμα eval { alarm ...; ...; alarm 0 }.

  • $! — μια μπλοκαριστική κλήση συστήματος που διακόπτεται από SIGALRM θέτει το $! σε EINTR, αλλά η Perl επανεκκινεί αυτόματα πολλές κλήσεις συστήματος σε ορισμένα συστήματα, οπότε δεν μπορείτε να βασιστείτε ότι θα δείτε EINTR. Χρησιμοποιήστε eval / die αντ” αυτού (δείτε παρακάτω).

Το κανονικό ιδίωμα χρονικού ορίου#

Χρησιμοποιήστε την alarm με eval και die για να βάλετε ανώτατο όριο πραγματικού χρόνου σε οποιοδήποτε μπλοκ κώδικα, περιλαμβανομένης μπλοκαριστικής I/O:

eval {
    local $SIG{ALRM} = sub { die "timeout\n" };   # \n matters
    alarm $timeout;
    my $nread = sysread $sock, $buf, 4096;
    alarm 0;                                      # cancel on success
};
if ($@) {
    die $@ unless $@ eq "timeout\n";              # rethrow others
    # ... handle timeout ...
}

Τρεις λεπτομέρειες είναι κρίσιμες:

  • Η local περιορίζει το εμβέλειο του χειριστή στο μπλοκ eval ώστε να μη διαρρέει στον καλούντα.

  • Το \n στο "timeout\n" καταστέλλει το επίθεμα αρχείου/γραμμής που η die θα πρόσθετε διαφορετικά, καθιστώντας αξιόπιστο τον έλεγχο ακριβούς αντιστοιχίας στο $@.

  • Η alarm 0 μέσα στο eval ακυρώνει τον χρονομετρητή στη διαδρομή επιτυχίας. Χωρίς αυτή, ένας χρονομετρητής που έμεινε οπλισμένος μπορεί να ενεργοποιηθεί κατά τη διάρκεια άσχετου κώδικα αφού επιστρέψει το eval.

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

Απλή προθεσμία πραγματικού χρόνου:

$SIG{ALRM} = sub { die "timeout\n" };
eval {
    alarm 5;
    my $line = <$slow_pipe>;        # may block indefinitely
    alarm 0;
};
warn "gave up waiting\n" if $@ eq "timeout\n";

Εμφωλευμένοι χρονομετρητές — αποθήκευση και επαναφορά:

my $saved = alarm 30;               # remember caller's timer
do_something();
alarm $saved;                       # put it back

Ακύρωση εκκρεμούς χρονομετρητή:

my $left = alarm 0;                 # 0 if none was pending

Περιοδικός παλμός χωρίς Time::HiRes:

$SIG{ALRM} = sub {
    print "tick\n";
    alarm 1;                        # re-arm from inside the handler
};
alarm 1;
sleep 10;                           # do not mix sleep+alarm in real code

Καθυστέρηση μικρότερη του δευτερολέπτου — χρησιμοποιήστε Time::HiRes:

use Time::HiRes qw(alarm);
alarm 0.25;                         # 250 ms

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

  • Μόνο ακέραια δευτερόλεπτα. Η πυρηνική alarm αποκόπτει κλασματικά ορίσματα σε ακέραια δευτερόλεπτα. Για λεπτότερη ακρίβεια, φορτώστε το Time::HiRes, που εξάγει μια αντικαταστάτη alarm η οποία δέχεται αριθμούς κινητής υποδιαστολής.

  • Απόκλιση χρονοπρογραμματισμού. Το σήμα μπορεί να φτάσει έως και περίπου ένα δευτερόλεπτο νωρίτερα ή αργότερα λόγω του τρόπου με τον οποίο ο πυρήνας μετράει τα δευτερόλεπτα και προγραμματίζει τη διεργασία σας. Αντιμετωπίστε την alarm N ως ένα όριο όχι-πριν-από-N-μείον-1, όχι ως ακριβή προθεσμία.

  • Ένας χρονομετρητής ανά διεργασία. Κάθε κλήση alarm αντικαθιστά τον προηγούμενο χρονομετρητή· δεν υπάρχει ουρά. Η τιμή επιστροφής είναι η μόνη καταγραφή σας για ό,τι αντικαταστάθηκε.

  • Η προεπιλεγμένη συμπεριφορά είναι μοιραία. Χωρίς εγκατεστημένο χειριστή για το ALRM, η λήξη του χρονομετρητή τερματίζει τη διεργασία με Alarm clock. Εγκαταστήστε έναν χειριστή πριν οπλίσετε τον χρονομετρητή.

  • Μην το συνδυάζετε με sleep. Σε πολλά συστήματα η sleep υλοποιείται πάνω στην alarm, οπότε το να οπλίσετε τη μία ακυρώνει την άλλη. Διαλέξτε μία ανά μπλοκ.

  • Το EINTR δεν είναι αξιόπιστο. Η Perl εγκαθιστά χειριστές σημάτων με SA_RESTART σε πολλές πλατφόρμες, οπότε ένα SIGALRM που διακόπτει μια μπλοκαριστική κλήση συστήματος επανεκκινεί διαφανώς την κλήση αντί να επιστρέψει με την $! σε EINTR. Ο φορητός τρόπος για την επιβολή προθεσμίας είναι eval / die από τον χειριστή, όπως φαίνεται παραπάνω.

  • Τα σήματα παραδίδονται μεταξύ των τελεστών Perl. Μέσα σε σφιχτό βρόχο καθαρής Perl ο χειριστής τρέχει στο επόμενο ασφαλές σημείο, όχι ακαριαία· μια κλήση XS που δεσμεύει την CPU μπορεί να καθυστερήσει περαιτέρω την παράδοση.

  • Η fork εκκαθαρίζει τον χρονομετρητή. Οι θυγατρικές διεργασίες ξεκινούν χωρίς εκκρεμές alarm ανεξάρτητα από το τι είχε οπλίσει η γονική.

  • Η exec εκκαθαρίζει τον χρονομετρητή στο Linux· η εικόνα της διεργασίας αντικατάστασης ξεκινά χωρίς εκκρεμές SIGALRM.

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

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

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

  • sleep — καθυστέρηση πραγματικού χρόνου· συχνά υλοποιείται πάνω στην alarm, οπότε οι δύο δεν συνδυάζονται

  • die — εγείρει από τον χειριστή ALRM την εξαίρεση που πιάνει η eval για την υλοποίηση χρονικού ορίου

  • eval — πιάνει το die από τον χειριστή και επιτρέπει στο πρόγραμμα να ανακάμψει από χρονικό όριο

  • %SIG — όπου εγκαθίσταται ο χειριστής ALRM· ο χειριστής αποφασίζει τι σημαίνει ο χρονομετρητής

  • select — η μορφή τεσσάρων ορισμάτων δέχεται χρονικό όριο κινητής υποδιαστολής και αποτελεί εναλλακτική της alarm για προθεσμίες I/O

  • Time::HiRes — απευθείας αντικαταστάτης της alarm που δέχεται αριθμούς κινητής υποδιαστολής μικρότερους του δευτερολέπτου μέσω setitimer(2)