430 likes | 712 Views
מערכות הפעלה. תרגול 6 – מימוש סינכרון תהליכים. מה בתכנית?. מימוש סנכרון תהליכים ב- XINU Mutexes. אמצעי תקשורת וסינכרון. סמפור wait(s)/signal(s) שליחת הודעות send(pid, msg)/receive() mutex (מניעה הדדית). מימוש סמפורים. מצב התהליך – PRWAIT
E N D
מערכות הפעלה תרגול 6 – מימוש סינכרון תהליכים
מה בתכנית? • מימוש סנכרון תהליכים ב-XINU • Mutexes מערכות הפעלה - תרגול 2
אמצעי תקשורת וסינכרון • סמפור • wait(s)/signal(s) • שליחת הודעות • send(pid, msg)/receive() • mutex (מניעה הדדית) מערכות הפעלה - תרגול 2
מימוש סמפורים • מצב התהליך – PRWAIT התהליך המחכה נמצא במצב PRWAIT המיוחד לסמפורים • תור לכל סמפור לכל סמפור נשמר תור FIFO של התהליכים המחכים לו, כך שsignal- יוכל לשחרר את הראשון שבהם, ולהעבירו לתור ה-ready • טבלת סמפורים semaph עם כניסות מסוג sentry • כל כניסה מכילה מונה, מצביעים לתור (בטבלת q) ודגל הכניסה (SFREE/SUSED) • ערך מזהה הסמפור sid הוא האינדקס של כניסתו בטבלת הסמפורים מערכות הפעלה - תרגול 2
מימוש סמפורים – sem.h מערכות הפעלה - תרגול 2
קריאת מ"ה wait – wait.c מערכות הפעלה - תרגול 2
קריאת מ"ה wait • ביצוע wait לא מופרע באמצע ע"י מערכת ההפעלה. הגנה באמצעות disable/restore. אולם החלפת הקשר היא אפשרית גם באמצע עקב reschedיזום • לא ניתן לבצע wait על סמפור שלא נוצר, או שכבר שוחרר • הכנסה לסוף תור הסמפור (כלומר FIFO) • הקריאה לresched- הכרחית מערכות הפעלה - תרגול 2
קריאת מ"ה signal – signal.c מערכות הפעלה - תרגול 2
קריאת מ"ה signal • ביצוע signalלא מופרע באמצע ע"י מערכת ההפעלה. הגנה באמצעות disable/restore. אולם החלפת הקשר היא אפשרית גם באמצע עקב reschedיזום • לא ניתן לבצע signal על סמפור שלא נוצר, או שכבר שוחרר • ההוצאה מראש תור הסמפורים (FIFO) • האם הקריאה ל-resched הכרחית כפי שהיתה הכרחית ב-wait? מערכות הפעלה - תרגול 2
קריאת מ"ה screate – screate.c לא ניתן ליצור סמפור עם ערך התחלתי שלילי. מה המשמעות של ערך שלילי? מערכות הפעלה - תרגול 2
קריאת מ"ה screate • ביצוע signalלא מופרע באמצע ע"י מערכת ההפעלה. הגנה באמצעות disable/restore. אולם החלפת הקשר היא אפשרית גם באמצע עקב reschedיזום • לא ניתן לבצע signal על סמפור שלא נוצר, או שכבר שוחרר • ההוצאה מראש תור הסמפורים (FIFO) • האם הקריאה ל-resched הכרחית כפי שהיתה הכרחית ב-wait? מערכות הפעלה - תרגול 2
קריאת מ"ה screate – המשך בעת אתחול המערכת הוצב קבוע SFREE לכל רשומות הסמפורים מערכות הפעלה - תרגול 2
קריאת מ"ה sdelete מערכות הפעלה - תרגול 2
קריאת מ"ה sdelete • אם, בעת מחיקת הסמפור, קיימים תהליכים המחכים לו, משחררים את כל התהליכים • בסוף לולאתwhile מבצעים resched. האם מותר גם בתוך הלולאה? • לשים לב:מחיקת סמפור כאשר יש תהליכים המחקים בתור היא פעולה מסוכנת ולא טבעית (הרי יש סיבה שבגללה הם מחכים לסמפור...) • ישנן מערכות הפעלה בהן לא ניתן לשחרר סמפור שתורו לא ריק מערכות הפעלה - תרגול 2
signaln.c ready() מרכזת את כל הטיפול בתור התהליכים המוכנים מערכות הפעלה - תרגול 2
sreset.c מה ההבדל בין קריאה ל-sreset לבין שתי קריאות רצופות ל-sdelete ו-screate? מערכות הפעלה - תרגול 2
העברת הודעות - יישום • משלוח הודעות – לא חוסם • קבלת הודעות – חוסמת או לא חוסמת • בקבלה חוסמת, אם לא ממתינה הודעה, מחכים • השולח משאיר את ההודעה בטבלת התהליכים, בשדות המיועדים לכך • כאשר שולח מבקש לשלוח הודעה לתהליך שעדיין לא קרא הודעה קודמת, ההודעה החדשה אינה נשלחת • אם השולח מזהה שהתהליך המחכה נמצא במצב מחכה להודעה, השולח מחזיר את התהליך המקבל לריצה * תהליך המקבל הודעה אינו יודע מי השולח מערכות הפעלה - תרגול 2
הודעות ב-XINU XINU מוגבלת מאוד באמצעי העברת ההודעות שלה. סוגי העברה: • משלוח לא חוסם (send למיניהם) • קבלה חוסמת ולא חוסמת (receiveclr, receive) • שליחת ההודעות לתהליך מסויים מערכות הפעלה - תרגול 2
הודעות ב-XINU • באמצעות העברת ההודעות ב-XINU ניתן: • להעביר מסרים • לתאם בין תהליכים (בנוסף לסמפורים) • הגבלות: • הודעה באורך של שני בתים בלבד (integer) • כשמגיעים מספר מסרים, רק הראשון מתקבל והשאר אובדים מערכות הפעלה - תרגול 2
העברת הודעות – מבנה נתונים • מצב התהליך – PRRECV, תהליך מחכה נמצא במצב PRRECV (מיוחד להמתנה להודעות) • שדות pmsgו-phasmsg בטבלת התהליכים • ההודעה הנשלחת לתהליך מוכנסת לשדה pmsg ברשומת התהליך • השדה phasmsg מסמן אם יש הודעה ממתינה מערכות הפעלה - תרגול 2
קריאת מ"ה send – send.c מערכות הפעלה - תרגול 2
קריאת מערכת הפעלה – send.c • send מתבצעת בצורה רצופה מבלי שתופרע ע"י מערכת ההפעלה • אולם, החלפת ההקשר יכולה לקרות ע"י reschedיזום • בדיקות תקינות כוללות בדיקה שאין כבר הודעה המחכה עבור התהליך המקבל • אם התהליך מחכה מעירים אותו ומבצעים resched מערכות הפעלה - תרגול 2
קריאת מ"ה receive – receive.c מערכות הפעלה - תרגול 2
קריאת מ"ה receive– receive.c • receive מתבצעת בצורה רצופה מבלי שתופרע ע"י מערכת ההפעלה • אולם, החלפת ההקשר יכולה לקרות ע"י reschedיזום • אם אין הודעה התהליך עובר למצב PRRECV ויוצא מהמעבד אחרי resched • האם הוא נמצא בתור כלשהו? איך יחזור לפעולה? • למה שומרים הודעה במשתנה לוקלי msg? מערכות הפעלה - תרגול 2
קריאת מ"ה receiveclr – receiveclr.c מערכות הפעלה - תרגול 2
מניעה הדדית • המטרה היא למנוע מצב שבוא שני תהליכים יגשו לאותו משאב בו זמנית • התסריט הלא רצוי הוא שתהליך אחד יתחיל לבצע פעולה על המשאב ואז יופסק ע"י פסיקה מבלי שיספיק לבצע את השינוי • במקרה כזה, תהליך שני יקבל את המשאב במצב לא תקין מערכות הפעלה - תרגול 2
פתרון למניעה הדדית ברמת המ"ה • ניתן לכבות את מנגנון הפסיקות (כיבוי IF) • במערכות המודרניות משתמשים בשיטה זו: • בקוד הפנימי של מערכת הפעלה • תוכניות קלט/פלט (כגון קריאה של USB או רשת) • הפתרון חסום ברמת האפליקציה מערכות הפעלה - תרגול 2
מניעה הדדית באמצעות mutex • ראינו מימוש אפשרי של מניעה הדדית באמצעות הסמפורים • בדרך כלל הדבר נעשה ע"י מימוש מנגנון נעילות הקרוי mutex • מנגנון הנעילה מזוהה עם משאב משותף שעבורו רוצים למנוע גישה בו-זמנית • מנגנון הנעילות לא מבטיח שתהליך שנכנס לקטע קריטי לא ייעצר עד שיצא ממנו. הוא מבטיח שתהליכים אחרים לא יכנסו לקטע קריטי נעול מערכות הפעלה - תרגול 2
מניעה הדדית באמצעות mutex • אם קטע קריטי נעול על ידי תהליך מסויים כל היתר לא יצליחו לעבור מנעול ויעצרו בכניסה לקטע קריטי • המנגנון לא מסוגל לכפות את עצמו על אף תהליך • התהליך חייב להיות מתוכנת לגשת לקטע קריטי דרך מנגנון הנעילות מערכות הפעלה - תרגול 2
הפעלת mutex • שני מצבים – "פתוח" ו-"נעול" • התהליך שנכנס לקטע קריטי "נועל" (lock) את mutex, מבצע קטע קריטי ו"משחרר" (unlock)את mutex ביציאה • אם תהליך מנסה לנעול מנגנון נעילות נעול, הוא נעצר ומצטרף לתור הממתינים למנגנון הנעילות מערכות הפעלה - תרגול 2
קריאות mutex נעילה: mutex_lock(&global_variable); שחרור: mutex_unlock(&global_variable); דוגמה: MUTEX_VAR mtx = MUTEX_VAR_INITIALIZER; mutex_lock(&mtx); critical_variable++; mutex_unlock(&mtx); מערכות הפעלה - תרגול 2
מימוש מנגנון הנעילות • ניתן לממש את מנגנון הנעילות בעזרת סמפור • ניתן להשתמש בסמפור יחיד שהערך ההתחלתי של המונה שלו הוא 1. • נקצה עבור כל מנגנון הנעילות רשומה המכילה: • סטטוס של מנגנון – סגור או פתוח • מספר הסמפור שהוקצה • שדה המציין את הבעלים הזמניים של מנגנון מערכות הפעלה - תרגול 2
הגדרת הרשומה – mutex.h מערכות הפעלה - תרגול 2
mutex.c SYSCALL mutex_lock(MUTEX_REC_PTR *mutex_var) { int nsem; int ps, ans; disable(ps); if ((*mutex_var) == (MUTEX_REC_PTR)NULL) { nsem = screate(1); if (nsem == SYSERR) { restore(ps); return SYSERR; }; (*mutex_var) = (MUTEX_REC_PTR) getmem(sizeof(MUTEX_REC)); (*mutex_var)->nsem = nsem; } ans = wait((*mutex_var)->nsem); (*mutex_var)->pid = currpid; restore(ps); return ans; } מערכות הפעלה - תרגול 2
mutex_lock • בנעילה ראשונה יוצרים רשומת mutex • מקצים סמפור עבור המנגנון ונשמוראת ה-sid שלו ברשומה • מחכים בתור לכניסה למנגנון הנעילה • מעדכנים את הפרטים של הבעלים הנוכחיים של mutex • משחזרים פסיקות. בפועל זה קורה פעמיים – בתוך screate וגם בתוך הקריאה עצמה מערכות הפעלה - תרגול 2
mutex.c SYSCALL mutex_unlock(MUTEX_REC_PTR *mutex_var) { int ps; disable(ps); if ( ((*mutex_var) == (MUTEX_REC_PTR)NULL) || ((*mutex_var)->pid != currpid) ) return SYSERR; if ( isempty(semaph[(*mutex_var)->nsem].sqhead)) { sdelete((*mutex_var)->nsem); freemem((*mutex_var), sizeof(MUTEX_REC)); (*mutex_var) = MUTEX_VAR_INITIALIZER; restore(ps); return OK; } /* if */ signal((*mutex_var)->nsem); restore(ps); return OK; } /* mutex_unlock */ מערכות הפעלה - תרגול 2
mutex_unlock • אם התור הוא ריק הרשומה משתחררת והסמפור נמחק • אם יש תהליכים המחכים בתור נקראת signal כדי לשחרר את התהליך הבא • התהליך משחרר נעילה ויוצא מקטע קריטי • התהליך המשוחרר נמצא באמצע פעולת lock. הוא רושם את עצמו כבעלים החדשים של mutex ומבצע קטע קריטי מערכות הפעלה - תרגול 2
דוגמה:בנק • קיימים 2 חשבונות בנק • הכספים עוברים מחשבון לחשבון • אל המשתנים הגלובליים ניגשים שני תהליכים: • תהליך ראשון מעביר סכומים מחשבון לחשבון • תהליך שני מדפיס סה"כ כסף בשני החשבונות • הסה"כ צריך להיות קבוע. האם זה קורה באמת? מערכות הפעלה - תרגול 2
ממוש 1 – testmu1.c volatile int bank[2] = {450, 600}; xmain() { resume(create(bank1,INITSTK, INITPRIO, "proc 1", 0) ); resume(create(bank2,INITSTK, INITPRIO, "proc 2", 0) ); } int bank1() { int sums[] = {100, -50, 150, -200}; int i=0, j, sum; while(1) { i++; i = i % 4; sum = sums[i]; bank[0] += sum; for(j=0; j < 32000; j++) ; bank[1] -= sum; } } int bank2() { int sum; int i= 0; while(1) { i++; i = i % 30000; sum = bank[0] + bank[1]; if ((sum != 1050)|| (i == 0)) printf("sum = %d\n", sum); } } מערכות הפעלה - תרגול 2
testmu1.c • הסכום הלא מתאים מודפס בכל מקרה גם פעם ב-30000 מקרים אם הוא מתאים • המימוש לא מגן על המשאב המשותף עם מנגנון הנעילות • נראה גיוון מספרים בפלט מערכות הפעלה - תרגול 2
testmu1.c - פלט sum = 850 sum = 850 sum = 850 sum = 1200 sum = 1200 sum = 1200 sum = 1200 sum = 1150 sum = 1150 sum = 1150 sum = 1150 sum = 1000 sum = 1000 sum = 1000 sum = 850 sum = 850 sum = 850 sum = 850 הפלט מכיל מספרים שונים מערכות הפעלה - תרגול 2
ממוש 2 – testmu2.c volatile int bank[2] = {450, 600}; xmain() { resume(create(bank1,INITSTK, INITPRIO, "proc 1", 0) ); resume(create(bank2,INITSTK, INITPRIO, "proc 2", 0) ); } int bank1() { int sums[] = {100, -50, 150, -200}; int i=0, j, sum; while(1) { i++; i = i % 4; sum = sums[i]; mutex_lock(&mutex1); bank[0] += sum; bank[1] -= sum; mutex_unlock(&mutex1); } } int bank2() { int sum; int i= 0; while(1) { i++; i = i % 30000; mutex_lock(&mutex1); sum = bank[0] + bank[1]; mutex_unlock(&mutex1); if ((sum != 1050)|| (i == 0)) printf("sum = %d\n", sum); } } מערכות הפעלה - תרגול 2