Download presentation
Presentation is loading. Please wait.
1
מערכות הפעלה תרגול 5 – אלגוריתם זימון התהליכים ב-Linux
2
מערכות הפעלה - תרגול 52 (c) ארז חדד 2003 תוכן התרגול אלגוריתם זימון התהליכים ב-Linux איך בוחרים, בכל נקודת זמן, את התהליך הבא להרצה במעבד?
3
מערכות הפעלה - תרגול 53 (c) ארז חדד 2003 הערה מקדימה אלגוריתם הזימון המוצג בתרגול - האלגוריתם של Linux 2.6 אינו חלק מגרסת הגרעין 2.4.X מופיע בספר Understanding the Linux Kernel גירסה 3 אלגוריתם יעיל יותר ועל כן משולב בגרעין המתוקן של RedHat 8.0 לקבלת מידע באינטרנט: חפשו מידע על על קבצי קוד הגרעין העוסקים בזימון בגרסאות הגרעין 2.6.X הקבצים העיקריים: include/linux/sched.h ו-kernel/sched.c ניתן לחפש גם לפי שם מחבר האלגוריתם החדש: Ingo Molnar ישנם אפילו שיפורים נוספים בגירסת האלגוריתם בגרעין 2.6.X
4
מערכות הפעלה - תרגול 54 (c) ארז חדד 2003 זימון תהליכים ב-Linux Linux עובדת לפי עקרון של time sharing – חלוקת זמן מעבד בין תהליכים. אלגוריתם הזימון קובע את סדר זימון התהליכים ואת זמן המעבד שכל תהליך מקבל. העברת המעבד מתהליך אחד לאחר באמצעות החלפת הקשר. התהליכים ב Linux מתחלקים לשני סוגים : תהליכי זמן-אמת – תהליכים הנדרשים להגיב על אירועי מערכת בזמן קצר ואחיד ככל האפשר. הקביעה אם תהליך הוא תהליך זמן-אמת נעשית ע"י המשתמש. תהליכים רגילים. תהליכי זמן-אמת מועדפים לריצה על-פני תהליכים רגילים. תהליכים רגילים אינם זוכים לרוץ אם יש תהליכי זמן-אמת מוכנים לריצה (מצב TASK_RUNNING )
5
מערכות הפעלה - תרגול 55 (c) ארז חדד 2003 מדיניות זימון של תהליכי זמן-אמת עבור תהליכי זמן-אמת קיימים שני סוגים של מדיניות זימון: SCHED_FIFO: שיתוף פעולה (non-preemptive) עם עדיפויות – המעבד מתפנה רק כאשר התהליך מחליט לוותר עליו או כאשר תהליך עדיף יותר מוכן לריצה. המעבד מוקצה לאחד מהתהליכים העדיפים ביותר המוכנים לריצה SCHED_RR: חלוקת זמן שווה (Round Robin) – זמן המעבד מחולק בצורה שווה בין כל התהליכים העדיפים ביותר המוכנים לריצה
6
מערכות הפעלה - תרגול 56 (c) ארז חדד 2003 זימון תהליכים רגילים התהליכים הרגילים מתחלקים לשני סוגים לפי אופי הפעילות שלהם: תהליך חישובי מעוניין להגדיל את זמן המעבד שלו ככל הניתן לא מוותר על המעבד מרצונו אלא מופקע. תהליך אינטראקטיבי – קלט/פלט מול המשתמש מעוניין להקטין את זמן התגובה שלו מוותר על המעבד מרצונו אחרי פרק זמן קצר שני סוגי התהליכים הרגילים מזומנים לפי אותה מדיניות זימון – SCHED_OTHER (נרחיב בהמשך ), אך פרמטרי הזימון שונים.
7
מערכות הפעלה - תרגול 57 (c) ארז חדד 2003 עדיפות תהליך עדיפות נקבעת עבור כל תהליך במערכת בצורה הבאה: עדיפות בסיסית עבור תהליך רגיל היא 120 עבור כל תהליך מוגדר הערך nice, כך ש: 19+ nice 20-. העדיפות הסטטית של תהליך נקבעת לפי: 120+nice ככל שהערך המספרי של העדיפות גבוה יותר, כך העדיפות נמוכה יותר. העדיפות קובעת: עבור תהליכים חישוביים - את משך זמן המעבד שיוקצה עבור תהליכים אינטראקטיביים – את זמן התגובה
8
מערכות הפעלה - תרגול 58 (c) ארז חדד 2003 עיקרון זימון תהליכים לכל תהליך מוקצב פרק זמן לשימוש במעבד – time slice אורך פרוסת הזמן (time slice) ליניארי בעדיפות. RR בין כל התהליכים המוכנים לריצה (בסדר יורד של עדיפויות) פרק זמן הנדרש כדי לבצע סבב RR שלם נקרא Epoch. time slice מוקצה לתהליך במאקרו TASK_TIMESLICE : #define MIN_TIMESLICE (10 * HZ / 1000)/* 10 msec */ #define MAX_TIMESLICE(300 * HZ / 1000) /* 300 msec */ #define TASK_TIMESLICE(p) \ MIN_TIMESLICE + (MAX_TIMESLICE – MIN_TIMESLICE) * (MAX_PRIO – 1 – (p)->static_prio)/39 תהליך בעדיפות בסיסית (120) מקבל זמן טיפוסי 150 msec עדיפות סטטית גבוהה - time slice קטן יותר
9
מערכות הפעלה - תרגול 59 (c) ארז חדד 2003 עיקרון זימון תהליכים (2) ביצירת תהליך חדש, תהליך האב מאבד מחצית מה-time slice שלו לטובת תהליך הבן המטרה: למנוע מצב בו תהליך יכול להרוויח זמן מעבד בתוך אותה תקופה, למשל באופן הבא: לפני סיום ה-time slice, התהליך מייצר תהליך בן שממשיך להריץ את הקוד למשך time slice נוסף באותה תקופה וכ"ו מימוש המנגנון בפונקציה do_fork() שמבצעת את fork() קובץ גרעין kernel/fork.c p->time_slice = (current->time_slice + 1) >> 1; current->time_slice >>= 1;
10
מערכות הפעלה - תרגול 510 (c) ארז חדד 2003 תהליכים אינטראקטיביים תהליך אינטראקטיבי שנבחר לריצה רץ עד שמוותר על המעבד (או כמובן מופקע ע"י תהליך בעל עדיפות טובה יותר) זכרו שתהליך אינטראקטיבי צורך מעט זמן מעבד בכל פעם. ככל שעדיפותו של התהליך טובה יותר הוא יגיע לרוץ על המעבד מהר יותר => זמן התגובה יקטן אם ה time slice של התהליך נגמר הוא מקבל מייד time slice נוסף, במסגרת ה-Epoch הנוכחי.
11
מערכות הפעלה - תרגול 511 (c) ארז חדד 2003 אלגוריתם הזימון של תהליכים רגילים זימון של כל התהליכים (גם חישוביים וגם אינטראקטיביים) נעשה ע"י זמן תהליכים ((scheduler אחד. תמיד נבחר לריצה תהליך עם העדיפות הגבוהה ביותר, שמוכן לריצה, ושעוד נותר זמן מה-time slice שלו. מבנה הנתונים runqueue, המשמש את scheduler לזימון תהליכים, גם הוא משותף לכל התהליכים הרגילים.
12
מערכות הפעלה - תרגול 512 (c) ארז חדד 2003 זַמָּן התהליכים scheduler מהו? זַמָּן התהליכים הוא רכיב תוכנה בגרעין Linux שאחראי על זימון התהליך הבא למעבד(מימוש – פונקציה schedule()) מתי מופעל? 1. בתגובה על פסיקת שעון – לאחר שתהליך כילה את ה-time slice שלו (בעקבות השגרה scheduler_tick()). 2. בטיפול בקריאת מערכת הגורמת לתהליך הקורא לעבור להמתנה, כך שהמעבד מתפנה להריץ תהליך אחר. 3. בחזרה של תהליך מהמתנה – יש לשבץ את התהליך ב-runqueue ולבדוק אם כתוצאה מכך יש לבצע החלפת הקשר. 4. כאשר תהליך מחליט לוותר עם המעבד מרצונו בקריאת מערכת כגון sched_yield().
13
מערכות הפעלה - תרגול 513 (c) ארז חדד 2003 מבט מקרוב על ה-runqueue nr_running curr idle active expired expired_ts... runqueue prio_array_t nr_active bitmapqueue.. 0 58.. 1 112.. 1 120.. מתאר תהליך swapper
14
מערכות הפעלה - תרגול 514 (c) ארז חדד 2003 מבט מקרוב על ה-runqueue (2) runqueue הוא מבנה נתונים המשמש את זמן התהליכים. כל runqueue (על כל מעבד) מכיל את הנתונים הבאים: nr_running – מספר התהליכים ב-runqueue (לא כולל את ה-swapper) curr – מצביע למתאר התהליך שרץ כרגע idle – מצביע למתאר תהליך ה-swapper active – מצביע למערך תורי העדיפויות של התהליכים הפעילים, כלומר תהליכים מוכנים לריצה (TASK_RUNNING) שנותר להם זמן ריצה במסגרת התקופה הנוכחית expired – מצביע למערך תורי העדיפויות של התהליכים המוכנים לריצה שכילו את ה-time slice שלהם בתקופה הנוכחית expired_timestamp – מתי עבר התהליך הראשון מ-active ל-expired בתקופה הנוכחית
15
מערכות הפעלה - תרגול 515 (c) ארז חדד 2003 מבט מקרוב על ה-runqueue (3) מערך תורי עדיפויות (struct prio_array) מכיל את הנתונים הבאים: nr_active – מספר התהליכים המצויים במערך זה bitmap[] – וקטור ביטים בגודל מספר דרגות העדיפות (140 ביטים) ביט i דלוק (1) אם יש תהליכים בתור המתאים לעדיפות i התהליך הבא לזימון הוא התהליך הראשון בתור העדיפות הנמוך ביותר ב-active שהביט שלו דלוק – חישוב ב-O(1) queue[] – מערך התורים עצמו, עם כניסה לכל דרגת עדיפות (140 כניסות). כל כניסה מכילה ראש תור מסוג list_t כמתואר בתרגול הקודם.
16
מערכות הפעלה - תרגול 516 (c) ארז חדד 2003 באיזה מצב התהליכים מגיעים ל הפונקציה schedule()? במצב TASK_(UN)INTERRUPTIBLE מתוך הפונקציה sleep_on (מקרים 2 בשקף 12) שם תהליך נוכחי משנה למצב TASK_(UN)INTERRUPTIBLE מכניס את עצמו בתור המתנה קורא ל schedule אחרי החזרה מ schedule מוציא את עצמו מתור המתנה במצב TASK_RUNNING בעקבות פסיקה שהדליקה דגל need_resched (מקרים,41,3 בשקף 12 ) תהליך אחר השתחרר מהמתנה פסיקת שעון שבה נגמר לתהליך הנוכחי ה time_slice sched_yield()
17
מערכות הפעלה - תרגול 517 (c) ארז חדד 2003 הפונקציה schedule() (1) prev = current; rq = this_rq();.. spin_lock_irq(&rq->lock); switch(prev->state) { case TASK_INTERRUPTIBLE: if (signal_pending(prev)) { prev->state = TASK_RUNNING; break; } default: deactivate_task(prev, rq); case TASK_RUNNING: ; } prev מצביע למתאר התהליך הנוכחי (שמוותר על המעבד) rq מכיל את ה-runqueue של המעבד הנוכחי חסימת הפסיקות רק תהליך שעבר למצב המתנה מוצא מתוך ה-runqueue למעט מצב של הפרעה בהמתנה
18
מערכות הפעלה - תרגול 518 (c) ארז חדד 2003 הפונקציה schedule() (2) if (!rq->nr_running) { next = rq->idle; rq->expired_timestamp = 0; goto switch_tasks; } array = rq->active; if (!array->nr_active) { rq->active = rq->expired; rq->expired = array; array = rq->active; rq->expired_timestamp = 0; } אם אין יותר תהליכים לזימון מתוך ה-runqueue, יזומן תהליך ה-swapper אם לא נותרו תהליכים ב-active array, מחליפים בין ה-active וה-expired (סיום תקופה והתחלת תקופה חדשה)
19
מערכות הפעלה - תרגול 519 (c) ארז חדד 2003 הפונקציה schedule() (3) idx = sched_find_first_bit(array->bitmap); queue = array->queue + idx; next = list_entry(queue->next, task_t, run_list); switch_tasks: clear_tsk_need_resched(prev); if (prev != next) {.. rq->curr = next; context_switch(prev, next);.. spin_unlock_irq(&rq->lock); } else spin_unlock_irq(&rq->lock); התהליך הבא לזימון למעבד (next) הוא התהליך הראשון בתור ב-active array בעל העדיפות הגבוהה ביותר שאלה: מי מבצע את אפשור הפסיקות מחדש לאחר הקריאה ל-context_switch()? תשובה: התהליך next ביצוע החלפת ההקשר ע"י קריאה ל-context_switch()
20
מערכות הפעלה - תרגול 520 (c) ארז חדד 2003 שימו לב! ברגע שנגמר הtime slice של תהליך חישובי, הוא עובר לתור ה expired לעומתו, תהליך אינטראקטיבי שמסיים time slice מיד מקבל time slice נוסף ונשאר בתור הactive נראה את זה בתרגול הבא
21
מערכות הפעלה - תרגול 521 (c) ארז חדד 2003 אפיון התנהגות תהליך (1) Linux מודדת את "זמן ההמתנה הממוצע" של תהליך "זמן המתנה ממוצע" = סך כל זמן ההמתנה בטווח הבינוני פחות סך כל זמן הריצה בכל פעם שתהליך מוותר על המעבד, ערך השעון נשמר (פונקצית schedule()): p->sleep_timestamp = jiffies; כאשר תהליך חוזר מהמתנה מוסף זמן ההמתנה לחשבון (פונקציה activate_task()) עד לגודל מקסימלי MAX_SLEEP_AVG: #define MAX_SLEEP_AVG (2*HZ) sleep_time = jiffies – p->sleep_timestamp; p->sleep_avg += sleep_time; if (p->sleep_avg > MAX_SLEEP_AVG) p->sleep_avg = MAX_SLEEP_AVG;
22
מערכות הפעלה - תרגול 522 (c) ארז חדד 2003 אפיון התנהגות תהליך (2) כל פעימת שעון בה התהליך רץ מורידה מהממוצע (הפונקציה sheduler_tick()) עד למינימום 0 if (p->sleep_avg) p->sleep_avg--; על-פי שיטת חישוב זו תהליך עתיר חישוב (חישובי) צפוי להגיע ל"זמן המתנה ממוצע" נמוך. תהליך עתיר ק/פ (אינטראקטיבי) צפוי להגיע ל"זמן המתנה ממוצע" גבוה. שימו לב: זמן המתנה ב runqueue לא נלקח בחשבון בשום מקום
23
מערכות הפעלה - תרגול 523 (c) ארז חדד 2003 הפונקציה scheduler_tick() (1) פונקציה זו מופעלת בכל פסיקת שעון ומעדכנת נתוני זימון של תהליכים. מופעלת כשהפסיקות חסומות כדי למנוע שיבוש נתונים ע"י הפעלת הפונקציה במקביל. מזהה צורך בהחלפת הקשר בעקבות סיום time slice של התהליך הנוכחי ומעדכנת את הדגל need_resched. משתמשת בפונקציות enqueue_task() ו-dequeue_task() להוצאה והכנסה של תהליך לאחד ממערכי התורים (active או expired) task_t *p = current; runqueue_t *rq = this_rq();.. if (p->sleep_avg) p->sleep_avg--; עדכון ה-sleep_avg של התהליך הנוכחי
24
מערכות הפעלה - תרגול 524 (c) ארז חדד 2003 הפונקציה scheduler_tick() (2) if (!--p->time_slice) { dequeue_task(p, rq->active); set_tsk_need_resched(p); p->prio = effective_prio(p); p->time_slice = TASK_TIMESLICE(p); if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) { if (!rq->expired_timestamp) rq->expired_timestamp = jiffies; enqueue_task(p, rq->expired); } else enqueue_task(p, rq->active); אם התהליך הנוכחי כילה את ה-time slice של עצמו, הוא מוצא מה-active, העדיפות הדינמית וה-time slice הבא שלו מחושבים מחדש, ויבוצע לו schedule() בהמשך כאשר התהליך הראשון בתקופה הנוכחית עובר ל-expired, מעודכן expired_timestamp תהליך אינטראקטיבי מוחזר ל-active לרוץ time slice נוסף בתקופה הנוכחית כאשר אין הרעבה
25
מערכות הפעלה - תרגול 525 (c) ארז חדד 2003 חישוב דינמי של עדיפות תהליך (1) Linux מעדכנת את העדיפות של כל תהליך "רגיל" באופן דינמי בהתאם לזמן ההמתנה הממוצע של התהליך. עדיפויות תהליכי זמן-אמת מקובעות לערך הסטטי החישוב מתבצע בפונקציה effective_prio(). העדיפות הדינמית מחושבת לפי האלגוריתם הבא: prio = static_prio – bonus if (prio < MAX_RT_PRIO) prio = MAX_RT_PRIO; if (prio > MAX_PRIO - 1) prio = MAX_PRIO – 1;
26
מערכות הפעלה - תרגול 526 (c) ארז חדד 2003 חישוב דינמי של עדיפות תהליך (2) הגבלת גודל ה-bonus ל-5 נועדה למנוע מצב שבו יתבצע היפוך עדיפויות בין תהליכים שעדיפויותיהם הבסיסיות (הסטטיות) רחוקות זו מזו. למשל, תהליך בעל nice 19 (עדיפות 139) יהפוך לעדיף יותר מתהליך בעל nice 0 (עדיפות 120) שיטת חישוב זו משפרת את העדיפות של תהליך ש"ממתין הרבה" (צפוי לתהליכים אינטראקטיביים) ומרעה את העדיפות של תהליך ש"ממתין מעט" (צפוי לתהליכים חישוביים) הערך של מחצית MAX_SLEEP_AVG, כלומר HZ פעימות או שניה אחת, נקבע בתור הסף בין "המתנה מרובה" ל"המתנה מועטת"
27
מערכות הפעלה - תרגול 527 (c) ארז חדד 2003 תהליכים אינטראקטיביים(1) העדיפות ההתחלתית,שנקבעת ע"י המשתמש, נקבעת לפי הערכתו האם תהליך הוא חישובי או אינטראקטיבי. nice = -20 => תהליך אינטראקטיבי (כמעט בוודאות). nice = 19 => תהליך חישובי. nice = 0 => ההחלטה דינמית של ה scheduler.
28
מערכות הפעלה - תרגול 528 (c) ארז חדד 2003 תהליכים אינטראקטיביים (2) סיווג תהליך כאינטראקטיבי מבוצע במאקרו TASK_INETRACTIVE : #define TASK_INTERACTIVE(p) \ ((p)->prio static_prio – DELTA(p)) כאשר DELTA(p) ניתנת להגדרה כדלקמן: תהליך מסווג כאינטראקטיבי אם העדיפות (הדינמית) שלו "חורגת" מהעדיפות הסטטית שלו. תוצאה של זמן המתנה ממוצע גבוה המקנה בונוס בחישוב העדיפות הדינמית של התהליך
29
מערכות הפעלה - תרגול 529 (c) ארז חדד 2003 תהליכים אינטראקטיביים (3) ככל שמשתפרת העדיפות הסטטית של התהליך, "קל יותר" לתהליך להיות מסווג כאינטראקטיבי. "קל יותר": התהליך צריך לצבור זמן המתנה ממוצע נמוך יותר על-מנת להיחשב כאינטראקטיבי לדוגמה: תהליך עם nice -20 יכול להיות "חזיר" בצריכת המעבד ולהגיע לעדיפות דינמית של בונוס שלילי -3 ועדיין להיות מסווג כאינטראקטיבי. לעומת זאת, תהליך עםnice 19 לא יכול כלל להיות מסווג כאינטראקטיבי כי נדרש בונוס של +7 והבונוס המקסימלי הוא +5
30
מערכות הפעלה - תרגול 530 (c) ארז חדד 2003 נקודות למחשבה מה קורה כשתהליך חישובי יוצא להמתנה ואז חוזר? הוא יכול להיחשב אינטראקטיבי לפרק זמן מסוים ולקבל "פיצוי" על ההמתנה. מה קורה כשתהליך אינטראקטיבי מנצל time slice מלא? נוצרת בעיה : עלול "לתקוע" את התהליכים האחרים בתור ה expired בנוסף עלול "לתקוע" את האינטראקטיביים האחרים בעדיפות גבוהה יותר ב active
31
מערכות הפעלה - תרגול 531 (c) ארז חדד 2003 מניעת הרעבה כדי למנוע הרעבה של תהליכים חישוביים ע“י תהליכים אינטראקטיביים, מוגדר "סף הרעבה" על זמן ההמתנה של התהליכים ב-expired: #define EXPIRED_STARVING(rq) \ ((rq)->expired_timestamp && \ (jiffies – (rq)->expired_timestamp >= \ STARVATION_LIMIT * ((rq)->nr_running + 1)) כאשר התהליכים ב-expired מורעבים מעבר לסף, מופסקת הענקת time slices נוספים לתהליכים אינטראקטיביים בתקופה הנוכחית. הסף המוגדר פרופורציוני למספר התהליכים ב- runqueue, כך שככל שיש יותר תהליכים מוכנים לריצה, הסף גבוה יותר.
32
מערכות הפעלה - תרגול 532 (c) ארז חדד 2003 מניעת הרעבה - חסרון המנגנון הוא גס: מאפשר לאינטראקטיביים להרעיב את החישוביים, במיוחד כשיש עומס על המערכת. לאחר שעוברים את הסף => מאפשר לחישוביים לעכב את האינטראקטיביים לזמן רב (כי אינט' לא מקבלים time slices נוספים)
33
מערכות הפעלה - תרגול 533 (c) ארז חדד 2003 איפה משתנה מה - סיכום sleep_avg מוקטן בכל פעימת שעון של התהליך הנוכחי ב scheduler_tick() מוגדל חזרה מהמתנה time_slice מוקטן כל פעימת שעון של התהליך הנוכחי ב scheduler_tick() כשנגמר – נותנים חדש מלא (גודלו תלוי ב static_prio) Fork – ½ מהאב כשבן מת – היתרה חוזרת לאב prio (הכוונה לעדיפות דינמית) כשנגמר time_slice נוכחי ב scheduler_tick() בחזרה מהמתנה בפונקציה wakeup_process ביחד עם שינוי sleep_avg מחשבים גם מחדש prio ומשבצים בתור המתאים חישוב prioמתבצע ע"י פונקציה effective_prio() שימו לב: time_slice לא משתנה כאן
34
מערכות הפעלה - תרגול 534 (c) ארז חדד 2003 מי משפיע על מי - סיכום time_sliceהוא פונקציה של: static_prio static_prio = 120+nice prio הוא פונקציה של: sleep_avg static_prio המיקום ב runqueue נקבע ע"פ: prio
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.