390 likes | 587 Views
מערכות הפעלה. תרגול 3 – תהליכים ב- Linux (1). תוכן התרגול. מבוא לתהליכים ב- Linux API לעבודה עם תהליכים מבוא לניהול תהליכים בתוך הגרעין מתאר התהליך ( process descriptor ) רשימת התהליכים ( process list ) מאגר התהליכים המוכנים לריצה ("הטווח הקצר") תורי המתנה ("הטווח הבינוני / ארוך").
E N D
מערכות הפעלה תרגול 3 – תהליכים ב-Linux (1)
תוכן התרגול • מבוא לתהליכים ב-Linux • API לעבודה עם תהליכים • מבוא לניהול תהליכים בתוך הגרעין • מתאר התהליך (process descriptor) • רשימת התהליכים (process list) • מאגר התהליכים המוכנים לריצה ("הטווח הקצר") • תורי המתנה ("הטווח הבינוני / ארוך") מערכות הפעלה - תרגול 3
מבוא לתהליכים ב-Linux (1) • תהליך (process) הוא ביצוע סדרתי של משימה, המוגדרת על-ידי תכנית (program) • תהליך = מופע (instance) של ביצוע תכנית • תהליך נקרא גם task במקומות שונים • מספר תהליכים מתבצעים "בו זמנית" על המעבד במחשב • למעשה – המעבד "ממתג" בין התהליכים בתדירות גבוהה באמצעות מנגנון החלפת הקשר. פרטים בתרגול הבא • עבור מערכת ההפעלה, תהליך הינו יישות עצמאית הצורכת משאבים • זיכרון, זמן מעבד, שטח דיסק וכו' • לכל תהליך ב-Linux יש מזהה הקרוי PID – Process IDentifier • מספר שלם בן 32 ביט ייחודי לתהליך (עד 32K תהליכים מטעמי תאימות הסטורית) • ערכי ה-pid ממוחזרים מתהליכים שסיימו לתהליכים חדשים שנוצרים מערכות הפעלה - תרגול 3
מבוא לתהליכים ב-Linux (2) • באיתחול Linux גרעין מערכת ההפעלה יוצר שני תהליכים בלבד • swapper (pid=0) – משמש לניהול זיכרון • init (pid=1) – ממנו נוצרים כל שאר התהליכים במערכת • כל תהליך נוסף נוצר ב-Linux כעותק של תהליך קיים • התהליך המקורי נקרא תהליך אב (או הורה) • התהליך החדש נקרא תהליך בן • תהליך הבן נוצר בעקבות ביצוע קריאת מערכת כדוגמת fork() על-ידי תהליך האב • תהליך אב יכול ליצור יותר מתהליך בן אחד מערכות הפעלה - תרגול 3
מבוא לתהליכים ב-Linux (3) • תהליך יכול לאחר היווצרו לבצע משימה שונה מאביו • על-ידי הסתעפות בקוד התכנית הקיימת, לאחר ההתפצלות מהאב • על-ידי טעינת משימה חדשה (תכנית חדשה) לביצוע • למשל, על-ידי קריאת המערכת execv() • תהליך אב יכול לבדוק סיום של כל תהליך בן שלו • אך לא של "נכדים", "נינים", "אחים" וכדומה • אב יכול להמתין לסיום בן לפני המשך פעולתו • למשל, על-ידי קריאת המערכת wait() מערכות הפעלה - תרגול 3
מבוא לתהליכים ב-Linux (4) • כדי לאפשר לאב לקבל מידע על סיום הבן, לאחר שתהליך מסיים את פעולתו הוא עובר למצב מיוחד – zombie – שבו התהליך קיים כרשומת נתונים בלבד ללא שום ביצוע משימה • הרשומה נמחקת לאחר שהאב קיבל את המידע על סיום הבן • מה קורה לתהליך שהופך ל"יתום" (orphan) לאחר שאביו כבר סיים? • התהליך הופך להיות בן של init • התהליך init ממשיך להתקיים לאורך כל פעולתה של מערכת ההפעלה • אחד מתפקידיו העיקריים - המתנה לכל בניו, כדי לפנות את נתוניהם לאחר סיום מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (1) • קריאת המערכת fork() • תחביר: pid_t fork(); • פעולה: מעתיקה את תהליך האב לתהליך הבן וחוזרת בשני התהליכים • קוד זהה (ומיקום בקוד) • זיכרון זהה (משתנים וערכיהם, חוצצים) • סביבה זהה (קבצים פתוחים, file descriptors, ספרית עבודה נוכחית) • פרמטרים: אין • ערך מוחזר: • במקרה של כישלון: -1 לאב (אין בן) • במקרה של הצלחה: לבן מוחזר 0 ולאב מוחזר ה-pid של הבן מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (2) • לאחר פעולת fork() מוצלחת, אמנם יש לאב ולבן את אותם משתנים בזיכרון, אך בעותקים נפרדים • כלומר, שינוי ערכי המשתנים אצל האב לא ייראה אצל הבן, וההיפך • כמו כן, תהליך הבן הוא תהליך נפרד מתהליך האב לכל דבר. בפרט, יש לו pid משלו • מה מדפיס הקוד הבא? main() { fork(); printf(“hello”); } מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (3) • תשובות אפשריות (בהנחה ש-fork() הצליחה): hellohello hheellollo • הסיבה: שני תהליכים כותבים פלט בצורה לא מתואמת • מבנה תכנית אופייני המשתמש ב-fork(): status = fork(); if (status < 0) // fork() failed – handle error (e.g. message & exit) if (status == 0) // son process – do son code else // father process – do father code מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (4) • קריאת המערכת execv() • תחביר: int execv(const char *filename, char *const argv[]); • פעולה: טוענת תכנית חדשה לביצוע על-ידי התהליך הקורא • פרמטרים: • filename – מסלול אל הקובץ המכיל את התכנית לטעינה • argv – מערך מצביעים למחרוזות המכיל את הפרמטרים עבור התכנית. האיבר הראשון מקיים argv[0] == filename או רק מכיל את שם קובץ התכנית. האיבר שאחרי הפרמטר האחרון מכיל NULL • ערך מוחזר: • במקרה של כישלון: -1 • במקרה של הצלחה: הקריאה אינה חוזרת. איזורי הזיכרון של התהליך מאותחלים לתכנית החדשה שמתחילה להתבצע מההתחלה. מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (5) • מה ידפיס הקוד הבא? main() { char *argv[] = {“date”, NULL}; execv(“/bin/date”, argv); printf(“hello”); } • התשובה: • אם execv() מצליחה: את התאריך והשעה • אם execv() נכשלת: hello מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (6) • דוגמה אופיינית לשילוב fork() עם execv(): pid = fork(); if (pid < 0) { handle fork() error } if (pid > 0) // do father code else execv(“son_prog”, argv_son); מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (7) • קריאת המערכת exit() • תחביר: void exit(int status); • פעולה: מסיימת את ביצוע התהליך הקורא ומשחררת את כל המשאבים שברשותו. התהליך עובר למצב zombie עד שתהליך האב יבקש לבדוק את סיומו ואז יפונה לחלוטין • פרמטרים: • status – ערך סיום המוחזר לאב אם יבדוק את סיום התהליך • ערך מוחזר: הקריאה אינה חוזרת מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (8) • קריאת המערכת wait() • תחביר: pid_t wait(int *status); • פעולה: גורמת לתהליך הקורא להמתין עד אשר אחד מתהליכי הבן שלו יסיים • פרמטרים: • status – מצביע למשתנה בו יאוחסן סטטוס הבן שסיים • ערך מוחזר: • אם אין בנים, או שכל הבנים כבר סיימו וכבר בוצע להם wait() - חזרה מיד עם ערך -1 • אם יש בן שסיים ועדיין לא בוצע לו wait() (zombie) – חזרה מייד עם ה-pid של הבן הנ"ל ועם סטטוס הסיום שלו. מאקרו-ים שונים מאפשרים לקבל מתוך הסטטוס מידע על הבן. למשל WEXITSTATUS (status) יתן את ערך הסיום של בן שסיים (הערך שהעביר כארגומנט ל-exit()). • אחרת – המתנה עד שבן כלשהו יסיים מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (9) • דוגמת קוד אופיינית בה האב מחכה לסיום כל תהליכי הבן: while (wait(&status) != -1); • קריאה שימושית נוספת: waitpid() – המתנה לסיום בן מסוים pid_t waitpid(pid_t pid, int *status, int options); • ניתן למשל, באמצעות options, לבחור רק לבדוק אם הבן סיים (ערך WNOHANG) או להמתין לסיום הבן (ערך 0) מערכות הפעלה - תרגול 3
API לתהליכים ב-Linux (10) • קריאת המערכת getpid() • תחביר: pid_t getpid() • פעולה: מחזירה לתהליך הקורא את ה-pid של עצמו • פרמטרים: אין • ערך מוחזר: ה-pid של התהליך הקורא • קריאה שימושית דומה: getppid() מחזירה את ה-pid של תהליך האב של התהליך הקורא • מה המשמעות של getppid() == 1 עבור תהליך משתמש טיפוסי? • תשובה: תהליך האב שיצר את התהליך הנוכחי סיים מערכות הפעלה - תרגול 3
אתחול תהליכים ב-Linux • משתמשים מתחברים לעבודה ב-Linux דרך מסופים • מסוף = מסך + מקלדת (מקומי או מרוחק) • התהליך init יוצר עבור כל מסוף של Linux תהליך בן הטוען ומבצע את המשימות הבאות לפי הסדר: • איתחול של המסוף בתכנית getty • תכנית login המאפשרת למשתמש להיכנס למערכת • לאחר שאושרה כניסת המשתמש: תכנית shell(כמו tcsh או bash) המאפשרת למשתמש להעביר פקודות למערכת ההפעלה • כאשר ה-shell מקבל פקודה, הוא מייצר תהליך בן שמבצע אותה, ממתין לסיום הבן ואז קורא את הפקודה הבאה מערכות הפעלה - תרגול 3
אתחול תהליכים ב-Linux (2) init Pid=1 fork() fork() fork() wait() Pid=7223 Pid=30498 Pid=8837 exit() exec(getty) exec(getty) exec(getty) exec(login) exec(login) exec(shell) shell fork() wait() Pid=5562 exec(command) exit() מערכות הפעלה - תרגול 3
ניהול תהליכים בגרעין (1) • לכל תהליך ב-Linux קיים בגרעין מתאר תהליך (process descriptor), שהוא רשומה מסוג task_struct (קובץ גרעין include/linux/sched.h) המכילה: • מצב התהליך • עדיפות התהליך • מזהה התהליך (pid) • מצביע לטבלת איזורי הזיכרון של התהליך • מצביע לטבלת הקבצים הפתוחים של התהליך • מצביעים למתארי תהליכים נוספים (רשימה מקושרת) • מצביעים למתאר תהליך האב ו"קרובי משפחה" נוספים • מסוף איתו התהליך מתקשר • ועוד.. מערכות הפעלה - תרגול 3
ניהול תהליכים בגרעין (2) • מצב התהליך נמצא בשדה state,שהוא משתנה בגודל 32 ביט המתפקד כמערך ביטים • בכל זמן שהוא, בדיוק אחד מהביטים ב-state דלוק בהתאם למצב התהליך באותו זמן • Linux מגדירה את המצבים הבאים לכל תהליך: • TASK_RUNNING – התהליך רץ או מוכן לריצה, כלומר נמצא בטווח הקצר • TASK_INTERRUPTIBLE – התהליך ממתין לאירוע כלשהו (טווח בינוני/ארוך) אך ניתן להפסיק את המתנת התהליך ולהחזירו למצב TASK_RUNNING באמצעות שליחת אות כלשהו לתהליך. זהו מצב ההמתנה הנפוץ. מערכות הפעלה - תרגול 3
ניהול תהליכים בגרעין (3) • TASK_UNINTERRUPTIBLE – התהליך ממתין לאירוע כלשהו (בדומה ל- TASK_INTERRUPTIBLE) אך פרט לאירוע לו הוא ממתין, לא ניתן ל"העיר" את התהליך • מצב המתנה נדיר לשימוש – למשל כאשר התהליך מבקש לגשת לחומרה ומערכת ההפעלה צריכה לסרוק אחר החומרה ללא הפרעה • TASK_STOPPED – ריצת התהליך נעצרה בצורה מבוקרת על-ידי תהליך אחר (בדרך-כלל debugger או tracer) • TASK_ZOMBIE – ריצת התהליך הסתיימה, אך תהליך האב של התהליך שסיים עדיין לא ביקש מידע על סיום התהליך באמצעות קריאה כדוגמת wait(). התהליך קיים כמתאר בלבד • את ערך השדה state ניתן לשנות בהצבה ישירה או על-ידי המאקרו set_task_state או set_current_state (קובץ גרעין include/linux/sched.h) מערכות הפעלה - תרגול 3
ניהול תהליכים בגרעין (4) • לכל תהליך יש מחסנית נוספת הקרויה kernel mode stack, כלומר "מחסנית גרעין" • מחסנית זו משמשת את גרעין מערכת ההפעלה בטיפול באירועים במהלך ריצת התהליך • פסיקות בכלל • קריאות מערכת בפרט • מחסנית הגרעין של כל תהליך מאוחסנת באיזור הזיכרון של הגרעין • כאשר במהלך ריצת התהליך מתבצע מעבר בין user mode ו-kernel mode, מתבצעת החלפת מחסניות (שינוי ערכי ss ו-esp) בין המחסנית הרגילה של התהליך ומחסנית הגרעין • ערכי ss:esp המצביעים למחסנית הרגילה נשמרים על-ידי המעבד במחסנית הגרעין מיד עם המעבר ל-kernel mode ומשוחזרים במעבר החוזר ל-user mode מערכות הפעלה - תרגול 3
ניהול תהליכים בגרעין (5) • מחסנית הגרעין מאוחסנת יחד עם מתאר התהליך בקטע זיכרון אחד בגודל 8KB, המתחיל בכתובת שהיא כפולה של 8KB (213) 0x015fbfff Kernel Mode Stack union task_union { struct task_struct task; unsigned long stack[2048]; }; המחסנית לא דורסת את מתאר התהליך מפני שאיננה גדלה מעבר ל-7200 בתים, וגודל מתאר התהליך קטן מ-1000 בתים 0x015fa878 esp 0x015fa3cb Process Descriptor 0x015fa000 current מערכות הפעלה - תרגול 3
ניהול תהליכים בגרעין (6) • מצורת האחסון הנ"ל נובעת דרך פשוטה "לשלוף" את כתובת מתאר התהליך מתוך esp כאשר המעבד ב-kernel mode: לאפס את 13 הביטים הנמוכים של esp • בהתאם לדוגמה בשקף הקודם: esp = 0x15fa878 esp & 0xffffe000 = 0x15fa000 – כתובת מתאר התהליך • המאקרו current (קובץ גרעין include/asm-i386/current.h) משתמש בשיטה זו על מנת לאחסן את כתובת מתאר התהליך בערך מוחזר p: movl $0xffffe000, %ecx movl %esp, p andl %ecx, p מערכות הפעלה - תרגול 3
רשימת התהליכים (1) ניהול תהליכים בגרעין • מתארי כל התהליכים מחוברים ברשימה מקושרת כפולה מעגלית הקרויה רשימת התהליכים (process list) באמצעות השדות prev_task ו-next_task • רשימה זו מקבילה ל"טבלת התהליכים" הקיימת במערכות ההפעלה אחרות • ראש הרשימה הוא המתאר של התהליך swapper prev_task next_task prev_task next_task prev_task next_task init_task מערכות הפעלה - תרגול 3
רשימת התהליכים (2) ניהול תהליכים בגרעין • המאקרו SET_LINKS ו-REMOVE_LINKS (קובץ גרעין include/linux/sched.h) משמשים להוספה והסרה של מתאר תהליך ברשימת התהליכים • מטפלים גם ב"קשרי משפחה" בין תהליכים. פרטים בהמשך • המאקרו for_each_task (אותו קובץ גרעין) מאפשר לעבור על כל התהליכים ברשימה בסריקה דרך השדה next_task: #define for_each_task(p) for (p = &init_task; (p = p->next_task) != &init_task; ) מערכות הפעלה - תרגול 3
מיפוי PID למתאר תהליך (1) ניהול תהליכים בגרעין • אמנם קריאות מערכת המתייחסות לתהליך מציינות את ה-pid של התהליך, אך הגרעין עובד עם מתאר התהליך • לפיכך, הוגדר בגרעין מנגנון המאתר את מתאר התהליך לפי ה-pid של התהליך • המנגנון מבוסס על hash-table בגודל PIDHASH_SZ (בד"כ 1024) כניסות • בדרך-כלל מספר התהליכים במערכת קטן בהרבה מ-32K ולכן אין צורך להחזיק כניסות עבור כל ה-pid האפשריים • התנגשויות בפונקצית ה-hash נפתרות על-ידי קישור מתארי התהליך, המתמפים לאותה כניסה בטבלה, ברשימה מקושרת כפולה דרך השדות pidhash_next ו-pidhash_pprev מערכות הפעלה - תרגול 3
מיפוי PID למתאר התהליך (2) ניהול תהליכים בגרעין • הפונקציות hash_pid() ו-unhash_pid() מאפשרות להוסיף ולהסיר מתאר תהליך לטבלה • הפונקציה find_task_by_pid() מבצעת את איתור מתאר התהליך לפי ה-pid הנתון pidhash … 0 PID 199 PID 26799 199 … PID 26800 216 pidhash_next pidhash_pprev … 1023 מערכות הפעלה - תרגול 3
ניהול קשרי משפחה בגרעין (1) ניהול תהליכים בגרעין • "קשרי המשפחה" בין תהליכים מיוצגים בגרעין באמצעות מצביעים בין מתארי תהליכים • מתאר תהליך אב מצביע למתאר תהליך הבן הצעיר ביותר שלו (שנוצר אחרון) באמצעות שדה p_cptr במתאר התהליך • מתאר תהליך מצביע למתאר תהליך האב שלו באמצעות השדה p_opptr • במתאר התהליך קיים שדה מצביע נוסף הקרוי p_pptr המצביע למתאר תהליך האב בפועל. ערך זה שונה מתהליך האב כאשר התהליך נמצא בריצה מבוקרת ע"י debugger או tracer • מתאר תהליך מצביע למתאר תהליך ה"אח הבוגר" (older sibling), כלומר מתאר התהליך שאביו יצר לפניו, באמצעות השדה p_osptr • מתאר תהליך מצביע למתאר תהליך ה"אח הצעיר" (younger sibling), כלומר מתאר התהליך שאביו יצר אחריו, באמצעות השדה p_ysptr מערכות הפעלה - תרגול 3
ניהול קשרי משפחה בגרעין (2) ניהול תהליכים בגרעין • באמצעות "קשרי המשפחה" • תהליך יכול לאתר את אביו • למשל, עבור getppid() • תהליך יכול לאתר את בניו לפי סדר יצירתם • למשל, עבור wait() P0 p_(o)pptr p_ysptr p_osptr p_cptr P1 P2 P3 P4 מערכות הפעלה - תרגול 3
רשימות מקושרות בגרעין (1) ניהול תהליכים בגרעין • לצורך ניהול תורים ומבני נתונים אחרים הגרעין משתמש ברשימות מקושרות כפולות מעגליות • הגדרת מבנה הרשימה בקובץ הגרעין include/linux/list.h • כל איבר ברשימה הוא מסוג list_t struct list_head { struct list_head *next, *prev; }; typedef struct list_head list_t; data structure 1 data structure 2 data structure 3 list_head next next next next prev prev prev prev מערכות הפעלה - תרגול 3
רשימות מקושרות בגרעין (2) ניהול תהליכים בגרעין • האיברים ברשימה הם שדות המוכלים ברשומות מבני נתונים • מבני הנתונים המכילים את אברי הרשימה מקושרים זה לזה באמצעות הרשימה • הפעולות על הרשימה כוללות, בין השאר: • יצירת (ראש) הרשימה: LIST_HEAD • הוספת איבר במקום נתון (list_add) ובסוף הרשימה (list_add_tail) • הסרת איבר נתון (list_del) • בדיקה האם הרשימה ריקה (list_empty) • גישה לרשומה המכילה איבר נתון (list_entry) #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr) – (unsigned long)(&((type *)0)->member))) • לולאת מעבר על איברים ברשימה (list_for_each) מערכות הפעלה - תרגול 3
הטווח הקצר (1) ניהול תהליכים בגרעין • מתארי התהליכים המוכנים לריצה ב-Linux (מצב TASK_RUNNING) נגישים מתוך מבנה נתונים הקרוי runqueue (קובץ גרעין kernel/sched.c) • לכל מעבד יש runqueue משלו • כל runqueue מכיל מספר תורים של מתארי תהליכים, אחד לכל עדיפות של תהליך • כל תור ממומש כרשימה מעגלית כפולה שתוארה קודם • השדה run_list במתאר התהליך הוא איבר הקישור ברשימה (מסוג list_head) מערכות הפעלה - תרגול 3
הטווח הקצר (2) ניהול תהליכים בגרעין • הפונקציות enqueue_task() ו-dequeue_task() מכניסות ומוציאות [מתאר] תהליך ב-runqueue • הפונקציה wake_up_process() הופכת תהליך ממתין למוכן לריצה (TASK_RUNNING), מוסיפה את התהליך ל-runqueue באמצעות enqueue_task() ומסמנת צורך בהחלפת הקשר אם התהליך החדש מועדף לריצה על-פני האחרים מערכות הפעלה - תרגול 3
הטווח הבינוני/ארוך (1) ניהול תהליכים בגרעין • תהליך שצריך להמתין לאירוע כלשהו לפני המשך ריצתו (TASK_(UN)INTERRUPTIBLE) נכנס לתור המתנה (wait queue) • כמו כן, התהליך יוצא מה-runqueue ומוותר על המעבד • כל תור המתנה משויך לאירוע או סוג אירוע כלשהו, כגון • פסיקת חומרה, למשל דיסק או שעון • התפנות משאב מערכת לשימוש. לדוגמה: ערוץ תקשורת שהתפנה וניתן לשלוח דרכו נתונים • אירועים אחרים כלשהם, כמו סיום תהליך • כאשר קורה האירוע אליו מקושר תור ההמתנה, מערכת ההפעלה "מעירה" תהליכים מתוך התור, כלומר מחזירה אותם למצב ריצה (TASK_RUNNING) מערכות הפעלה - תרגול 3
הטווח הבינוני/ארוך (2) ניהול תהליכים בגרעין • תהליך ממתין בתור יכול להיות באחד משני מצבים: • exclusive (בלעדי) – כאשר האירוע המעורר קורה, מעירים אחד מהתהליכים שממתינים עם סימון "בלעדי". למשל: כאשר האירוע הוא שחרור של משאב שניתן לשימוש רק על-ידי תהליך יחיד ב-זמנית • non-execlusive (משותף) – כאשר האירוע המעורר קורה, מעירים את כל התהליכים שממתינים עם סימון "משותף". למשל: כאשר האירוע הוא פסיקת שעון שיכולה לסמן סוף המתנה עבור תהליכים שונים הממתינים למשך זמן קצוב • בדרך-כלל אין באותו תור המתנה ממתינים בלעדיים ומשותפים יחד מערכות הפעלה - תרגול 3
הטווח הבינוני/ארוך (3) ניהול תהליכים בגרעין • תור המתנה ממומש כרשימה מקושרת כפולה מעגלית שתוארה קודם (קובץ גרעין include/linux/wait.h) struct __wait_queue_head { spinlock_t lock; struct list_head task_list; }; typedef __wait_queue_head wait_queue_head_t; • כל תהליך בתור מוצבע מאיבר ברשימה המוגדר כדלהלן: struct __wait_queue { unsigned int flags; struct task_struct *task; struct list_head task_list; }; typedef struct __wait_queue wait_queue_t; השדה lock מיועד להגן על התור מפני גישה במקביל על-ידי שני תהליכים או יותר השדה flags מציין האם ההמתנה היא exclusive (1) או non-exclusive (0) מערכות הפעלה - תרגול 3
הטווח הבינוני/ארוך (4) ניהול תהליכים בגרעין • פונקציה להכנסת תהליך להמתנה בתור - sleep_on() void sleep_on(wait_queue_head_t *q) { unsigned long flags; wait_queue_t wait; wait.flags = 0; wait.task = current; current->state = TASK_UNINTERRUPTIBLE; add_wait_queue(q, &wait); schedule(); remove_wait_queue(q, &wait); } הפונקציות add_wait_queue[_exclusive]() ו-remove_wait_queue() מכניסות ומוציאות תהליך מהתור מערכות הפעלה - תרגול 3
הטווח הבינוני/ארוך (5) ניהול תהליכים בגרעין • פונקציות נוספות מאפשרות הכנסת תהליך לתור כשהוא ממתין במצב interruptible ו/או כשהממתין בלעדי • במקביל, פונקציות המשמשות "להעיר" תהליכים: • wake_up מעירה את כל הממתינים המשותפים ואחד מהבלעדיים • גרסאות נוספות של wake_up: • להעיר מספר מוגבל של תהליכים ממתינים • להעיר רק ממתינים שהם interruptible • לבצע החלפת הקשר אם התהליך המועדף לריצה משתנה לאחר שמעירים תהליכים מערכות הפעלה - תרגול 3