This question spans 3 stages — each part below is tagged with, and links to, the stage it belongs to.
תזכורת: המבנה thread שבתוך מתאר התהליך מכיל את השדות esp, esp0, eip. הגרעין משתמש בשדות הללו כדי לשמור את מצב המעבד במהלך החלפת הקשר. מהנדס הגרעין מכלוף הקשרוביץ' ניסה להקטין את המבנה thread על ידי מחיקת שדות לא נחוצים. השדה הראשון שמכלוף ניסה להסיר הוא thread.esp0. מכלוף ניסה להסיר גם את השדה thread.eip, ובעזרת מספר תיקונים בקוד הגרעין הצליח במשימה. בסעיפים הבאים (12,13,14) אתם נדרשים למצוא את התיקונים הנדרשים על מנת שהגרעין יעבוד בצורה תקינה ללא השדה thread.eip במתאר התהליך. התיקון הראשון שמכלוף ביצע הוא במאקרו switch_to, אשר נקרא מתוך פונקצית הגרעין context_switch שמבצעת החלפת הקשר בין שני תהליכים בלינוקס: #define switch_to(prev, next, last) 1. movl prev, eax 2. movl next, %edx 3. pushl %esi 4. pushl %edi 5. pushl %ebp ??? 6. movl %esp, prev->thread.esp 7. movl next->thread.esp, %esp 8. movl $1f, prev->thread.eip 9. pushl next->thread.eip 10. jmp _switch_to 11. 1: ??? 12. popl %ebp 13. popl %edi 14. popl %esi כאשר prev מצביע למתאר התהליך הנוכחי, ואילו next מצביע למתאר התהליך הבא שיקבל את המעבד. כפי שניתן לראות, בחלק מהתיקונים הנדרשים, מכלוף הסיר את שורות 8,9. המלצה: פתרו את סעיפים 12,13, ו-14 יחדיו. תזכורת: פונקצית הגרעין copy_thread נקראת במהלך קריאת המערכת fork, ומאתחלת את תכולת מחסנית הגרעין של תהליך הבן ואת שדה thread במתאר תהליך הבן, ומוגדרת באופן הבא: 1. int copy_thread(..., struct task_struct* p, struct pt_regs* regs) { 2. struct pt_regs* childregs; 3. childregs = ((struct pt_regs*)(8192 + (unsigned long)p)) - 1; 4. struct_cpy(childregs, regs); 5. childregs->eax = 0; 6. p->thread.esp = (unsigned long) childregs; 7. p->thread.esp0 = (unsigned long) (childregs+1); 8. p->thread.eip = (unsigned long) ret_from_fork; 9. ??? 10. ... 11. } כאשר p מצביע למתאר תהליך הבן, ו-regs מצביע לרגיסטרים שאוחסנו במחסנית הגרעין של האב בעת קבלת הפסיקה וביצוע המאקרו SAVE_ALL כפי שניתן לראות, בחלק מהתיקונים הנדרשים, מבלוף הקשרוביץ' הסיר את שורה 9.
Full original question text (raw OCR)
שאלה 2 – החלפת הקשר (25 נק') – 5 סעיפים
מה הצירוף של פקודות מכונה השקול לפקודה "ret"? א. popl %eip ב. movl (%esp), %eip ג. popl %eip subl $4, %esp ד. movl (%esp), %eip subl $4, %esp ה. subl $4, %esp popl %eip ו. subl $4, %esp movl (%esp), %eip
Voluntary vs preemptive context switchLatency vs throughput process classificationNon-preemptible kernel implicationsבאיזה ביטוי ניתן להחליף את השדה thread.esp0 ועדיין לקבל קוד גרעין תקין? א. thread.eip >> 0xffffe000 ב. thread.esp >> 0xffffe000 ג. thread.eip & 0xffffe000 ד. thread.esp & 0xffffe000 ה. (thread.eip & 0xffffe000) + 8192 ו. (thread.esp & 0xffffe000) + 8192
Pipe IPC semanticsאיזו שורה צריכה להיכנס בין שורות 5 ו-6? א. pushl %eip ב. pushl $1f ג. pushl prev->thread.eip ד. pushl next->thread.eip ה. pushl ret_from_fork ו. אין צורך להוסיף דבר במקום הנ"ל
fork/exec address-space semanticslibc syscall wrapper caching pitfallsPipe IPC semanticsאיזו שורה צריכה להיכנס בין שורות 11 ו-12? א. popl %eip ב. addl $4, esp ג. popl prev->thread.eip ד. popl next->thread.eip ה. jmp ret_from_fork ו. אין צורך להוסיף דבר במקום הנ"ל
fork/exec address-space semanticslibc syscall wrapper caching pitfallsPipe IPC semanticsמה צריך להתווסף במקום שורה 9? א. p->thread.esp -= 4; ב. p->thread.esp -= 4; *(unsigned long*)p->thread.esp = (unsigned long) ret_from_fork; ג. childregs->esp -= 4; ד. *(unsigned long*)childregs->esp = (unsigned long) ret_from_fork; ה. childregs->esp -= 4; *(unsigned long*)childregs->esp = (unsigned long) ret_from_fork; ו. אין צורך להוסיף דבר במקום הנ"ל
fork/exec address-space semanticslibc syscall wrapper caching pitfallsPipe IPC semantics
The exam question — original PDF
pages 6, 7, 8Exactly as it appears on the exam paper.
Built from these components
Ordered basic → advanced. Master the earlier ones first.
Review the material
Read these before you answer — each verified slide teaches a component this question tests, and nothing from an unrelated topic is included. Tutorial slides show the actual slide image.
אחרי fork()parentint main() { int x = 0; pid_t p = fork(); if (p == 0) { x = 1; } else { x = 2; }}sonint main() { int x = 0; pid_t p = fork(); if (p == 0) { x = 1; } else { x = 2; }}מערכות הפעלה - תרגול 210
הדפסה מתואמת למסךשימוש ב-wait() יכול לפתור את הבעיה שראינו קודם כאשר מדפיסים למסך במקביל משני תהליכים:int main() { pid_t p = fork(); if (p > 0) { // parent waits for child wait(NULL); } printf(“hello”); return 0;}מערכות הפעלה - תרגול 215
קריאות המערכת getpid(), getppid()pid_t getpid();קריאת מערכת המחזירה לתהליך הקורא את ה-pid של עצמו.pid_t getppid();קריאת מערכת המחזירה את ה-PID של תהליך האב של התהליך הקורא.שאלה: מה המשמעות של getppid() == 1 עבור תהליך משתמש טיפוסי?תשובה: תהליך האב הוא init. קורה למשל אם תהליך הבן יתום.מערכות הפעלה - תרגול 222
FD (file descriptors)כל פעולות קלט/פלט של תהליך בלינוקס מבוצעות דרך "קבצים":קבצים "רגילים" לאחסון מידע (/usr/file.txt) נמצאים בדיסק.התקני חומרה גם כן מיוצגים כקבצים, אבל נמצאים בזיכרון.למשל, העכברים המחוברים למחשב מיוצגים כ- /dev/input/mouseN .גם ערוצי תקשורת כמו pipes מיוצגים ע"י קבצים שנמצאים בזיכרון.הקשר בין תהליך לבין קובץ שהוא ניגש אליו נשמר, ברמת המשתמש, ע"י מספר שלם שנקרא file descriptor (FD).לדוגמה: קריאת המערכת open() מחזירה FD.המשתמש מעביר את ה-FD לקריאות מערכת כמו read(), write() כדי לקרוא ולכתוב לקובץ.מערכות הפעלה - תרגול 320
שחרור file objectשאלה: מי מבצע את שחרור הזיכרון של file object? מתי ניתן לשחררו? ייתכנו מצבים בהם תהליכים שונים מצביעים לאותו file object, לכן שחרור ה-file object יכול להתבצע רק לאחר ביצוע close() מכל התהליכים החולקים את אותו ה-file object. זכרו של-file object יש מונה (f_count) הסופר את כמות התהליכים המצביעים עליו בכל רגע נתון. המונה קטן באחד עם כל פעולת close() על האובייקט. כאשר המונה מתאפס, ה-file object ישוחרר.מערכות הפעלה - תרגול 342
TL;DRזַמָּן התהליכים ((scheduler הוא הרכיב במערכת ההפעלה שאחראי על בחירת התהליך הבא שירוץ על המעבד.אנחנו נלמד, בתור דוגמה, את אלגוריתם הזימון של לינוקס.אבל לפני שנלמד דוגמה "אמיתית" ומורכבת, נרצה להבין את הגישות הבסיסיות בזימון תהליכים כדי לפתח אינטואיציה.מערכות הפעלה - תרגול 52זימון תהליכים בלינוקסCFS = completely fair schedulerאלגוריתם זימון של תהליכים רגיליםSCHED_FIFO, SCHED_RRאלגוריתם זימון של תהליכי זמן אמת
אלגוריתם SRTFSRTF = shortest remaining time firstנקרא גם: STCF = shortest time to completion firstאופן פעולה: כמו SJF, אבל עם הפקעות.בכל פעם שתהליך חדש מגיע למערכת, SRTF מחשב למי מבין התהליכים (כולל התהליך החדש) נותר הכי פחות זמן לרוץ, ובוחר את התהליך הזה לריצה.תחת ההנחות החדשות, ניתן להוכיח כי SRTF אופטימלי במדד זמן התגובה הממוצע.בתוספת הנחה כי זמן החלפת הקשר הוא אפסי.מערכות הפעלה - תרגול 515כל התהליכים רצים למשך אותו זמן.כל התהליכים מגיעים באותו זמן (t=0).אם תהליך התחיל לרוץ, אז הוא ירוץ עד לסיומו ללא הפסקות.התהליכים משתמשים רק במעבד ולא מבצעים I/O.זמן הריצה של כל התהליכים ידוע מראש.
נהוג לסווג תהליכים לשני סוגיםתהליך אינטראקטיבי I/O Boundמעוניין בזמן המתנה נמוך.latency sensitive.דוגמה: נגן סרטים שמחליף 60 פריימים בשנייה.בדרך-כלל מוותר על המעבד מרצונו אחרי פרק זמן קצר בגלל המתנה לפעולות I/O.תהליך חישוביCPU Boundמעוניין בזמן תגובה נמוך.throughput sensitive.דוגמה: סקריפט python שמנתח נתונים ע"י חישובים אלגבריים.בדרך-כלל לא מוותר על המעבד מרצונו אלא מופקע.מערכות הפעלה - תרגול 523
מדיניות זימון של תהליךלכל תהליך זמן-אמת יש מדיניות זימון (scheduling policy):SCHED_FIFO או SCHED_RR .נקבעת ע"י המשתמש באמצעות קריאות מערכת sched_setscheduler() .מדיניות הזימון – תשפיע על כמה זמן ריצה כל תהליך יקבל ואופן עבודת התור.מערכות הפעלה - תרגול 535
מהי החלפת הקשר?מערכות הפעלה - תרגול 610
מהי החלפת הקשר?לכל תהליך יש "הקשר ביצוע" (execution context) המכיל את כל המידע הדרוש לביצוע התהליך.מחסניות, רגיסטרים, תכולת זיכרון, קבצים פתוחים, ..."החלפת הקשר" = עצירת הביצוע של התהליך הנוכחי ושמירת ההקשר שלו.טעינת ההקשר של התהליך הבא לביצוע.הקשר התהליך הנוכחי מתחלף – מכאן שם הפעולה "החלפת הקשר".מערכות הפעלה - תרגול 611
שני סוגים של החלפת הקשרהחלפת הקשר כפויה(== הפקעה)הגרעין מפקיע (כלומר, לוקח בכוח) את המעבד מהתהליך, למשל בעקבות:פסיקת שעון (מטופלת בשגרה scheduler_tick) אשר מגלה כי הזמן שהוקצב לתהליך הנוכחי אזל.אירוע אסינכרוני אשר מעיר תהליך בעל עדיפות טובה יותר מהתהליך הרץ כרגע.לדוגמה: פסיקת דיסק או שחרור מנעול שתהליך המתין לו.החלפת הקשר יזומההתהליך מוותר מרצונו על המעבד, למשל באמצעות:קריאת מערכת חוסמת (כמו wait(), read(), …) אשר מוציאה את התהליך להמתנה.קריאת מערכת exit() אשר מסיימת את התהליך.קריאת מערכת sched_yield() – קריאת מערכת ייעודית לוויתור על המעבד.מערכות הפעלה - תרגול 613
הפונקציה __switch_to_asm (3) popq %r15 popq %r14 popq %r13 popq %r12 popq %rbx popq %rbp jmp __switch_to מערכות הפעלה - תרגול 635שחזור הרגיסטרים ממחסנית הגרעין של next.אלו הרגיסטרים ש-next שמר כאשר הוא קרא להחלפת הקשר בעבר.קפיצה (jmp) במקום קריאה (call) לפונקציה. למה?כתובת החזרה מהפונקציה __switch_to() כבר שמורה על המחסנית של next.
מרחבי זיכרון וקריאות מערכתחוטים הנוצרים ע"י קריאת המערכתclone() משתפים את מרחב הזיכרון ע"י הצבעה לאותו מתאר מרחב הזיכרון של תהליך האב.יש להגדיל את מונה השיתוף (mm_users) של מתאר מרחב הזיכרון של תהליך האב.קריאת המערכת execv() ודומותיה טוענות תהליך חדש ולכן הן משחררות את מרחב הזיכרון ומקצות אחד חדש.קריאת המערכתfork() מקצה לתהליך הבן מרחב זיכרון משלו.במקרה שכזה צריך להעתיק את מרחב הזיכרון של האב לזה של הבן.בפועל, בדרך-כלל אין באמת העתקה בזכות מנגנון copy-on-write.23מערכות הפעלה - תרגול 10
The exam text, the skills it tests, and the exact slides are already in context.