OS PyramidTechnion 234123 · Operating Systemsbasics → exam
Stage 6: Synchronization & Threads
OS-Spring2021-examBQuestion 4core25 pts

This question spans 2 stages — each part below is tagged with, and links to, the stage it belongs to.

הערה: בסעיפים הבאים, מתייחסים למערכת עם מעבד יחיד, אלא אם נאמר אחרת.

  1. a· short_answer· 4 ptsSynchronization & Threads

    הסבירו את המונחים הבאים בקצרה: a. קטע קריטי b. מניעה הדדית c. Race condition d. Deadlock

    Mutex correctness and deadlock avoidanceBanker's algorithm safety check
  2. בהינתן קטעי הקוד משני חוטים (X ו-Y משתנים בוליאניים משותפים שמאותחלים באופן רנדומלי לפני שהחוטים מגיעים לקטעי קוד הבאים): Thread1: while (X == Y); // critical section X = Y; Thread 2: while(X != Y); // critical section X = !Y; אילו מהטיעונים הבאים נכון: a. הקוד מבטיח מניעה הדדית, אבל לא מבטיח ״התקדמות" (progress). b. הקוד מבטיח התקדמות, אבל לא מבטיח מניעה הדדית. c. הקוד לא מבטיח התקדמות ולא מבטיח מניעה הדדית. d. הקוד מבטיח מניעה הדדית והתקדמות. נימוק:

    Pipe IPC semantics
  3. להלן הצעה למימוש מנעול: המשתנה L משתנה מסוג int שמאותחל ל- 0, הפונקציה (fetch_and_add(x,c מבצעת את הפעולות הבאות באופן אטומי: קוראת ערך המשתנה x, מוסיפה אליו c ומחזירה את הערך לפני ההוספה. lock(L) { while (fetch_and_add(L, 1)) { L = 1; } } unlock(L) { L = 0; } האם המימוש הנ"ל עובד? כן / לא הסבירו בקצרה (אם המימוש לא עובד, תארו תרחיש בעייתי): נימוק:

    Mutex correctness and deadlock avoidance
  4. בהינתן השינוי הבא בקוד מסעיף קודם: lock(L) { while (fetch_and_add(L, 1)); } unlock(L) { L = 0; } האם המימוש החדש עובד? כן / לא הסבירו בקצרה (אם המימוש לא עובד, תארו תרחיש בעייתי): נימוק:

    libc syscall wrapper caching pitfallsSpinlock implementation propertiesSemaphore blocking and busy-wait
  5. יונתן, סטודנט לקורס מערכות הפעלה מימש מנעול חדש שתומך בשתי הפעולות הבאות: struct my_lock { unsigned int value = 0; }; void lock (struct my_lock *s) { unsigned int y; unsigned int *x = &(s->value); do { fetch_and_set(x, &y); } while (y); } void unlock (struct my_lock *s) { s->value = 0; } כאשר (fetch_and_set(x,y פעולה אטומית שמעדכנת את הערך ש- x מצביע עליו ל- 1, ושומרת את הערך הקודם בתוך המשתנה y. א ) אילו מהטיעונים הבאים נכון: 1. הקוד לא מבטיח מניעה הדדית. 2. המנעול עובד באופן תקין. 3. במערכת מרובת מעבדים, הקוד עלול ליצור deadlock. 4. במערכת מרובת מעבדים, הקוד לא מבטיח מניעה הדדית. נימוק:

    libc syscall wrapper caching pitfallsMutex correctness and deadlock avoidanceBanker's algorithm safety check
  6. f· short_answer· 2 ptsSynchronization & Threads

    ב) בהנחה שהמנעול הנ"ל עובד (תוקן במידת הצורך), משיקולי יעילות, באיזה מרחב (גרעין או משתמש) עדיף להשתמש במנעול כזה? גרעין / משתמש נימוק:

    fork/exec address-space semanticsMutex correctness and deadlock avoidance
  7. g· code· 4 ptsDeadlock

    יונתן שינה את קוד הגרעין כך שהמנעול החדש משמש להגנה על תור הריצה ובנוסף הוא הוסיף קריאת מערכת חדשה שמדפיסה את כל התהליכים שנמצאים בתור הריצה: void print_rq_syscall() { lock(&rq_lock); // // print run queue // unlock(&rq_lock); } יונתן שם לב שבחלק מהמקרים, קריאת המערכת החדשה גורמת למחשב להיתקע. הסבירו למה זה קורה, הדגימו באמצעות תרחיש, והציעו דרך לתיקון הבעיה. נימוק:

    libc syscall wrapper caching pitfallsMutex correctness and deadlock avoidance

The exam question — original PDF

pages 12, 13, 14, 15

Exactly as it appears on the exam paper.

loading page 12
loading page 13
loading page 14
loading page 15

Built from these components

Ordered basic → advanced. Master the earlier ones first.

L3fork/exec address-space semanticsL3libc syscall wrapper caching pitfallsL3Local vs global interrupt disableL3Pipe IPC semanticsL3Spinlock implementation propertiesL3Semaphore blocking and busy-waitL3Mutex correctness and deadlock avoidanceL4Banker's algorithm safety check

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.

Tutorial 2slide 10אחרי fork()parentint main() { int x = 0; pid_t p = fork(); if (p == 0) { x = 1; } else { x = 2; }}sonint main() { int...

אחרי 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

Tutorial 2slide 15הדפסה מתואמת למסךשימוש ב-wait() יכול לפתור את הבעיה שראינו קודם כאשר מדפיסים למסך במקביל משני תהליכים:int main() { pi...

הדפסה מתואמת למסךשימוש ב-wait() יכול לפתור את הבעיה שראינו קודם כאשר מדפיסים למסך במקביל משני תהליכים:int main() { pid_t p = fork(); if (p > 0) { // parent waits for child wait(NULL); } printf(“hello”); return 0;}מערכות הפעלה - תרגול 215

Tutorial 2slide 22קריאות המערכת getpid(), getppid()pid_t getpid();קריאת מערכת המחזירה לתהליך הקורא את ה-pid של עצמו

קריאות המערכת getpid(), getppid()pid_t getpid();קריאת מערכת המחזירה לתהליך הקורא את ה-pid של עצמו.pid_t getppid();קריאת מערכת המחזירה את ה-PID של תהליך האב של התהליך הקורא.שאלה: מה המשמעות של getppid() == 1 עבור תהליך משתמש טיפוסי?תשובה: תהליך האב הוא init. קורה למשל אם תהליך הבן יתום.מערכות הפעלה - תרגול 222

Tutorial 3slide 20FD (file descriptors)כל פעולות קלט/פלט של תהליך בלינוקס מבוצעות דרך "קבצים":קבצים "רגילים" לאחסון מידע (/usr/file

FD (file descriptors)כל פעולות קלט/פלט של תהליך בלינוקס מבוצעות דרך "קבצים":קבצים "רגילים" לאחסון מידע (/usr/file.txt) נמצאים בדיסק.התקני חומרה גם כן מיוצגים כקבצים, אבל נמצאים בזיכרון.למשל, העכברים המחוברים למחשב מיוצגים כ- /dev/input/mouseN .גם ערוצי תקשורת כמו pipes מיוצגים ע"י קבצים שנמצאים בזיכרון.הקשר בין תהליך לבין קובץ שהוא ניגש אליו נשמר, ברמת המשתמש, ע"י מספר שלם שנקרא file descriptor (FD).לדוגמה: קריאת המערכת open() מחזירה FD.המשתמש מעביר את ה-FD לקריאות מערכת כמו read(), write() כדי לקרוא ולכתוב לקובץ.מערכות הפעלה - תרגול 320

Tutorial 3slide 42שחרור file objectשאלה: מי מבצע את שחרור הזיכרון של file object? מתי ניתן לשחררו? ייתכנו מצבים בהם תהליכים שונים מצביע...

שחרור file objectשאלה: מי מבצע את שחרור הזיכרון של file object? מתי ניתן לשחררו? ייתכנו מצבים בהם תהליכים שונים מצביעים לאותו file object, לכן שחרור ה-file object יכול להתבצע רק לאחר ביצוע close() מכל התהליכים החולקים את אותו ה-file object. זכרו של-file object יש מונה (f_count) הסופר את כמות התהליכים המצביעים עליו בכל רגע נתון. המונה קטן באחד עם כל פעולת close() על האובייקט. כאשר המונה מתאפס, ה-file object ישוחרר.מערכות הפעלה - תרגול 342

Tutorial 7slide 14יצירת חוט חדשפרמטרים:thread – מצביע למקום בו יאוחסן מזהה החוט החדש במקרה של סיום הפונקציה בהצלחה

יצירת חוט חדשפרמטרים:thread – מצביע למקום בו יאוחסן מזהה החוט החדש במקרה של סיום הפונקציה בהצלחה.attr – מאפיינים המתארים את תכונות החוט החדש, כגון האם החוט הוא חוט גרעין או חוט משתמש, האם ניתן לבצע לו join, כלומר להמתין לסיומו, וכו'. בד"כ נספק ערך NULL המציין חוט ברירת המחדל של המערכת, שניתן להמתין לסיומו.void* (*start_routine)(void*) מצביע לפונקציה שתהווה את קוד החוט. הערך המוחזר מפונקציה זו במקרה של סיומה הטבעי הינו ערך הסיום של החוט.arg – פרמטר שיסופק לפונקציה עם הפעלתה.מערכות הפעלה - תרגול 714

Tutorial 7slide 45מנעולים עם/בלי המתנהspinlockכאשר חוט מנסה לתפוס מנעול נעול, הוא בודק את ערך המנעול שוב ושוב מבלי לצאת מתור הריצה

מנעולים עם/בלי המתנהspinlockכאשר חוט מנסה לתפוס מנעול נעול, הוא בודק את ערך המנעול שוב ושוב מבלי לצאת מתור הריצה.טכניקה כזו נקראת גם: polling, busy waiting, busy looping.יתרון: התהליך יכול להמשיך לרוץ עוד בקוונטום הנוכחי, וכך לחסוך את התקורה על החלפת הקשר.עדיף כאשר זמן ההמתנה המשוער נמוך יותר מהמחיר של החלפת הקשר (כלומר, כאשר הקטע הקריטי קצר, כפי שקורה לרוב בקוד גרעין).mutexכאשר חוט מנסה לתפוס מנעול נעול, מערכת ההפעלה תעביר אותו לתור המתנה ותבצע החלפת הקשר.כאשר המנעול ישוחרר, מערכת ההפעלה תעיר את אחד החוטים המחכים למנעול.יתרון: תהליך חדש יכול לרוץ מיד, וכך לא מתבזבז זמן מעבד יקר.עדיף כאשר זמן ההמתנה המשוער גבוה יחסית (כלומר, כאשר הקטע הקריטי ארוך, כפי שקורה לרוב בקוד משתמש).מערכות הפעלה - תרגול 745

Tutorial 8slide 2TL;DRבתרגול הקודם למדנו לכתוב קוד מקבילי באמצעות חוטים

TL;DRבתרגול הקודם למדנו לכתוב קוד מקבילי באמצעות חוטים.ראינו שבכל בעיה לא טריוויאלית יש צורך בסנכרון בין החוטים.היום נלמד על מנגנוני סנכרון נוספים של ממשק pthreads :לבסוף, נלמד דוגמה נוספת של קוד מקבילי חשוב: גרעין לינוקס.קוד הגרעין לא משתמש בחוטים, אבל ניגש לזיכרון משותף מתוך מספר מסלולי בקרה שרצים במקביל, ולכן העקרונות שלמדנו תקפים גם עבורו.2מערכות הפעלה - תרגול 8להבטחת סדרלהבטחת אטומיותמשתני תנאי(condition variables)מנעולים(mutexes)סמפורים (semaphores)

Tutorial 8slide 6משתנה תנאי (condition variable)משתנה תנאי הוא אובייקט סנכרון המאפשר לחוט לצאת להמתנה בתוך קטע קריטי

משתנה תנאי (condition variable)משתנה תנאי הוא אובייקט סנכרון המאפשר לחוט לצאת להמתנה בתוך קטע קריטי.כלומר, לפנות את המעבד ולצאת לתור המתנה.ההמתנה תתבצע עד לקיום תנאי כלשהו.ההמתנה מאפשרת לאכוף סדר בביצוע של החוטים. שימוש תכנותי נכון במשתני תנאי מחייב להגדיר גם:משתנה מצב – החוט עובר להמתנה או חוזר מהמתנה בהתאם לערכו של משתנה המצב.מנעול mutex – מבטיח לנו אטומיות והגנה על הקטע הקריטי.מערכות הפעלה - תרגול 86

Tutorial 8slide 7סכימה כללית למשתני תנאיcond_t c; // should be initializedmutex_t m; // should be initializedint state_var = 0;החוט המ...

סכימה כללית למשתני תנאיcond_t c; // should be initializedmutex_t m; // should be initializedint state_var = 0;החוט הממתין לאירוע יקרא ל:while (!condition_holds(state_var)) cond_wait(&c, &m);החוט שמסמן לחוטים הממתינים להמשיך יקרא ל:if (condition_holds(state_var)) cond_signal(&c);מערכות הפעלה - תרגול 87מדוע cond_wait() מקבלתגם את המנעול?

Tutorial 8slide 11המתנה על משתני תנאיint pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);פעולה: משחררת את המנעול ומעביר...

המתנה על משתני תנאיint pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);פעולה: משחררת את המנעול ומעבירה את החוט להמתין על משתנה התנאי באופן אטומי (ראינו קודם מדוע זה הכרחי).החוט הממתין חייב להחזיק במנעול mutex לפני הקריאה.בחזרה מהמתנה על משתנה התנאי, החוט עובר להמתין על המנעול. החוט יחזור מהקריאה ל-pthread_cond_wait() רק לאחר שינעל מחדש את ה-mutex.ערך מוחזר: הפעולה תמיד מצליחה ומחזירה 0.11מערכות הפעלה - תרגול 8

Tutorial 8slide 38מועד א', אביב 2008, שאלה 1sem_t sem; // Global semaphore, with initial value 1 int writer_lock() { sem_wait(sem);} in...

מועד א', אביב 2008, שאלה 1sem_t sem; // Global semaphore, with initial value 1 int writer_lock() { sem_wait(sem);} int writer_unlock() { sem_post(sem);} int reader_lock() { while(sem_getvalue(sem) <= 0) sleep(1); sem_wait(sem);} int reader_unlock() { sem_post(sem);}סעיף ב: להלן הצעה לפתרון בעיית קוראים/כותבים עם עדיפות לכותבים, המשתמשת בסמפורים.תארו 3 בעיות שונות של נכונות ו/או יעילות שיש בפתרון הנ"ל. הניחו כי הסמפור הינו הוגן.מערכות הפעלה - תרגול 838

Tutorial 8slide 39מועד א', אביב 2008, שאלה 1sem_t sem; // Global semaphore, with initial value 1 int writer_lock() { sem_wait(sem);} in...

מועד א', אביב 2008, שאלה 1sem_t sem; // Global semaphore, with initial value 1 int writer_lock() { sem_wait(sem);} int writer_unlock() { sem_post(sem);} int reader_lock() { while(sem_getvalue(sem) <= 0) sleep(1); sem_wait(sem);} int reader_unlock() { sem_post(sem);}בעיית נכונות: הפתרון לא מאפשר ליותר מקורא אחד להיכנס לקטע קריטי.בעיית נכונות: אם יש גם קוראים וגם כותבים, הכותבים לא בהכרח יקבלו עדיפות ועלולים להיות מורעבים בניגוד לדרישה.בעיית יעילות: קוראים מבצעים busy wait.מערכות הפעלה - תרגול 839

Tutorial 8slide 56פסיקות חומרה במערכת מרובת ליבותבמערכת מרובת ליבות, מעבדים שונים יכולים לגשת בו-זמנית למבני נתונים משותפים  יש להוסיף...

פסיקות חומרה במערכת מרובת ליבותבמערכת מרובת ליבות, מעבדים שונים יכולים לגשת בו-זמנית למבני נתונים משותפים  יש להוסיף נעילה מעבר לחסימת הפסיקות המקומית.שגרות טיפול בפסיקות חומרה עושות שימוש במנעולי spinlock (מנעולים הממומשים כ-busy wait).שאלה: מדוע מעדיפים מנעולי spinlock על-פני מנעולי סמפור?busy wait הוא המתנה יעילה יותר כאשר מדובר בנעילות קצרות מאוד כפי שקורה בגרעין, מפני שכך נחסכת התקורה של כניסה ויציאה מהמתנה.בעיית הוגנות, למשל בתרחיש הבא:תהליך רץ, ובאותו הזמן מתקבלת פסיקת חומרה (למשל מהמקלדת).הטיפול בפסיקה מנסה לתפוס את המנעול, אבל המנעול כבר תפוס.התהליך עובר לתור המתנה מסיבה שאינה תלויה בו.מערכות הפעלה - תרגול 856

Ask Gemini
OS-Spring2021-examB · Q4 — question + its material already loaded
Pick a shortcut above or ask anything about this question.
The exam text, the skills it tests, and the exact slides are already in context.