OS PyramidTechnion 234123 · Operating Systemsbasics → exam
Stage 2: Processes & Signals
2018A_Winter_AQuestion 1core25 pts

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

חברת MaKore, הכורה (mining) מטבעות דיגיטליים, מריצה בכל רגע מספר גדול של תהליכים על-מנת להאיץ את פעולת הכרייה. התהליכים שומרים את תוצאות החישובים שלהם לקבצים בדיסק כדי למנוע אובדן מידע במקרה שהתהליך קורס לפתע. בכל שניה התהליך קורא לפונקציה הבאה כדי ליצור את שם הקובץ שבו ייכתב הפלט שלו: #include <string> using namespace std; string create_file_name(time_t timestamp) { pid_t pid = getpid(); string s = “results-” + to_string(pid) + to_string(timestamp); return s; } כפי שניתן לראות, כל תהליך מוסיף את ה-PID שלו לשם הקובץ כדי למנוע התנגשות בין קבצים של תהליכים שונים. לצורך הפשטות, לאורך כל השאלה הניחו כי החברה אינה משתמשת בחוטים כלל. שרה, בוגרת הקורס ומהנדסת צעירה בחברה, הבחינה כי הפונקציה הנ"ל נקראת פעמים רבות במהלך הריצה של כל תהליך. לכן שרה הציעה את השיפור הבא לקוד המקורי: pid_t pid = getpid(); string create_file_name(time_t timestamp) { string s = “results-” + to_string(pid) + to_string(timestamp); return s; } דנה, מהנדסת בכירה בחברה, התלהבה מהרעיון של שרה והחליטה לקחת אותו צעד אחד קדימה. דנה עדכנה את פונקצית המעטפת (wrapper function) של קריאת המערכת ()getpid כפי שמופיעה בספרית libc באופן הבא: 1. + pid_t cached_pid = -1; // global variable 2. pid_t getpid() { 3. unsigned int res; 4. + if (cached_pid != -1) { 5. + return cached_pid; 6. + } 7. __asm__ volatile( 8. "int 0x80;" 9. :"=a"(res) :"a"(__NR_getpid) :"memory" 10. ); 11. + cached_pid = res; 12. return res; 13. } שורות מסומנות ב-+" הן שורות שדנה הוסיפה לקוד המקורי. אלו השורות היחידות שהשתנו בספריה. תזכורת: שורת האסמבלי שומרת את הערך "NR_getpid__" ברגיסטר eax לפני ביצוע הפקודה, ומציבה את ערך eax לאחר ביצוע הפקודה במשתנה res. שרה השתמשה בספריה החדשה (של דנה), אך תוכניות מסוימות שעבדו לפני השינוי הפסיקו לעבוד כנדרש עם הספריה החדשה. כדי לתקן את התקלה שנוצרה, דנה מציעה בנוסף את התיקון הבא של פונקציית המעטפת של fork: 1. + // the same global variable from above 2. + extern pid_t cached_pid; 3. pid_t fork() { 4. unsigned int res; 5. __asm__ volatile( 6. "int 0x80;" 7. :"=a"(res) :"a"(__NR_fork) :"memory" 8. ); 9. + ??? 10. return res; 11. } סאטושי, מנהל החברה, הבחין כי למרות התיקון לעיל, הספריה החדשה עדיין בעייתית כאשר הקוד משתמש בסיגנלים. סאטושי הדגים את הבעיה באמצעות הקוד הבא: 1. void my_signal_handler(int signum) { 2. cout << getpid() << endl; 3. } 4. int main() { 5. // set a new signal handler 6. signal(SIGUSR1, my_signal_handler); 7. pid_t pid = fork(); 8. if (pid > 0) { // parent 9. kill(pid, SIGUSR1); // send a signal to the child 10. wait(NULL); 11. } 12. }

Full original question text (raw OCR)

חלק 1 - קריאות מערכת (25 נק') חברת MaKore, הכורה (mining) מטבעות דיגיטליים, מריצה בכל רגע מספר גדול של תהליכים על-מנת להאיץ את פעולת הכרייה. התהליכים שומרים את תוצאות החישובים שלהם לקבצים בדיסק כדי למנוע אובדן מידע במקרה שהתהליך קורס לפתע. בכל שניה התהליך קורא לפונקציה הבאה כדי ליצור את שם הקובץ שבו ייכתב הפלט שלו: #include <string> using namespace std; string create_file_name(time_t timestamp) { pid_t pid = getpid(); string s = “results-” + to_string(pid) + to_string(timestamp); return s; } כפי שניתן לראות, כל תהליך מוסיף את ה-PID שלו לשם הקובץ כדי למנוע התנגשות בין קבצים של תהליכים שונים. לצורך הפשטות, לאורך כל השאלה הניחו כי החברה אינה משתמשת בחוטים כלל. 1. (5 נק') היכן הגרעין שומר את ה-PID של התהליך? נימוק: שרה, בוגרת הקורס ומהנדסת צעירה בחברה, הבחינה כי הפונקציה הנ"ל נקראת פעמים רבות במהלך הריצה של כל תהליך. לכן שרה הציעה את השיפור הבא לקוד המקורי: pid_t pid = getpid(); string create_file_name(time_t timestamp) { string s = “results-” + to_string(pid) + to_string(timestamp); return s; } 2. (5 נק') מדוע הפתרון של שרה עדיף על המימוש המקורי? נימוק: דנה, מהנדסת בכירה בחברה, התלהבה מהרעיון של שרה והחליטה לקחת אותו צעד אחד קדימה. דנה עדכנה את פונקצית המעטפת (wrapper function) של קריאת המערכת ()getpid כפי שמופיעה בספרית libc באופן הבא: 1. + pid_t cached_pid = -1; // global variable 2. pid_t getpid() { 3. unsigned int res; 4. + if (cached_pid != -1) { 5. + return cached_pid; 6. + } 7. __asm__ volatile( 8. "int 0x80;" 9. :"=a"(res) :"a"(__NR_getpid) :"memory" 10. ); 11. + cached_pid = res; 12. return res; 13. } • שורות מסומנות ב-+" הן שורות שדנה הוסיפה לקוד המקורי. אלו השורות היחידות שהשתנו בספריה. • תזכורת: שורת האסמבלי שומרת את הערך "NR_getpid__" ברגיסטר eax לפני ביצוע הפקודה, ומציבה את ערך eax לאחר ביצוע הפקודה במשתנה res. 3. (5 נק') מהי התקלה שנוצרה בעקבות השינוי? נימוק: כדי לתקן את התקלה שנוצרה, דנה מציעה בנוסף את התיקון הבא של פונקציית המעטפת של fork: 1. + // the same global variable from above 2. + extern pid_t cached_pid; 3. pid_t fork() { 4. unsigned int res; 5. __asm__ volatile( 6. "int 0x80;" 7. :"=a"(res) :"a"(__NR_fork) :"memory" 8. ); 9. + ??? 10. return res; 11. } 4. (5 נק') השלימו את התיקון הנדרש בשורה 10: נימוק: סאטושי, מנהל החברה, הבחין כי למרות התיקון לעיל, הספריה החדשה עדיין בעייתית כאשר הקוד משתמש בסיגנלים. סאטושי הדגים את הבעיה באמצעות הקוד הבא: 1. void my_signal_handler(int signum) { 2. cout << getpid() << endl; 3. } 4. int main() { 5. // set a new signal handler 6. signal(SIGUSR1, my_signal_handler); 7. pid_t pid = fork(); 8. if (pid > 0) { // parent 9. kill(pid, SIGUSR1); // send a signal to the child 10. wait(NULL); 11. } 12. } 5. (5 נק') מהי התקלה בקוד לעיל ומתי היא תתרחש? נימוק:

  1. (5 נק') היכן הגרעין שומר את ה-PID של התהליך? a. בספריה .libc b. במחסנית המשתמש. c. במחסנית הגרעין. d. בערימה. e. במתאר התהליך (ה-PCB). f. בתור הריצה (runqueue)

    fork/exec address-space semanticslibc syscall wrapper caching pitfalls
  2. 2· mcq· 5 ptsIntroduction

    (5 נק') מדוע הפתרון של שרה עדיף על המימוש המקורי? a. פחות החלפות הקשר. b. פחות קריאות מערכת. c. עדיפות ריצה גבוהה יותר כי התהליך יסווג כחישובי. d. עדיפות ריצה גבוהה יותר כי התהליך יסווג כאינטראקטיבי. e. פחות החטאות TLB. f. פחות חריגות דף.

    nice value and dynamic priorityLatency vs throughput process classificationlibc syscall wrapper caching pitfallsVirtual-to-physical address translation
  3. (5 נק') מהי התקלה שנוצרה בעקבות השינוי? a. אם שני תהליכים קוראים ל-()getpid בו-זמנית עלול להיווצר race condition. b. fork().b עלולה לחזור עם אותו ערך בתהליך האב ובתהליך הבן. c. fork().C עלולה להיכשל במקרים בהם המימוש המקורי היה מצליח. d. getpid().d עלולה להחזיר pid של תהליך אחר. e. getpid().e עלולה להחזיר "1-". f. execv() .f עלולה להיכשל במקרים בהם המימוש המקורי היה מצליח.

    System call trap and kernel entryfork/exec address-space semanticslibc syscall wrapper caching pitfalls
  4. (5 נק') השלימו את התיקון הנדרש בשורה 10: a. if (res == 0) cached_pid = -1; b. if (res == 0) cached_pid = getpid(); c. if (res == 0) return cached_pid; d. if (res > 0) cached_pid = -1; e. if (res > 0) cached_pid = getpid(); f. if (res > 0) return cached_pid;

    System call trap and kernel entryfork/exec address-space semanticslibc syscall wrapper caching pitfalls
  5. (5 נק') מהי התקלה בקוד לעיל ומתי היא תתרחש? a. הסיגנל לא יטופל אם האב שלח את הסיגנל לפני שהבן התחיל לרוץ. b. הסיגנל לא יטופל אם האב שלח את הסיגנל אחרי שהבן סיים את שורה 8. c. הסיגנל לא יטופל ללא תלות בסדר הזימון של התהליכים. d. שורה 2 תדפיס ערך שגוי אם האב שלח את הסיגנל לפני שהבן התחיל לרוץ. e. שורה 2 תדפיס ערך שגוי אם האב שלח את הסיגנל אחרי שהבן סיים את שורה 8. f. שורה 2 תדפיס ערך שגוי ללא תלות בסדר הזימון של התהליכים.

    Signal delivery and handler timing

The exam question — original PDF

pages 2, 3, 4, 5, 6

Exactly as it appears on the exam paper.

loading page 2
loading page 3
loading page 4
loading page 5
loading page 6

Built from these components

Ordered basic → advanced. Master the earlier ones first.

L2System call trap and kernel entryL2nice value and dynamic priorityL2Latency vs throughput process classificationL3fork/exec address-space semanticsL3libc syscall wrapper caching pitfallsL3Local vs global interrupt disableL3Signal delivery and handler timingL3Pipe IPC semanticsL3Virtual-to-physical address translation

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 8קריאת המערכת kill#include <sys/types

קריאת המערכת kill#include <sys/types.h>#include <signal.h>int kill(pid_t pid, int sig);פעולה: שולחת את הסיגנל שמספרו sig לתהליך המזוהה ע"י pid.אם הערך של sigהוא 0, אז הפעולה רק בודקת שהתהליך pid קיים, מבלי לשלוח signal (שימושי לבדיקת תקפות pid).ערך מוחזר:0 בהצלחה-1 בכישלון (למשל, אם אין תהליך בעל מזהה pid)מערכות הפעלה - תרגול 38

Tutorial 3slide 9העברת סיגנלים בשני שלביםרישום – מערכת ההפעלה רושמת ב-PCB של תהליך היעד שיש לו סיגנל ממתין (pending signal)

העברת סיגנלים בשני שלביםרישום – מערכת ההפעלה רושמת ב-PCB של תהליך היעד שיש לו סיגנל ממתין (pending signal).הרישום מתבצע במערך בינארי בין 31 ביטים, ולכן לכל תהליך יכול להיות לכל היותר סיגנל ממתין אחד מכל מספר.טיפול – בכל פעם שהתהליך חוזר ממצב גרעין למצב משתמש, מערכת ההפעלה בודקת אם יש סיגנלים ממתינים ומטפלת בהם.בסיום הטיפול בסיגנל, מערכת ההפעלה תאפס את הביט המתאים במערך. במידה ויש מספר סיגנלים ממתינים, סדר הטיפול מתחילת המערך לסופו.מערכות הפעלה - תרגול 39

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 5slide 9דוגמת FCFSaverageResponseTime = (10 + 20 + 30) / 3 = 20כעת נסיר את הנחה 1 ("כל התהליכים רצים למשך אותו זמן")

דוגמת FCFSaverageResponseTime = (10 + 20 + 30) / 3 = 20כעת נסיר את הנחה 1 ("כל התהליכים רצים למשך אותו זמן"). לכל תהליך זמן ריצה משלו.תוכלו לחשוב על דוגמה שבה FCFS אינו יעיל?מערכות הפעלה - תרגול 59כל התהליכים רצים למשך אותו זמן.כל התהליכים מגיעים באותו זמן (t=0).אם תהליך התחיל לרוץ, אז הוא ירוץ עד לסיומו ללא הפסקות.התהליכים משתמשים רק במעבד ולא מבצעים I/O.זמן הריצה של כל התהליכים ידוע מראש.

Tutorial 5slide 23נהוג לסווג תהליכים לשני סוגיםתהליך אינטראקטיבי I/O Boundמעוניין בזמן המתנה נמוך

נהוג לסווג תהליכים לשני סוגיםתהליך אינטראקטיבי I/O Boundמעוניין בזמן המתנה נמוך.latency sensitive.דוגמה: נגן סרטים שמחליף 60 פריימים בשנייה.בדרך-כלל מוותר על המעבד מרצונו אחרי פרק זמן קצר בגלל המתנה לפעולות I/O.תהליך חישוביCPU Boundמעוניין בזמן תגובה נמוך.throughput sensitive.דוגמה: סקריפט python שמנתח נתונים ע"י חישובים אלגבריים.בדרך-כלל לא מוותר על המעבד מרצונו אלא מופקע.מערכות הפעלה - תרגול 523

Tutorial 5slide 50עדיפויותCFS מאפשר למשתמש להגדיר עדיפויות לתהליכים וכך לחלק את זמן המעבד בצורה שונה בין התהליכים

עדיפויותCFS מאפשר למשתמש להגדיר עדיפויות לתהליכים וכך לחלק את זמן המעבד בצורה שונה בין התהליכים.העדיפות של התהליך מיוצגת ע"י הערך -20 ≤ nice ≤ +19 .ברירת המחדל היא nice=0 .תהליך "נחמד" יותר יהיה בעדיפות נמוכה יותר.לכל עדיפות יש משקל:מערכות הפעלה - תרגול 550

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

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

Tutorial 8slide 12שחרור חוטים ממתיניםint pthread_cond_signal(pthread_cond_t *cond); משחררת את אחד החוטים הממתינים (הגינות לא מובטחת)

שחרור חוטים ממתיניםint pthread_cond_signal(pthread_cond_t *cond); משחררת את אחד החוטים הממתינים (הגינות לא מובטחת).int pthread_cond_broadcast(pthread_cond_t *cond);משחררת את כל החוטים הממתינים.כל החוטים מפסיקים להמתין על משתנה התנאי ועוברים להמתין על המנעול. החוטים יחזרו לפעילות בזה אחר זה (בסדר כלשהו, לאו דווקא הוגן) לאחר שינעלו מחדש את ה-mutex.שימו לב: אם אין אף חוט שממתין באותו רגע על משתנה התנאי cond, הפעולות חסרות השפעה (הסיגנל הולך לאיבוד ואינו נזכר הלאה).ערך מוחזר: הפונקציות תמיד מצליחות ומחזירות 0.מערכות הפעלה - תרגול 812

Tutorial 10slide 19טבלת הדפים (page table)לכל תהליך יש טבלת דפים משלו – מבנה נתונים אשר ממפה בין דפים למסגרות

טבלת הדפים (page table)לכל תהליך יש טבלת דפים משלו – מבנה נתונים אשר ממפה בין דפים למסגרות.ניתן לממש טבלת דפים באמצעות מבני נתונים שונים: מערך פשוט, עצים, טבלאות גיבוב (hash tables), ...עבור כל דף במרחב הזיכרון הווירטואלי של התהליך, יש כניסה בטבלת הדפים אשר מציינת:האם הדף נמצא בזיכרון ובאיזו מסגרת?האם הדף נמצא בדיסק ובאיזה מיקום?האם הדף מעולם לא הוקצה? (כלומר איננו בזיכרון ואיננו בדיסק)טבלת הדפים אחראית לתפקידים נוספים כמו הגנת גישה.למשל: טבלת הדפים מסמנת דפים לקריאה בלבד ומונעת גישות כתיבה.19מערכות הפעלה - תרגול 10

Tutorial 10slide 39סיכום: תהליך התרגום במעבדי אינטל39the CPU searches the TLBhitmissthe CPU walks the page tablecompletedpage faultphysi...

סיכום: תהליך התרגום במעבדי אינטל39the CPU searches the TLBhitmissthe CPU walks the page tablecompletedpage faultphysical addressvirtual addressthe OS serves the page faultמערכות הפעלה - תרגול 10

Ask Gemini
2018A_Winter_A · Q1 — 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.