Context switch Flashcards

1
Q

יתרונות וחסרונות של החלפות הקשר?

A

יתרונות:

  1. ניצול טוב יותר של משאבי המערכת.
  2. הקטנת זמן התגובה של תהליכים אינטראקטיביים.

חסרונות:
פגיעה בנצילות המעבד (CPU utilization).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

מהם 2 הסוגים של החלפות הקשר?

A
  1. יזומה - התהליך מוותר מרצונו על המעבד

2. כפויה (הפקעה) - הגרעין מפקיע (כלומר, לוקח בכוח) את המעבד מהתהליך

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

האם גרעין לינוקס ניתן להפקעה?

A

בגרסאות ישנות של לינוקס, הגרעין לא היה מאפשר להפקיע את המעבד מתהליך שנמצא במצב גרעין.
אבל החל מגרסה 2.6, גרעין לינוקס מסוגל להפקיע את המעבד גם מתהליך שנמצא במצב גרעין.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

האם החלפת הקשר מתבצעת במצב משתמש או במצב גרעין?

A

החלפת הקשר דורשת את התערבות מערכת ההפעלה, ולכן היא מתרחשת במצב גרעין.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

היכן נשמרים הרגיסטרים והדגלים בעת החלפת הקשר?

A

השמירה והטעינה מתבצעת במחסנית הגרעין ובשדה thread ב-PCB.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

איך המעבד יודע איפה נמצאת מחסנית הגרעין של התהליך הנוכחי?

A

באמצעות מבנה מיוחד הנקרא TSS.

במעבר בין רמות הרשאה (user mode->kernel mode) מעבדי IA-32 קוראים את השדה TSS.sp0 כדי למצוא את בסיס מחסנית הגרעין.

החלפת הקשר צריכה אם כן לעדכן את השדה TSS.sp0 כך שיצביע למחסנית הגרעין של התהליך הבא לביצוע.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Why do need a separate TSS struct for each CPU?

A

The TSS.sp0 serves as a “register” that points to the base of the current kernel stack. Since all registers are per-CPU, so is the TSS struct.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

איך אנו ניגשים ל-TSS?

A

בעזרת רגיסטר ששמו TR.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

מה תפקידו של השדה thread ב-PCB?

A

השדה thread הוא מבנה מטיפוס thread_struct אשר נמצא בתוך ה-PCB.

השדה משמש לשמירת חלק מהקשר התהליך.
פריט המידע החשוב מבחינתנו הוא המצביע לראש מחסנית הגרעין שנשמר ונטען בזמן החלפת הקשר.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

מה ההבדל בין השדה thread ב-PCB לבין TSS.Sp0?

A

thread מכיל כאחד השדות שלו מצביע לראש מחסנית הגרעין (איפה היא מסתיימת כרגע).
TSS.SP0 לעומת זאת מצביע לבסיס מחסנית הגרעין (איפה היא מתחילה).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

תאר את שלבי החלפת ההקשר.

A
  1. context_switch() – פונקצית C כללית, לא תלוית ארכיטקטורה.
  2. __switch_to_asm() – פונקציה ספציפית לארכיטקטורה, כתובה באסמבלי כי קוראים/כותבים לרגיסטרים.
  3. __switch_to() – פונקצית C, ספציפית לארכיטקטורה כי ניגשים למבנה TSS.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

מה עושה הפונקציה context_switch()? (2 דברים עיקריים)

A

מחליפה בין מרחבי הזיכרון של התהליכים (ע”י קריאה ל-switch_mm()).
קוראת ל-switch_to_asm שמחליפה רגיסטרים (שומרת וטוענת את ההקשר).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

מה הדברים העיקריים שעושה switch_to_asm?

A
  1. ראשית היא שומרת את כל הרגיסטרים לפי קונבנציות קריאה לפונקציה, כי הקומפיילר לא יודע שהולכת להתבצע החלפת הקשר ולכן על הפונקציה לגבות את הרגיסטרים שבאחריותה.
  2. אח”כ מחליפה מחסניות: movq %rsp, prev->thread.rsp
    movq next->thread.rsp, %rsp
    (prev ו next עוברים כמובן ברגיסטרים זה רק פסאודו-קוד)
  3. שחזור הרגיסטרים ממחסנית הגרעין של next.
    אלו הרגיסטרים ש-next שמר כאשר הוא קרא להחלפת הקשר בעבר.
  4. קופצים ל-switch_to, קפיצה ולא call כי כתובת החזרה כבר שמורה על המחסנית.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

בפונקציה switch_to_asm לאן מצביע next->thread.rsp ?

A

תלוי.

  1. מקרה אתחול: התהליך next הוא תהליך חדש שנוצר ע”י fork() ועדיין לא רץ אף פעם.
  2. מקרה סטנדרטי: התהליך next כבר רץ בעבר ועבר החלפת הקשר. במקרה זה המחסנית של next נראית כמו המחסנית של prev, כי prev הוא תהליך שעובר עכשיו החלפת הקשר!

במקרה הסטנדרטי יהיו על המחסנית כל הרגיסטרים ש-switch_to_asm גיבתה (ולכן נוכל לשחזרם) ואח”כ כתובת החזרה של switch_to_asm.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

מה הדברים העיקריים שעושה switch_to()?

A
  1. update_sp0(next_p);
    עדכון מצביע מחסנית הגרעין
    של התהליך הנוכחי ב-TSS
  2. return
    שליפת כתובת החזרה מראש המחסנית
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

מדוע הפונקציה __switch_to() מופעלת באמצעות קפיצה (פקודת jmp) ולא באמצעות קריאה רגילה (פקודת call) ?

A

פקודת call דוחפת למחסנית את כתובת החזרה של השורה הבאה לביצוע, ואנחנו רוצים כתובת חזרה שונה בהתאם למקרה:

  1. במקרה האתחול: הכתובת היא ret_from_fork
  2. במקרה הסטנדרטי: הכתובת היא כתובת החזרה מ-__switch_to_asm() .

בשני המקרים, מכיוון ש-__switch_to() מוגדרת כפונקציה לכל דבר, הקוד שלה מסתיים בפקודת ret ששולפת כתובת חזרה מהמחסנית (של התהליך הבא לביצוע) וקופצת אליה.

17
Q

כיצד ממומש fork()?

A

קריאת המערכת fork() משתמשת בפונקציה פנימית של הגרעין הקרויה do_fork() לבניית ההקשר של התהליך החדש.
גם קריאות מערכת אחרות ליצירת תהליכים (לדוגמה clone()) קוראות ל-do_fork().

18
Q

מהם 7 השלבים שמבצעת do_fork()?

A

הפונקציה do_fork() מבצעת את השלבים הבאים:

  1. מקצה PCB חדש ומחסנית גרעין חדשה עבור תהליך הבן.
  2. קוראת לפונקציה copy_thread(), אשר ממלאת את מחסנית הגרעין של תהליך הבן כך שייראה כאילו הוא קרא לקריאת המערכת fork() ואז לפונקציה __switch_to_asm() .
  3. מעתיקה לתהליך הבן את מרבית הנתונים מ-PCB האב.
    למשל את טבלת הקבצים הפתוחים (file descriptors) ואת שגרות הטיפול בסיגנלים.
    העתקת תכולת הזיכרון מתבצעת בשיטת COW.
  4. מקשרת את תהליך הבן ל”בני משפחתו”.
  5. מוסיפה את תהליך הבן לרשימת התהליכים הגלובאלית וגם לטבלת הערבול PID->PCB.
  6. מעבירה את תהליך הבן למצב TASK_RUNNING ומכניסה אותו ל-runqueue.
  7. לסיום, הפונקציה מחזירה את ה-pid של תהליך הבן, וערך זה מוחזר גם מתהליך האב.
19
Q

מהם שלבי הפונקציה copy_thread()?

A
  1. מוצאת את מבנה pt_regs אצל תהליך הבן:
    struct pt_regs* childregs = task_pt_regs(p);
  2. מוצאת את מבנה pt_regs אצל תהליך האב:
    struct pt_regs* regs = task_pt_regs(current);
  3. מעתיקה את הרגיסטרים של האב לבן:
    * childregs = *regs;
  4. מעדכנת את ערכו של rax ל-0 בתהליך הבן:
    childregs->rax = 0;
  5. מעדכנת את כתובת החזרה השמורה על המחסנית:
    struct inactive_task_frame* frame =
    (struct inactive_task_frame*)(childregs) - 1;
    frame->ret_addr = (unsigned long)ret_from_fork;
19
Q

מהם שלבי הפונקציה copy_thread()?

A
  1. מוצאת את מבנה pt_regs אצל תהליך הבן:
    struct pt_regs* childregs = task_pt_regs(p);
  2. מוצאת את מבנה pt_regs אצל תהליך האב:
    struct pt_regs* regs = task_pt_regs(current);
  3. מעתיקה את הרגיסטרים של האב לבן:
    * childregs = *regs;
  4. מעדכנת את ערכו של rax ל-0 בתהליך הבן:
    childregs->rax = 0;
  5. מעדכנת את כתובת החזרה השמורה על המחסנית:
    struct inactive_task_frame* frame =
    (struct inactive_task_frame*)(childregs) - 1;
    frame->ret_addr = (unsigned long)ret_from_fork;
  6. מצביעה את p->thread.rsp לראש מחסנית הגרעין של הבן:
    p->thread.rsp = (unsigned long)frame;
20
Q

מה תפקיד הפונקציה ret_from_fork?

A

פונקציה זו מופעלת כאשר תהליך הבן מזומן לראשונה למעבד במהלך החלפת הקשר.

עושה POP_REGS ואז sysret.

למעשה, ביצוע הקוד ב-ret_from_fork יגרום לסיום הקריאה fork() בתהליך הבן עם ערך מוחזר 0.

21
Q

מה עושה הפונקציה do_exit?

A
  1. משחררת את המשאבים שבשימוש התהליך: סוגרת קבצים פתוחים, משחררת איזורי זיכרון, וכולי.
  2. רושמת את ערך הסיום של exit() לשדה exit_code ב-PCB.
  3. מעדכנת קשרי משפחה: כל בניו של התהליך שסיים הופכים להיות בנים של init.
  4. משנה את מצב התהליך ל-TASK_ZOMBIE.
  5. קוראת לפונקציה schedule(), אשר מוציאה את התהליך מתור הריצה ומזמנת לריצה תהליך אחר במקומו. ריצת התהליך מסתיימת סופית בפונקציה __switch_to_asm().
22
Q

איך ומתי נמחק הPCB של תהליך בן?

A

ה-PCB של הבן מפונה רק כאשר תהליך האב מקבל חיווי על סיום התהליך, באמצעות קריאת מערכת כדוגמת wait().

מחיקת ה-PCB מבוצעת ע”י הפונקציה release_task() שנקראת מתוך המימוש של wait().
פונקציה זו, בין השאר, מנתקת את התהליך מרשימת התהליכים הגלובאלית ומטבלת הערבול PID->PCB, ומפנה את השטח המוקצה ל-PCB ומחסנית הגרעין.