Managing Jobs
מערכות הפעלה מאפשרות בדרך כלל לייצג באופן מפורש קבוצות של תהליכים
ולבצע פעולות על כל התהליכים של קבוצה בבת אחת. קבוצה כזו, שנקראת חבורת
תהליכים )process group( או עבודה )job( נוצרת באופן מפורש על
ידי קריאת מערכת, וניתן לשייך אליה תהליך או תהליכים. כאשר תהליך שמשוייך
לחבורה כזו יוצר תהליך נוסף, התהליך החדש משוייך אוטומטית לחבורה )אלא
אם משייכים אותו מפורשות לחבורה אחרת(. המנגנון הזה גורם לכך שניתן לשייך
לחבורה עץ תהליכים שלם, שבו תהליכים יוצרים תהליכים אחרים, גם אם התהליכים
הללו אינם משייכים את עצמם לחבורה באופן מפורש. תוכניות מעטפת )shells(,
למשל, משתמשות במנגנון הזה על מנת לאפשר למשתמש להפסיק את פעולתה של
תוכנית שהריץ דרך המעטפת, גם אם התוכנית הזו יצרה מספר גדול של תהליכים,
שהמעטפת אינה מודעת כלל לקיומם, ושהתהליך או התהליכים שיצרו אותם אולי
כבר אינם קיימים.
בחלונות יש שני סוגים של חבורות תהליכים. סוג אחד נקרא חבורת תהליכים
)process group(. הסוג הזה דומה לחבורות תהליכים ביוניקס ולינוקס.
יוצרים חבורה כזו על ידי ניתוק של תהליך מהחבורה שהוא שייך אליה; זה
יוצר חבורה חדשה שהתהליך שנותק שייך אליה וששמה הוא מספר התהליך. הסוג
הזה מוגבל למדי, מכיון שניתן להשתמש בו רק כאשר כל התהליכים בחבורה מחוברים
לקונסולה אחת. מכיון שתוכניות גרפיות ותוכנות שרת בדרך כלל אינן מחוברות
לקונסולה, לא תמיד אפשר להשתמש בסוג הזה.
הסוג השני נקרא עבודה )job(, הוא יותר כללי, ובו נשתמש בתרגיל זה.
שיוך תהליך לעבודה:
HANDLE CreateJobObject (LPSECURITY_ATTRIBUTES sa,
LPCTSTR name);
BOOL AssignProcessToJobObject(HANDLE job,
HANDLE process);
קריאת המערכת הראשונה יוצרת עבודה עם הרשאות נתונות )NULL
עבור ברירת המחדל( ועם שם נתון. הקריאה מחזירה מזהה לעבודה. כאשר העבודה
נוצרת, שום תהליך אינו משוייך אליה )גם לא התהליך שיצר אותה(. קריאת
המערכת השניה משייכת תהליך לעבודה, כאשר גם התהליך וגם העבודה מצויינים
על ידי מזהה פתוח, לא על ידי שם העבודה ולא על ידי מספר התהליך.
כאמור, מטרת השיוך היא בדרך כלל לדאוג שהתהליך וגם כל צאצאיו יהיו משוייכים
לעבודה. אולם אם יוצרים תהליך באופן רגיל ולאחר שהוא נוצר משייכים אותו,
התהליך החדש עלול ליצור תהליכים נוספים לפני שנספיק לשייך אותו לעבודה.
במקרה כזה, לאחר שיוכו הוא אמנם יהיה משוייך, אבל הצאצאים שכבר יצר לא
ישוייכו לעבודה. על מנת למנוע מצב כזה, צריך ליצור את התהליך החדש כך
שלא יוכל לרוץ, לשייך אותו לעבודה, ורק אז להתיר לו להתחיל לרוץ. כדי
שהתהליך לא יתחיל לרוץ מייד עם יצירתו, יש ליצור אותו עם הדגל CREATE_SUSPENDED.
כדי לאפשר לו להתחיל לרוץ לאחר שיוכו לעבודה, יש לקרוא לקריאת המערכת
DWORD ResumeThread(HANDLE thread);
עם ארגומנט שהוא מזהה של החוט הראשי של התהליך. במקרה של כשלון הקריאה
הזו מחזירה -1. )חוט יכול להיות מושעה מריצה בגלל מספר
סיבות; הקריאה הזו מקטינה את מספר הסיבות באחד ומחזירה את מספר הסיבות
שנותרו, 0 במקרה שהחוט יכול כעת להמשיך לרוץ.(
הפסקת ריצה של עבודה שלמה:
BOOL TerminateJobObject(HANDLE job,
UINT exit_code);
הקריאה מפסיקה את פעולתם של כל התהליכים שמשוייכים לעבודה שהמזהה שלה
נתון. המזהה הוא המזהה שהתקבל מהקריאה CreateJobObject
או מהקריאה OpenJobObject שפותחת עבודה על פי שמה. הארגומנט
השני הוא הערך שיוחזר מכל התהליכים שבעבודה.
התרגיל.
עליך לכתוב תוכנית בשם ex-job שניתן להפעיל בשתי דרכים.
כאשר מפעילים אותה עם שני ארגומנטים או יותר, היא יוצרת עבודה חדשה ויוצרת
תהליך חדש ששורת הפקודה שלו היא הארגומנטים השני ואילך של ex-job.
לפני שהיא יוצרת את התהליך החדש, היא יוצרת אירוע מסוג איפוס ידני ששמו
הוא הארגומנט הראשון של ex-job ושמאותחל למצב לא מסומן.
לאחר שהתהליך החדש התחיל לרוץ, התוכנית מחכה לאירוע, וכאשר האירוע מסומן,
היא מפסיקה את פעולת כל התהליכים שמשוייכים לעבודה.
כאשר מפעילים את התוכנית עם ארגומנט אחד בלבד, היא פותחת את האירוע ששמו
הוא הארגומנט הראשון שלה באמצעות קריאת המערכת OpenEvent
ומסמנת אותו, מה שאמור לגרום לעבודה שהתחלנו אולי קודם לכן להפסיק לרוץ.
הנה הפעלה לדוגמה של התכנית:
¯ the name of the event
c:\> ex-job job-event netstat.exe -e 5
the command line to run in the new job
כדי להפסיק את פעולת העבודה, מפעילים מתוך חלון אחר את הפקודה
c:\> ex-job job-event
כדי לבדוק את התוכנית, השתמשו בתוכנית CloneProcess שמסופקת
יחד עם התרגיל. צריך להריץ את התוכנית הזו עם ארגומנט אחד, משך זמן בשניות.
התוכנית יוצרת שני תהליכים בהפרש של משך הזמן הנתון ואז מסיימת את פעולתה.
לאורך זמן, האפקט הוא של יצירת מספר אקספוננציאלי של תהליכים, כאשר התהליכים
שיצרו אותם כבר אינם קיימים. צפו ביצירת התהליכים הללו בעזרת מנהל המשימות
ונסו לסיים את פעולתם בעזרת מנהל המשימות )לא קל אם משך הזמן בין יצירת
תהליכים קצר(. כעת נסו לסיים את פעולתם בעזרת התוכנית שכתבתם; זה אמור
להיות קל יותר. על מנת להמנע מיצירת תהליכים מרובים מדי, כדאי להריץ
את CloseProcess לפחות בתחילה עם משך זמן של כ--10
שניות או יותר.
Copyright Sivan Toledo 2004