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/OTime::HiRes— απευθείας αντικαταστάτης τηςalarmπου δέχεται αριθμούς κινητής υποδιαστολής μικρότερους του δευτερολέπτου μέσωsetitimer(2)