This question spans 3 stages — each part below is tagged with, and links to, the stage it belongs to.
אברהם החליט להוסיף למערכת לינוקס שלו את היכולת להבחין בין תהליכים "רגילים" לתהליכים "מיוחדים". לשם כך הוסיף שדה is_special במתאר התהליך (PCB) אשר עובר בתורשה מאב לבניו. השדה is_special מאותחל לאפס (תהליך "רגיל") עבור התהליך init ולכן כל תהליך הוא "רגיל" כל עוד לא נאמר אחרת. בנוסף לכך, קריאת המערכת execv() אינה משנה את השדה is_special. אברהם הגדיר שתי קריאות מערכת חדשות לקריאה/כתיבה של השדה is_special: int is_special(); מחזירה 1 אם התהליך הקורא הוא "מיוחד", ו- 0 אם הוא "רגיל" int set_special(pid_t p); משנה את תהליך p להיות "מיוחד" (ללא קשר למצבו הקודם) כאשר p יכול להיות רק בן ישיר של התהליך הקורא. שימו לב: תהליך לא יכול להפוך את עצמו ל-"מיוחד". כאמור, התכונה "מיוחד" עוברת בתורשה מאב לבניו, ולכן כל הבנים של p שיווצרו *אחרי* קריאת מערכת זו יהיו "מיוחדים". ערכי חזרה אפשריים: 0 אם תהליך p הפך להיות "מיוחד" (ללא קשר למצבו הקודם) 1- אם הקריאה תיכשל (למשל, אם p אינו בן של התהליך הקורא) לאברהם יש תוכנית, הנקראת very_special, שאותה הוא רוצה להריץ כתהליך מיוחד. לשם כך אברהם כתב את התוכנית הבאה והריץ אותה מה- shell שהוא תהליך "רגיל" בעצמו: prog1.c ```c 1. #include <sys/types.h> 2. #include <unistd.h> 3. 4. int main() { 5. pid_t p = fork(); 6. if (p > 0) { // parent 7. set_special(p); 8. } else if (p == 0) { // child 9. execv("/usr/bin/very_special", ...); 10. } 11. return 0; 12. } ```
Full original question text (raw OCR)
מערכות הפעלה (234123) שאלה 2 - תהליכים (25 נק') אברהם החליט להוסיף למערכת לינוקס שלו את היכולת להבחין בין תהליכים "רגילים" לתהליכים "מיוחדים". לשם כך הוסיף שדה is_special במתאר התהליך (PCB) אשר עובר בתורשה מאב לבניו. השדה is_special מאותחל לאפס (תהליך "רגיל") עבור התהליך init ולכן כל תהליך הוא "רגיל" כל עוד לא נאמר אחרת. בנוסף לכך, קריאת int is_special(); int set_special(pid_t p); .is_special אינה משנה את השדה execv)( המערכת אברהם הגדיר שתי קריאות מערכת חדשות לקריאה/כתיבה של השדה is_special מחזירה 1 אם התהליך הקורא הוא "מיוחד", ו- 0 אם הוא "רגיל" משנה את תהליך p להיות "מיוחד" (ללא קשר למצבו הקודם) כאשר ק יכול להיות רק בן ישיר של התהליך הקורא. שימו לב: תהליך לא יכול להפוך את עצמו ל-"מיוחד". כאמור, התכונה "מיוחד" עוברת בתורשה מאב לבניו, ולכן כל הבנים של p שיווצרו *אחרי* קריאת מערכת זו יהיו "מיוחדים". ערכי חזרה אפשריים: 0 אם תהליך ק הפך להיות "מיוחד" (ללא קשר למצבו הקודם) 1- אם הקריאה תיכשל (למשל, אם p אינו בן של התהליך הקורא) לאברהם יש תוכנית, הנקראת very_special, שאותה הוא רוצה להריץ כתהליך מיוחד. לשם כך אברהם כתב את התוכנית הבאה והריץ אותה מה- shell שהוא תהליך "רגיל" בעצמו: prog1.c ```c 1. #include <sys/types.h> 2. #include <unistd.h> 3. 4. int main() { 5. pid_t p = fork(); 6. if (p > 0) { // parent 7. set_special(p); 8. } else if (p == 0) { // child 9. execv("/usr/bin/very_special", ...); 10. } 11. return 0; 12. } ``` 1. (2 נק') בקוד של אברהם יש בעיה: במקרים מסוימים התוכנית very_special רצה בתור תהליך רגיל למשך פרק זמן כלשהו. הסבירו באילו מקרים הבעיה מתרחשת ומדוע היא אינה בהכרח מתרחשת תמיד. נימוק: אברהם ניסה לתקן את הבעיה וכתב תוכנית חדשה: prog2.c ```c 1. #include <sys/types.h> 2. #include <unistd.h> 3. 4. int main() { pid_t p1 = fork(); if (p1 > 0) { // parent set_special(p1); } else if (p1 == 0) { // child pid_t p2 = fork(); if (p2 == 0) { // grand-child execv("/usr/bin/very_special", ...); } } return 0; } ``` 2. (3 נק') למרבה הצער, הבעיה רק החריפה: במקרים מסוימים התוכנית very_special רצה בתור תהליך רגיל *לנצח* (ולא רק למשך פרק זמן כלשהו). הסבירו באילו מקרים הבעיה מתרחשת ומדוע היא גורמת לתוכנית very_special להיתקע לנצח בתור תהליך רגיל. נימוק: אברהם ביקש מחבריו לקורס "מערכות הפעלה" לעזור לו לפתור את הבעיות שתוארו לעיל. לשמחתו הוא קיבל ארבע הצעות לתיקון. בכל אחד מהסעיפים הבאים מציגים הצעה לפתרון הבעיה על ידי הוספת שורות קוד לתוכנית המקורית prog1 (לנוחותכם, השורות שהתווספו מודגשות). הצעה ראשונה לתיקון הבעיה: prog3.c ```c 1. #include <sys/types.h> 2. #include <unistd.h> 3. 4. int main() { 5. pid_t p = fork(); 6. if (p > 0) { // parent 7. set_special(p); 8. } else if (p == 0) { // child 9. If (is_special()) 10. execv("/usr/bin/very_special", ...); 11. } 12. return 0; 13. } ``` 3. (5 נק') האם שינוי זה פותר את הבעיה? כן / לא נימוק: אם לא, האם אתם יכולים להציע שינוי בשורת קוד אחת ויחידה שהתווספה כדי לתקן את הבעיה? כן / לא אם כן, נא לציין איזה שורה ואיך לתקן אותה: מספר שורה התיקון (שורת קוד החדשה במקום השורה הקיימת) הצעה שניה לתיקון הבעיה: prog4 ```c 1. // includes all necessary headers... 2. int main() { 3. // declare and initialize semaphore with initial value=0 4. pid_t p = fork(); 5. if (p > 0) { // parent 6. set_special(p); 7. sem_post(&sem); 8. } else if (p == 0) { // child 9. sem_wait(&sem); 10. execv("/usr/bin/very_special", ...); 11. } 12. // destroy the semaphore 13. return 0; 14. } ``` 4. (5 נק') האם שינוי זה פותר את הבעיה? כן / לא נימוק: אם לא, האם אתם יכולים להציע שינוי בשורת קוד אחת ויחידה שהתווספה כדי לתקן את הבעיה? כן / לא אם כן, נא לציין איזה שורה ואיך לתקן אותה: מספר שורה התיקון (שורת קוד החדשה במקום השורה הקיימת) הצעה שלישית לתיקון הבעיה: prog5 ```c 1. // includes all necessary headers... 2. 3. int main() { 4. int fd[2]; 5. pipe(fd); 6. pid_t p = fork(); 7. if (p > 0) { // parent 8. set_special(p); 9. while (write(fd[1], "#", 1) != 1); 10. } else if (p == 0) { // child 11. char c; 12. while (read(fd[0], &c, 1) != 1); 13. execv("/usr/bin/very_special", ...); 14. } 15. return 0; 16. } ``` 5. (5 נק') האם שינוי זה פותר את הבעיה? כן / לא נימוק: אם לא, האם אתם יכולים להציע שינוי בשורת קוד אחת ויחידה שהתווספה כדי לתקן את הבעיה? כן / לא אם כן, נא לציין איזה שורה ואיך לתקן אותה: מספר שורה התיקון (שורת קוד החדשה במקום השורה הקיימת) הצעה רביעית לתיקון הבעיה: prog6 ```c 1. // includes all necessary headers... 2. int main() { 3. pid_t p = fork(); 4. if (p > 0) { // parent 5. set_special(p); 6. kill(getpid(), SIGCONT); 7. } else if (p == 0) { // child 8. kill(getpid(), SIGSTOP); 9. execv("/usr/bin/very_special", ...); 10. } 11. return 0; 12. } ``` 6. (5 נק') האם שינוי זה פותר את הבעיה? כן / לא נימוק: אם לא, האם אתם יכולים להציע שינוי בשורת קוד אחת ויחידה שהתווספה כדי לתקן את הבעיה? כן / לא אם כן, נא לציין איזה שורה ואיך לתקן אותה: מספר שורה התיקון (שורת קוד החדשה במקום השורה הקיימת)
(2 נק') בקוד של אברהם יש בעיה: במקרים מסוימים התוכנית very_special רצה בתור תהליך רגיל למשך פרק זמן כלשהו. הסבירו באילו מקרים הבעיה מתרחשת ומדוע היא אינה בהכרח מתרחשת תמיד. נימוק:
System call trap and kernel entryfork/exec address-space semanticslibc syscall wrapper caching pitfallsאברהם ניסה לתקן את הבעיה וכתב תוכנית חדשה: prog2.c ```c 1. #include <sys/types.h> 2. #include <unistd.h> 3. 4. int main() { 5. pid_t p1 = fork(); 6. if (p1 > 0) { // parent 7. set_special(p1); 8. } else if (p1 == 0) { // child 9. pid_t p2 = fork(); 10. if (p2 == 0) { // grand-child 11. execv("/usr/bin/very_special", ...); 12. } 13. } 14. return 0; 15. } ``` (3 נק') למרבה הצער, הבעיה רק החריפה: במקרים מסוימים התוכנית very_special רצה בתור תהליך רגיל *לנצח* (ולא רק למשך פרק זמן כלשהו). הסבירו באילו מקרים הבעיה מתרחשת ומדוע היא גורמת לתוכנית very_special להיתקע לנצח בתור תהליך רגיל. נימוק:
fork/exec address-space semanticslibc syscall wrapper caching pitfallsאברהם ביקש מחבריו לקורס "מערכות הפעלה" לעזור לו לפתור את הבעיות שתוארו לעיל. לשמחתו הוא קיבל ארבע הצעות לתיקון. בכל אחד מהסעיפים הבאים מציגים הצעה לפתרון הבעיה על ידי הוספת שורות קוד לתוכנית המקורית prog1 (לנוחותכם, השורות שהתווספו מודגשות). הצעה ראשונה לתיקון הבעיה: prog3.c ```c 1. #include <sys/types.h> 2. #include <unistd.h> 3. 4. int main() { 5. pid_t p = fork(); 6. if (p > 0) { // parent 7. set_special(p); 8. } else if (p == 0) { // child 9. If (is_special()) 10. execv("/usr/bin/very_special", ...); 11. } 12. return 0; 13. } ``` (5 נק') האם שינוי זה פותר את הבעיה? כן / לא נימוק: אם לא, האם אתם יכולים להציע שינוי בשורת קוד אחת ויחידה שהתווספה כדי לתקן את הבעיה? כן / לא אם כן, נא לציין איזה שורה ואיך לתקן אותה: מספר שורה התיקון (שורת קוד החדשה במקום השורה הקיימת)
fork/exec address-space semanticslibc syscall wrapper caching pitfallsהצעה שניה לתיקון הבעיה: prog4 ```c 1. // includes all necessary headers... 2. int main() { 3. // declare and initialize semaphore with initial value=0 4. pid_t p = fork(); 5. if (p > 0) { // parent 6. set_special(p); 7. sem_post(&sem); 8. } else if (p == 0) { // child 9. sem_wait(&sem); 10. execv("/usr/bin/very_special", ...); 11. } 12. // destroy the semaphore 13. return 0; 14. } ``` (5 נק') האם שינוי זה פותר את הבעיה? כן / לא נימוק: אם לא, האם אתם יכולים להציע שינוי בשורת קוד אחת ויחידה שהתווספה כדי לתקן את הבעיה? כן / לא אם כן, נא לציין איזה שורה ואיך לתקן אותה: מספר שורה התיקון (שורת קוד החדשה במקום השורה הקיימת)
fork/exec address-space semanticslibc syscall wrapper caching pitfallsSemaphore blocking and busy-waitהצעה שלישית לתיקון הבעיה: prog5 ```c 1. // includes all necessary headers... 2. 3. int main() { 4. int fd[2]; 5. pipe(fd); 6. pid_t p = fork(); 7. if (p > 0) { // parent 8. set_special(p); 9. while (write(fd[1], "#", 1) != 1); 10. } else if (p == 0) { // child 11. char c; 12. while (read(fd[0], &c, 1) != 1); 13. execv("/usr/bin/very_special", ...); 14. } 15. return 0; 16. } ``` (5 נק') האם שינוי זה פותר את הבעיה? כן / לא נימוק: אם לא, האם אתם יכולים להציע שינוי בשורת קוד אחת ויחידה שהתווספה כדי לתקן את הבעיה? כן / לא אם כן, נא לציין איזה שורה ואיך לתקן אותה: מספר שורה התיקון (שורת קוד החדשה במקום השורה הקיימת)
fork/exec address-space semanticslibc syscall wrapper caching pitfallsPipe IPC semanticsHard link vs symlink inode behaviorהצעה רביעית לתיקון הבעיה: prog6 ```c 1. // includes all necessary headers... 2. int main() { 3. pid_t p = fork(); 4. if (p > 0) { // parent 5. set_special(p); 6. kill(getpid(), SIGCONT); 7. } else if (p == 0) { // child 8. kill(getpid(), SIGSTOP); 9. execv("/usr/bin/very_special", ...); 10. } 11. return 0; 12. } ``` (5 נק') האם שינוי זה פותר את הבעיה? כן / לא נימוק: אם לא, האם אתם יכולים להציע שינוי בשורת קוד אחת ויחידה שהתווספה כדי לתקן את הבעיה? כן / לא אם כן, נא לציין איזה שורה ואיך לתקן אותה: מספר שורה התיקון (שורת קוד החדשה במקום השורה הקיימת)
System call trap and kernel entryfork/exec address-space semanticslibc syscall wrapper caching pitfalls
The exam question — original PDF
pages 4, 5, 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.
inodes & *stat syscalls • lstat(2) – Exactly the same as stat(2) if applied to a hard link – But if applied to a symlink, would return the information of this symlink (not to the target of the symlink) – In this case, POSIX says that the only fields within the stat structure that you can portably use are: • st_mode which will specify that the file is a symlink • st_size symlink content length (= length of target filepath) – The value of the rest of the fields could be valid, but it is not specified by POSIX – Notably, it is not specified if a symlink has a corresponding inode • Will be discussed shortly OS (234123) - files 34
אחרי 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
קריאות המערכת getpid(), getppid()pid_t getpid();קריאת מערכת המחזירה לתהליך הקורא את ה-pid של עצמו.pid_t getppid();קריאת מערכת המחזירה את ה-PID של תהליך האב של התהליך הקורא.שאלה: מה המשמעות של getppid() == 1 עבור תהליך משתמש טיפוסי?תשובה: תהליך האב הוא init. קורה למשל אם תהליך הבן יתום.מערכות הפעלה - תרגול 222
קריאת המערכת 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
העברת סיגנלים בשני שלביםרישום – מערכת ההפעלה רושמת ב-PCB של תהליך היעד שיש לו סיגנל ממתין (pending signal).הרישום מתבצע במערך בינארי בין 31 ביטים, ולכן לכל תהליך יכול להיות לכל היותר סיגנל ממתין אחד מכל מספר.טיפול – בכל פעם שהתהליך חוזר ממצב גרעין למצב משתמש, מערכת ההפעלה בודקת אם יש סיגנלים ממתינים ומטפלת בהם.בסיום הטיפול בסיגנל, מערכת ההפעלה תאפס את הביט המתאים במערך. במידה ויש מספר סיגנלים ממתינים, סדר הטיפול מתחילת המערך לסופו.מערכות הפעלה - תרגול 39
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
יצירת חוט חדשפרמטרים:thread – מצביע למקום בו יאוחסן מזהה החוט החדש במקרה של סיום הפונקציה בהצלחה.attr – מאפיינים המתארים את תכונות החוט החדש, כגון האם החוט הוא חוט גרעין או חוט משתמש, האם ניתן לבצע לו join, כלומר להמתין לסיומו, וכו'. בד"כ נספק ערך NULL המציין חוט ברירת המחדל של המערכת, שניתן להמתין לסיומו.void* (*start_routine)(void*) מצביע לפונקציה שתהווה את קוד החוט. הערך המוחזר מפונקציה זו במקרה של סיומה הטבעי הינו ערך הסיום של החוט.arg – פרמטר שיסופק לפונקציה עם הפעלתה.מערכות הפעלה - תרגול 714
TL;DRבתרגול הקודם למדנו לכתוב קוד מקבילי באמצעות חוטים.ראינו שבכל בעיה לא טריוויאלית יש צורך בסנכרון בין החוטים.היום נלמד על מנגנוני סנכרון נוספים של ממשק pthreads :לבסוף, נלמד דוגמה נוספת של קוד מקבילי חשוב: גרעין לינוקס.קוד הגרעין לא משתמש בחוטים, אבל ניגש לזיכרון משותף מתוך מספר מסלולי בקרה שרצים במקביל, ולכן העקרונות שלמדנו תקפים גם עבורו.2מערכות הפעלה - תרגול 8להבטחת סדרלהבטחת אטומיותמשתני תנאי(condition variables)מנעולים(mutexes)סמפורים (semaphores)
שחרור חוטים ממתיניםint pthread_cond_signal(pthread_cond_t *cond); משחררת את אחד החוטים הממתינים (הגינות לא מובטחת).int pthread_cond_broadcast(pthread_cond_t *cond);משחררת את כל החוטים הממתינים.כל החוטים מפסיקים להמתין על משתנה התנאי ועוברים להמתין על המנעול. החוטים יחזרו לפעילות בזה אחר זה (בסדר כלשהו, לאו דווקא הוגן) לאחר שינעלו מחדש את ה-mutex.שימו לב: אם אין אף חוט שממתין באותו רגע על משתנה התנאי cond, הפעולות חסרות השפעה (הסיגנל הולך לאיבוד ואינו נזכר הלאה).ערך מוחזר: הפונקציות תמיד מצליחות ומחזירות 0.מערכות הפעלה - תרגול 812
מועד א', אביב 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
מועד א', אביב 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
בלינוקס יש שני סוגי קישורים (links)soft / symbolic linkln -s src dstקישור סימבולי הוא קובץ חדש עם inode נפרד מזה של הקובץ המקורי.כתיבה דרך הקישור כותבת לקובץ אליו הוא מצביע.מחיקת הקישור (באמצעות הפקודה rm) לא תמחק את הקובץ המוצבע.אפשר ליצור קישורים סימבוליים גם לקובץ שלא קיים.hard linkln src dstקישור קשיח הוא שם נרדף לקובץ המקורי כי הוא מצביע ישירות ל-inode של הקובץ המקורי.כתיבה דרך הקישור כותבת לקובץ אליו הוא מצביע.מחיקת הקישור תקטין את מונה הקישורים של הקובץ (כפי שנשמר ב-inode).הקובץ יימחק מהדיסק רק כאשר כל ה-hard links אליו יימחקו.מערכות הפעלה - תרגול 1213
>> rm /A/helloמערכות הפעלה - תרגול 1219inode #2type=dirdatanameinode #A5B7inode #5type=dirdatanameinode #…………inode #13type=soft_linkdatainode #7type=dirdatanameinode #soft13……data block/A/helloקישור "שבור"!dangling link
The exam text, the skills it tests, and the exact slides are already in context.