תהליכים

kill#

לשלוח אות לרשימת תהליכים.

kill הוא הקצה הקדמי של Perl לקריאת המערכת kill(2). הוא מספק את SIGNAL לכל תהליך שמזוהה על־ידי LIST ומחזיר את מספר הארגומנטים שהאות נשלח אליהם בהצלחה. זוהי גם הדרך הסטנדרטית לשאול את הקרנל ״האם ה־PID הזה עדיין קיים והאם מותר לי לשלוח לו אות?״ מבלי להפריע ליעד בפועל, דרך הצורה signal-zero.

תקציר#

kill SIGNAL, LIST
kill SIGNAL            # tainting side-effect only; returns 0

מה מקבלים בחזרה#

מספר הארגומנטים ב־LIST שהועברו בהצלחה ל־kill(2). זה אינו בהכרח מספר התהליכים שאכן קיבלו את האות - כאשר SIGNAL שלילי, או כאשר PID מזהה קבוצת תהליכים, ארגומנט אחד יכול לכוון לתהליכים רבים. בשגיאות הרשאה או ״אין תהליך כזה״ עבור ארגומנט, אותו ארגומנט אינו נספר ו־$! מוגדר ל־errno המתאים (EPERM, ESRCH, EINVAL).

my $cnt = kill 'HUP', $child1, $child2;
kill 'KILL', @goners;

עם LIST ריקה, לא נשלח אות וערך ההחזרה הוא 0. צורת kill SIGNAL חשופה נכתבת לעיתים אך ורק כדי להפעיל בדיקות taint על SIGNAL; ראו perlsec.

SIGNAL: שם או מספר#

SIGNAL יכול להיות או שם אות (מחרוזת) או מספר אות. צורת המחרוזת מומלצת לניידות מכיוון שלאותו אות יכולים להיות מספרים שונים במערכות הפעלה שונות.

שם אות יכול לשאת את התחילית SIG או לא - 'TERM' ו־'SIGTERM' קוראים בשם לאותו אות.

kill 'TERM', $pid;      # portable
kill 'SIGTERM', $pid;   # also fine
kill 15, $pid;          # SIGTERM on Linux; may differ elsewhere
kill 9, $pid;           # SIGKILL on Linux

רשימת שמות האותות הזמינים בפלטפורמה הנוכחית חיה ב־$Config{sig_name}, שמסופקת על־ידי המודול Config.

SIGNAL 0: בדיקת חיוּת#

אם SIGNAL הוא המספר 0 או המחרוזת 'ZERO' (או 'SIGZERO'), שום אות אינו מסופק בפועל. kill(2) עדיין מבצע את בדיקת ההרשאה שלו, כך שהקריאה אומרת לך האם יהיה אפשרי לשלוח אות לתהליך - כלומר, התהליך קיים ובבעלות אותו משתמש, או שהקורא הוא super-user.

if (kill 0, $pid) {
    # process exists and we may signal it
}
elsif ($!{ESRCH}) {
    # no such process - it has exited and been reaped
}
elsif ($!{EPERM}) {
    # it exists but we are not allowed to signal it
}

זהו המבנה הקנוני לבדיקה שתהליך בן עדיין חי, אפילו כ־zombie, ולא שינה את ה־UID שלו. הוא אינו קוצר את הבן - יש להשתמש ב־waitpid לשם כך.

SIGNAL שלילי: לשלוח אות לקבוצת תהליכים#

שם או מספר אות שלילי שולח אות לקבוצת תהליכים שלמה במקום לתהליך יחיד. kill '-KILL', $pgrp ו־kill -9, $pgrp שניהם שולחים SIGKILL לכל תהליך בקבוצה שמזוהה על־ידי $pgrp.

kill '-TERM', $pgrp;    # polite shutdown of the whole group
kill -9, $pgrp;         # forced

אותות חיוביים הם כמעט תמיד מה שרוצים. יש לפנות לאות שלילי רק כאשר היעד הוא בכוונה מנהיג של קבוצת תהליכים (לדוגמה תהליך בן שמיקמת בקבוצה משלו עם setpgrp כך שניתן לסיים את תת־העץ בבת אחת).

PID 0, PIDs שליליים#

ההתנהגות של kill כאשר PID הוא אפס או שלילי תלויה במערכת ההפעלה. במערכות תואמות POSIX (כולל Linux):

  • PID 0 - לשלוח אות לקבוצת התהליכים של השולח עצמו.

  • PID -1 - לשלוח אות לכל תהליך שלקורא יש הרשאה לשלוח לו אות (super-user: כל המערכת, פחות כמה PIDs מיוחדים).

  • כל PID שלילי אחר - מתנהג כמו SIGNAL שלילי מול הערך המוחלט: לשלוח אות לקבוצת התהליכים שה־PGID שלה שווה ל־abs(PID).

אם גם SIGNAL וגם ה־PID שליליים, התוצאה לא־מוגדרת; Perl עתידי עשוי להתריע. אין לשלב את השניים.

דוגמאות#

לבקש בנימוס מתהליך בן להיכבות, ואז להסלים:

kill 'TERM', $child;
sleep 2;
kill 'KILL', $child if kill 0, $child;
waitpid $child, 0;

לא לקצור כלום, רק לבדוק האם קובץ PID עדיין נוקב בשם תהליך חי:

open my $fh, "<", "/run/daemon.pid" or die $!;
chomp(my $pid = <$fh>);
close $fh;

if (kill 0, $pid) {
    print "daemon alive (pid $pid)\n";
} else {
    print "stale pid file: $!\n";
    unlink "/run/daemon.pid";
}

לשלוח אות לקבוצת תהליכים שלמה שנוצרה על־ידי תהליך בן של pipeline של shell:

my $pgrp = getpgrp($child);
kill '-TERM', $pgrp;

להתקין מטפל ולשלוח לעצמך אות:

local $SIG{USR1} = sub { warn "got USR1\n" };
kill 'USR1', $$;        # $$ is our own PID

לשדר למספר workers ולגלות לכמה מהם היה אפשר להגיע:

my @workers = (1234, 1235, 9999);   # 9999 has exited
my $hit = kill 'HUP', @workers;     # probably 2
warn "lost a worker: $!\n" if $hit < @workers;

מקרי קצה#

  • LIST ריקה: kill 'TERM' לא שולח כלום ומחזיר 0. הקריאה עדיין מעריכה את SIGNAL, וזוהי הסיבה היחידה לכתוב אותה (הפצת taint).

  • שם אות לא־מוכר: מעלה חריגה (Unrecognized signal name "FOO"). יש לאמת מול $Config{sig_name} אם השם מגיע מקלט משתמש.

  • $! אחרי הצלחה חלקית: kill עובר על LIST משמאל לימין. $! משקף את הארגומנט הכושל האחרון, לא את כולם. אם נדרשות שגיאות פר־PID, יש לקרוא ל־kill פעם אחת לכל PID.

  • Zombies עדיין נחשבים כחיים: kill 0, $zombie_pid מחזיר אמת עד שהתהליך הבן נקצר עם wait או waitpid. לרוב זה מה שרוצים לבדיקת חיוּת; לעיתים זה מפתיע.

  • מסירת אותות אסינכרונית: kill מוצלח אומר רק שהאות נכנס לתור. המטפל ב־%SIG בתהליך הקולט עשוי לרוץ מאוחר יותר, ומנגנון safe-signal של Perl דוחה מסירה לגבול ה־opcode הבא.

  • שליחה לעצמך: kill SIGNAL, $$ עובד ומהווה את הדרך הרגילה לבדוק את המטפלים שלך ב־%SIG.

  • אין LIST, עם taint: תחת -T, כתיבת kill 'TERM' מפיצה את ה־taintedness של 'TERM' עבור תופעת הלוואי של הפעלת הבדיקה - לצורה אין שימוש אחר.

הבדלים מ־upstream#

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

ראו גם#

  • fork - ליצור את התהליך הבן שתשלח אליו אות מאוחר יותר

  • wait - לקצור תהליך בן שהסתיים, לנקות zombies ש־kill 0 עדיין ידווח עליהם כחיים

  • waitpid - לקצור תהליך בן ספציפי, אופציונלית non-blocking (WNOHANG) כך שהוא משתלב בצורה נקייה עם kill 0

  • getpgrp / setpgrp - לבחון ולשנות חברות בקבוצת תהליכים, על מה ש־kill עם SIGNAL שלילי פועל

  • %SIG - להתקין מטפלים לאותות שאתה שולח עם kill

  • perlipc - טיפול מלא באותות, מטפלים, וסמנטיקת safe-signal