תהליכים

alarm#

תזמון SIGALRM שיישלח לתהליך הנוכחי לאחר מספר שלם של שניות שעון־קיר.

alarm דורך טיימר ספירה לאחור יחיד לכל תהליך. כאשר הטיימר פוקע, הקרנל שולח SIGALRM; מה שקורה אחר כך תלוי כולו במטפל האות המותקן ב־%SIG. הטיימר רץ בזמן שעון־קיר, לא בזמן CPU, כך שתהליך ישן או חסום עדיין מקבל את האות בזמן (בכפוף ל־jitter של התזמון).

תקציר#

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 במקום זאת (ראו להלן).

הניב הקנוני ל־timeout#

שימוש ב־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 חליפי המקבל מספרים בנקודה צפה.

  • jitter של תזמון. האות עשוי להגיע מוקדם או מאוחר עד כשנייה אחת בגלל אופן ספירת השניות בקרנל ותזמון התהליך. יש להתייחס ל־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#

תואם מלא ל־Perl 5.42 upstream.

ראו גם#

  • sleep — השהיית שעון־קיר; לעתים קרובות ממומש על גבי alarm, ולכן השניים אינם משתלבים

  • die — מעלה את החריגה ממטפל ALRM ש־eval תופס כדי לממש timeout

  • eval — תופס את ה־die מהמטפל ומאפשר לתוכנית להתאושש מ־timeout

  • %SIG — המקום שבו מטפל ALRM מותקן; המטפל מחליט מה הטיימר משמעותו

  • select — הצורה בעלת ארבעת הארגומנטים מקבלת timeout בנקודה צפה ומהווה חלופה ל־alarm עבור תאריכי יעד של I/O

  • Time::HiRes — חליף alarm ישיר המקבל מספרים בנקודה צפה תת־שנייתיים באמצעות setitimer(2)