waitpid#
המתנה לתהליך־ילד ספציפי שיסתיים ואיסופו.
waitpid הוא המקבילה הממוקדת של wait. היכן ש־wait אוסף כל ילד וחוסם עד שאחד יוצא, waitpid מאפשר לנקב איזה ילד (או איזו קבוצת ילדים) להמתין לו, ומאפשר לבדוק במקום לחסום על־ידי העברת WNOHANG ב־FLAGS. סטטוס היציאה של הילד הנאסף נוחת ב־$?, זהה ל־wait.
תקציר#
use POSIX ":sys_wait_h";
waitpid $pid, 0; # block until $pid exits
waitpid $pid, WNOHANG; # poll: is $pid done yet?
waitpid -1, WNOHANG; # reap any ready zombie, don't block
מה מקבלים בחזרה#
ה־PID של הילד הנאסף בהצלחה.
0כאשרWNOHANGמוגדר ולפחות ילד תואם אחד קיים אך אף אחד מהם עדיין לא הסתיים - כלומר ״עדיין רץ, לנסות שוב מאוחר יותר״.-1כאשר אף תהליך־ילד אינו תואם אתPID(מעולם לא היה קיים, כבר נאסף, או - במערכות שמבצעות איסוף אוטומטי - נקטף על־ידי הקרנל בלי עזרת Perl).
סטטוס יציאת הילד נכתב ל־$? וסטטוס הפלטפורמה הגולמי ל־${^CHILD_ERROR_NATIVE} בכל איסוף מוצלח. יש לפענח את $? באותו אופן כמו לאחר wait או קריאת backtick:
my $reaped = waitpid $pid, 0;
if ($reaped > 0) {
my $exit = $? >> 8;
my $signal = $? & 127;
my $core = $? & 128;
}
מה משמעות PID#
PID בוחר אילו ילדים waitpid מוכן לאסוף:
PID חיובי - להמתין לאותו ילד ספציפי.
-1בחזרה משמעו אין ילד כזה (כבר נאסף, או שמעולם לא היה שלך).0- להמתין לכל ילד בקבוצת התהליכים הנוכחית. שימושי כאשר התוכנית הצטרפה לקבוצת תהליכים עם תהליכים משתפי־פעולה אחרים ורוצים לאסוף רק את שלך.-1- להמתין לכל ילד, באותו תחום כמוwait.פחות מ־
-1- להמתין לכל ילד שמזהה קבוצת התהליכים שלו שווה לערך המוחלט שלPID.waitpid -$pgid, 0ממתין לכל חבר בקבוצת תהליכים$pgid.
FLAGS הוא מסיכת ביטים. הדגל הנייד הוא WNOHANG, ההופך את הקריאה ללא־חוסמת: אם אף ילד תואם לא יצא, waitpid מחזיר 0 מיד במקום לישון. FLAGS של 0 משמעו ״לחסום עד שמשהו ניתן לאיסוף״, וממומש בכל פלטפורמה ש־Perl רץ עליה - גם באלה ללא קריאת מערכת מקומית waitpid(2), שבהן Perl מחקה אותו על־ידי זכירת סטטוסי היציאה של ילדים שכבר ראה.
מצב גלובלי שהוא נוגע בו#
$?- מוגדר לסטטוס היציאה של הילד הנאסף (מוזז: בית גבוה = קודexit, 7 ביטים נמוכים = אות, ביט0x80= דגל core-dumped).${^CHILD_ERROR_NATIVE}- מוגדר למילת סטטוס הפלטפורמה הגולמית, לפני ש־Perl מנרמלת אותה ל־$?.
שני המשתנים אינם נוגעים כאשר waitpid מחזיר 0 (WNOHANG ושום דבר מוכן) או -1 (אין ילד תואם).
דוגמאות#
לחסום עד שילד ידוע יסתיים, ואז לבחון את קוד היציאה שלו:
my $pid = fork // die "fork: $!";
if ($pid == 0) {
exec "/usr/bin/gzip", "big.log" or die "exec: $!";
}
waitpid $pid, 0;
die "gzip failed with status ", $? >> 8 if $?;
לבדוק בלי לחסום - שימושי בתוך לולאת אירועים או מטפל SIGCHLD:
use POSIX ":sys_wait_h";
sub harvest_children {
while ((my $kid = waitpid -1, WNOHANG) > 0) {
warn "child $kid exited with ", $? >> 8, "\n";
}
}
האידיום הקלאסי ״לאסוף כל zombie ממתין, לא לחסום״:
use POSIX ":sys_wait_h";
1 while waitpid(-1, WNOHANG) > 0;
להמתין רק לילדים בקבוצת תהליכים ספציפית. setpgid תחילה, ואז למקד בקבוצה על־ידי שלילת המזהה שלה:
my $leader = fork // die "fork: $!";
if ($leader == 0) {
setpgid 0, 0; # become process-group leader
exec @worker_cmd or die "exec: $!";
}
setpgid $leader, $leader; # parent matches, races with child
# ... later, reap any member of that group:
while ((my $kid = waitpid -$leader, WNOHANG) > 0) {
log_exit($kid, $?);
}
הבחנה בין שלושת ערכי ההחזרה במקום אחד:
my $r = waitpid $pid, WNOHANG;
if ($r == $pid) { finalize_child($pid, $?) }
elsif ($r == 0) { still_running($pid) }
elsif ($r == -1) { warn "no such child $pid (already reaped?)" }
מקרי קצה#
WNOHANGדורשuse POSIX. הקבוע אינו בנוי־ב־Perl; הוא מגיע מהמודולPOSIX, בדרך כלל דרךuse POSIX ":sys_wait_h";. כתיבת מספר שלם חשוף (waitpid $pid, 1) עובדת ב־Linux אך אינה ניידת ומסתירה כוונה.$SIG{CHLD} = 'IGNORE'מתחרה עםwaitpid. כאשר אומרים לקרנל לאסוף ילדים אוטומטית, לא נשאר ל־waitpidמה למצוא - הוא מחזיר-1עם$!מוגדר ל־ECHILD. יש לבחור באסטרטגיית איסוף אחת לתוכנית, לא בשתיהן.PIDs שכבר נאספו מחזירים
-1. PID של ילד יחיד ניתן לאסוף רק פעם אחת; לאחר מכן, הוא נשכח. אם מעבירים את אותו PID ל־waitpidפעמיים, הקריאה השנייה מחזירה-1.PIDשלילי הוא קבוצת תהליכים, לא ״כל אחד חוץ מזה״.waitpid -1234, 0ממתין לחברי קבוצת תהליכים1234, לא ל־״כל ילד חוץ מ־PID 1234״. קוראים שאינם מכירים את מוסכמת POSIX לעיתים קוראים את זה לא נכון.WNOHANGעםPID = -1היא הצורה הסטנדרטית ״לרוקן את תור ה־zombies״. בשילוב עם לולאתwhileהיא אוספת כל ילד שכבר יצא ועוצרת ברגע שאין לקרנל יותר מה להחזיר.ערך החזרה
-1יכול גם להיות איסוף אוטומטי. במערכות מוגדרות לאסוף ילדים אוטומטית (למשל מטפל$SIG{CHLD} = 'IGNORE'המותקן בעת ההפעלה),waitpidאינו רואה ילד ומחזיר-1גם עבור PID ש־fork זה עתה. ראוperlipcלהערות הפלטפורמה.המתנה חוסמת תמיד זמינה. גם בפלטפורמות ללא קריאת מערכת מקומית
waitpid(2)אוwait4(2),waitpid PID, 0עובד - Perl מחקה אותו על־ידי שמירת סטטוסי הילדים שכבר אסף.
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
wait- לחסום עבור כל ילד; שווה־ערך ל־waitpid -1, 0במערכות התומכות בוfork- ליצור את הילדים שתאספו מאוחר יותר עםwaitpidkill- לשלוח אות לאותם PIDs וקבוצות תהליכים ש־waitpidמקבל$?- סטטוס יציאה של הילד הנאסף; יש לפענח כ־$? >> 8עבור קוד היציאה,$? & 127עבור האות${^CHILD_ERROR_NATIVE}- מילת סטטוס פלטפורמה גולמית עבור אותו ילדPOSIX- מקור שלWNOHANGושאר:sys_wait_h