שבוע #01 פרקProcess Synchronization : סינכרון בין תהליכים קורס מערכות הפעלה א' מכללת הדסה /מכללה חרדית צבי מלמד [email protected] הרצאות הקורס מבוססות במידה רבה ביותר על ההרצאות של ד"ר יורם ביברמן © כל הזכויות שמורות לד"ר יורם ביברמן ולצבי מלמד ©צבי מלמד 1 a cooperating process • תהליך משתף-פעולה )(a cooperating process – תהליך שעשוי להשפיע או להיות מושפע מתהליכים אחרים במערכת. – עשוי לחלוק מרחב כתובות )בפרט נתונים( עם אחרים – עשוי להעביר מידע דרך ערוצי תקשורת עם עמיתים. • בפרק זה ,כשנדבר על תהליך אנו נתכוון גם לפתיל – שיתוף נתונים עם פתילים אחרים רלבנטי במיוחד לגבי פתילים ©צבי מלמד 2 רקע Background • תיפעול זוג תהליכים :יצרן וצרכן – מעבירים זה לזה הודעות – באמצעות זיכרון משותף המנוהל כתור – מימוש במערך מעגלי בגודל BUFFER_SIZEתאים • אופן הפעולה: – היצרן מוסיף איברים לתור ,והצרכן גורע. • משתני עזר: – מונה ) (counterהמציין כמה איברים מצויים בתור בכל נקודת זמן .היצרן מגדיל את המונה ,הצרכן מקטין. – המשתנה inמציין לאן מכניס היצרן את האיבר הבא, – המשתנה outיציין מהיכן יצרוך הצרכן את האיבר הבא. – in, outמאותחלים לאפס. ©צבי מלמד 3 מבוא )דוגמא( – קוד היצרן • תיאום: – כאשר התור מלא על היצרן להמתין ,כאשר התור ריק על הצרכן להמתין. • קוד היצרן: { )while (1 while (counter == BUFFER_SIZE) ; // busy wait ; buffer[in] = new_item ; in = (in +1) % BUFFER_SIZE ; counter++ } ©צבי מלמד 4 מבוא )דוגמא( – קוד הצרכן :• קוד הצרכן סימטרי while (1) { while (counter == 0) ; //busy wait new_item = buffer[out] ; out = (out +1) % BUFFER_SIZE ; counter-- ; } 5 ©צבי מלמד דוגמת מבוא – תיזמון אפשרי ותוצאתו • עתה נניח שהתור מכיל 17פריטים. היצרן והצרכן פונים אליו במקביל, בפרט מתפעלים במקביל את :counter האחד מגדיל את ערכו ,השני מקטינו. • נניח שמערכת ההפעלה מתזמנת את הפעולות באופן הבא )תוך שהמעבד מבצע לסירוגין כל תהליך( התוצאה :ערכו של המונה הוא 16 )ולא 17כפי שראוי(. הסיבה :שני התהליכים פנו למשתנה המשותף במקביל ,ותפעלו אותו באופן לא מתואם\מסונכרן צרכן יצרן reg1 = counter )(reg117 reg2 = counter )(reg217 reg1++ )(reg118 reg2-)(reg216 counter = reg1 )(counter18 counter = reg2 )(counter16 ©צבי מלמד 6 מבוא )המשך( • תקלה דומה אך שונה: – עדכונו של המשתנה )על-פי ערכו של אוגר מתאים( ע"י תהליך א' לוקח יותר ממחזור זיכרון יחיד – בין תחילת העדכון לסיומו מתעניין תהליך ב' בערכו של המשתנה ,ועל כן מקבל ערך שגוי. ©צבי מלמד 7 מצבי מירוץ Race Condition - • מצבי מרוץ ) :(race conditionמצב שבו התוצאה תלויה בתזמון המקרי בו המחשב הריץ את התהליכים )ולכן התוצאה עלולה להיות שגויה(. • הפתרון :מנגנון שיאפשר רק לתהליך יחיד לפנות למשתנה counter בפרק זמן כלשהו. • הנושא הזה באופן כללי נקרא: תיאום בין תהליכים או process synchronization ©צבי מלמד 8 מנעול lock • • • פתרון אפשרי לבעיה :מנעול ).(lock – רק תהליך יחיד יכול לנעול את המנעול בכל יחידת זמן. – על המנעול מוגדרות פעולות אטומיות של נעילה ושחרור נעילה\רכישה ):(lock/acquire – תהליך המבצע פעולה זו נחסם עד שהמנעול מתפנה ,ואז הוא יכול להתקדם – מובטח שהוא התהליך היחיד שרכש את המנעול )ועל כן היחיד שפונה למשתנה( פתיחה\שחרור ):(unlock/release – שחרור המנעול ,כך שתהליך אחר יוכל לרכוש )לנעול ( אותו. – הטיפול במשתנים המשותפים ,אליהם נרצה לאפשר גישה בלבדית ) (exclusiveיחייב ראשית רכישת המנעול. – בתום הטיפול במשתנים ישוחרר המנעול. ©צבי מלמד 9 מנעול lock • עבודה עם המנעול באופן הזה תבטיח שלא ייווצר מצב מרוץ. • רעיון אפשרי: – נגדיר משתנה בולאני שיהווה את המנעול. – המשתנה עשוי להיות פתוח או סגור )(true or false ©צבי מלמד 10 בעיה ביישום פתרון המנעול • בכדי לרכוש את המנעול מבצע התהליך שתי פעולות: תהליך ב' קרא את מצב המנעול – )א( בדיקת ערכו של המנעול – )ב( עדכון ערכו קרא את מצב המנעול – יש חשש למצב הבא )נניח שהמנעול פתוח(: – ולא השגנו בלבדיות בפניה למשתנים המשותפים. תהליך א' אם ערכו פתוח אזי עדכן את ערכו לסגור אם ערכו פתוח אזי עדכן את ערכו לסגור טפל במשתנים המשותפים טפל במשתנים המשותפים ©צבי מלמד 11 בעיה ביישום פתרון המנעול מסקנה: • מנעול הוא רעיון יפה ,אבל צריך דרך )יותר( מעודנת ,מתוחכמת לממשו מאשר משתנה בוליאני פשוט שערכו נבדק ובמידת האפשר מעודכן ©צבי מלמד 12 פתרונות לבעית מימוש המנעול • שלושה סוגי פתרונות לבעיה כיצד לממש מנעול ולהשיג בלבדיות בטיפול במשתנים: – פתרונות תכנה: • פרוטוקולים )=אוסף כללים( שיקבעו כיצד על התהליכים לנהוג ע"מ שתושג הבלבדיות. – פתרונות חומרה • מסייעים להשיג את התוצאה הרצויה. – פתרונות תכנה מרמה גבוה • מבני נתונים ופעולות מרמה גבוהה,שית ַ מכו ע"י הקומפיילר ,ויאפשרו להשיג את הבלבדיות בנוחות • מימוש הפעולות על ידי הקומפיילר) ,בד"כ תוך שימוש במנגנוני חומרה( ©צבי מלמד 13 בעיית הקטע הקריטי The Critical-Section Problem • • • נניח מערכת בת nתהליכים .לכל אחד מהתהליכים יש קטע קריטי שבו הוא פונה למשתנים או קבצים משותפים ועליו לעשות זאת באופן בלבדי. משמעות המונח קטע קריטי: – לכל אחד מ n-התהליכים יש "קטע קריטי" .זהו קטע שבו הם ניגשים למשאבים משותפים )למשל משתנים או קבצים(. – בגלל בעיות – כגון בעיות מירוץ ,אסור שיותר מתהליך אחד ימצא בקטע הקריטי שלו בו זמנית. – כלומר ,לכל היותר אחד מתוך nהתהליכים מבצע בכל נקודת זמן נתונה את הקטע הקריטי שלו – התהליכים האחרים יבצעו וימצאו באזורים אחרים של התוכנית .כלומר או שהם ימתינו להכנס לקטע הקריטי או שהם יבצעו את "מה שנשאר" – קטע שיורי )(remainder section בעיית הקטע הקריטי: – מה יהיה הפרוטוקול )כללי ההתנהגות( באמצעותו נממש קטע קריטי. ©צבי מלמד 14 בעיית הקטע הקריטי The Critical-Section Problem • הפרוטוקול יכלול: • קטע כניסה )(entry section – סדרת פעולות אותן צריך לבצע תהליך המעוניין להיכנס לקטע הקריטי שלו. • קטע יציאה )(exit section – סדרת פעולות אותן יבצע התהליך בסיום ביצוע הקטע הקריטי שלו • קטע שיורי )(remainder section – קוד שאינו מטפל ב ַ משתנים המשותפים ©צבי מלמד 15 בעיית הקטע הקריטי The Critical-Section Problem 16 ©צבי מלמד שלוש הדרישות לבעיית הקטע הקריטי • בלבדיות ):(mutually exclusiveness – רק תהליך יחיד יוכל לבצע את הקטע הקריטי שלו בכל נקודת זמן. • התקדמות ):(progress – אם mתהליכים כלשהם מעוניינים להיכנס לקטע קריטי )כל תהליך לקטע קריטי של עצמו( ,ואף תהליך אינו מצוי בקטע קריטי .אזי תוך זמן סופי אחד המעוניינים יזכה להיכנס לקטע קריטי = אין קיפאון. • המתנה חסומה ) ,(bounded waitingאי הרעבה – יש חסם על מספר הפעמים שתהליכים אחרים רשאים להיכנס לקטע קריטי. שלהם אחרי שתהליך Pביקש להיכנס לקטע קריטי .שלו ,ועד שבקשתו מסופקת= הוגנות ©צבי מלמד 17 פתרונות תכנה לזוג תהליכים בלבד • Two-Processes Solutions • נניח זוג תהליכים P0, P1 :בלבד. • נניח -כי כל פעולת מכונה ,בפרט השמה ,היא אטומית – )כלומר מתבצעת בשלמותה בלא שניתן יהיה לראות 'תוצאת ביניים' של הפעולה(. – הנחה זאת איננה לגמרי מוצדקת • הערה :במחשבים מודרניים פעולת השמה/פקודת מחשב עשויה להימשך מספר מחזורי זיכרון ,ועל-כן עלול לקרות מצב בו :תהליך א' מתחיל לעדכן משתנה ,תהליך ב' קורא ערך לא תקין של המשתנה ,תהליך א' מסיים את עדכון ערך המשתנה ©צבי מלמד 18 פרוטוקול א' • התהליכים יחלקו משתנה משותףturn : – כאשר ערכו אפס רשאי P0להיכנס לקטע הקריטי – כאשר ערכו אחד ,רשאי P1להיכנס לקטע הקריטי • המשתנה יאותחל לערך כלשהו. { )while (1 while (turn != 0) ; // busy wait הקטע הקריטי ; turn = 1 הקוד של תהליך P0 הקטע השיורי } ©צבי מלמד 19 בדיקת תכונות פרוטוקול א' • נבדוק אילו משלוש הדרישות שהצגנו הפרוטוקול משיג: – בלבדיות מושגת. אינה בהכרח מושגת P0 :לא יוכל להיכנס – התקדמות לקטע הקריטי שלו שוב ,עד ש P1 -ייכנס לקטע הקריטי שלו. – המתנה חסומה מושגת :אם שני התהליכים רוצים להיכנס לקטע הקריטי אזי הם ייכנסו לסירוגין. ©צבי מלמד 20 הבעיה של פרוטוקול א' • פרוטוקול א' לא התעניין בשאלה :האם התהליך האחר בכלל מעוניין להיכנס לקטע הקריטי • ההנחה :הקטע הקריטי מבוצע לסירוגין • התור הועבר לתהליך P0או P1בכל מקרה • על כן עתה נציע שיפור ,נגדיר את המערך: • ; }bool want[2] = {false, false • want[i] == trueמשמע התהליך #iמעוניין להיכנס לק.ק. ©צבי מלמד 21 הבעיה של פרוטוקול א' • פרוטוקול א' לא התעניין בשאלה" :האם התהליך האחר בכלל מעוניין להיכנס לקטע הקריטי" • ההנחה :הקטע הקריטי מבוצע לסירוגין • התור הועבר לתהליך P0או P1בכל מקרה ©צבי מלמד 22 פרוטוקול ב' • נגדיר את המערך: ; }bool want[2] = {false, false • want[i] == trueמשמע התהליך #iמעוניין להיכנס לקטע הקריטי { )while (1 // I want to enter ; want[0] = true // I wait to my pal ;)]while (want[1 הקטע הקריטי ; want[0] = false הקוד של תהליך P0 הקטע השיורי } ©צבי מלמד 23 בדיקת תכונות פרוטוקול ב' • בלבדיות נשמרת :אם חברי רוצה להיכנס אני ממתין. • התקדמות :עלולה שלא להתקיים) .ראה הדוגמא( P0 P1 want[0]=true התוצאה/מסקנה: התהליכים בחסימה הדדית want[1]=true ;]while (want[1 מתקיים ;]while (want[0 מתקיים ©צבי מלמד 24 'פרוטוקול ג :משתנים bool want[2]={false, false}; int turn = 0; while (1) { want[0] = true ; turn = 1 ; // I want to enter as a gentlemen // I allow my pal 2 be the 1st while(want[1] && turn == 1) ; // I wait while it is his turn // AND he also wants to enter הקטע הקריטי want[0] = false ; P0 הקוד של תהליך הקטע השיורי { 25 ©צבי מלמד • פרוטוקול ג' – בדיקת בלבדיות +התקדמות • נניח ששני התהליכים מעוניינים להיכנס – שניהם מבצעיםwant[i] = true: – השני מביניהם שיבצע turn = other-process :יציב אחרון ערך ל turn -לפני לולאת ההמתנה ,וזה יהיה הערך שיוותר במשתנה – ולכן תהליך זה לא יכנס לקטע הקריטי – אבל משנהו כן ייכנס ,וייכנס לבד. – אם רק אחד מעוניין להיכנס )זאת אומרת שהשני נמצא בקטע השיורי( רואים שהוא יוכל להיכנס ,וברור שהוא נכנס לבד ©צבי מלמד 26 פרוטוקול ג' – בדיקת הוגנות • השני שהזין ערך למשתנה הוא האיטי יותר • עמיתו המהיר ייכנס לקטע הקריטי ,יצא ,יסמן שאינו מעוניין להיכנס ,ונניח שאף יספיק לסמן שהוא שוב מעוניין להיכנס. • מיד לאחר מכן ,הזריז יעניק את התור ל ַאיטי ,ובכך ייתקע את עצמו .עד מתי? • עד שהאיטי ייכנס ,יצא ,ויסמן שאינו מעוניין; • לחילופין ,אם האיטי פתאום הפך נורא זריז אזי הוא :יסמן שאינו מעוניין ,שהוא שוב מעוניין ,ושעתה תורו של חברו. • בקיצור :מי שיצא בהכרח מעניק התור למשנהו. ©צבי מלמד 27 • מצאנו פתרון לבעית הקטע הקריטי של שני תהליכים • בדקנו וראינו שהוא מקיים את שלושת הדרישות :בלבדיות, התקדמות ,ואי-חסימה/הוגנות פרוטוקול ג' -סיכום • משתנים: ;}bool want[2]={false, false ;int turn = 0 { )while (1 // I want to enter as a gentlemen // I allow my pal 2 be the 1st ; want[0] = true ; turn = 1 ; )while(want[1] && turn == 1 // I wait while it is his turn // AND he also wants to enter הקטע הקריטי ; want[0] = false הקטע השיורי הקוד של תהליך P0 { ©צבי מלמד 28 פתרונות תכנה לתהליכים רבים Multi Process Solutions • מעודדים מהצלחתינו למצוא פתרון תוכנה לבעית הקטע הקריטי לשני תהליכים – ניגש להתמודד עם האתגר הבא :פתרון תוכנה לאותה בעיה – אבל הפעם ,עבור תהליכים מרובים – nתהליכים • נכיר שני פתרונות: (1אלגוריתם )פרוטוקול( המאפיה (2מימוש מנעולים באמצעות תכנה ©צבי מלמד 29 אלג' המאפיה Bakery Algorithm • מקור השם :מדוכני מזון מהיר בהם מופעל האלגוריתם: – מי שמגיע מקבל מספר – המספר הבא בתור )פחות או יותר( ... – למה פחות או יותר? כי במציאות זה עובד בקלות ופשטות... אבל בתוכנה ,אני בודק מה המספר הכי גבוה עד כה ,ומוסיף אחד – זהו המספר שלי אבל במקביל אלי ,אולי עוד מישהו עשה תהליך כזה ,ואז לשנינו יש אותו מספר ©צבי מלמד 30 אלג' המאפיה Bakery Algorithm • האלגורתים מניח: – לכל תהליך קיים מזהה ייחודי בתחום .0..n-1 • האלג' עשוי להיות מורץ במערכת מקבילית )בת כמה מעבדים(. • סימונים: – ) (a, b) < (c, dעל-פי יחס הסדר הלקסיקוגרפי 'הרגיל'. – ) max(a0, … ,an-1מחזירה ערך הגדול או שווה מכל הai - ).(i=0, …, n-1 ©צבי מלמד 31 אלגוריתם המאפיה Bakery Algorithm • מבני נתונים: ; } bool choosing[n] = { false • -מציין האם התהליך נמצא כרגע בתהליך בחירת מספר לתור )לביצוע הקטע הקריטי( ; }int number[n] = {0 • -מערך של "המספר בתור" של כל תהליך ©צבי מלמד 32 המספר אינו בהכרח ייחודי ,בגלל שתהליך אחר במעבד )הזה או מעבד אחר( עשוי לבקש מספר במקביל 'אלי' אלגוריתם המאפיה { )while (1 אני כרגע בוחר מספר choosing[me] = true ; // = ]number[me ; max(number[0],...,number[n-1]) + 1 סיימתי לבחור choosing[me] = false ; // עבור "כל האחרים" אם מישהו כרגע בוחר מספר ,אני מחכה שיסיים לבחור כל עוד האחר רוצה להיכנס ,והוא בעדיפות על-פני אני ממתין { )for (other = 0; other < n; other++ ; )]while (choosing[other && while (number[other] != 0 < )(number[other], other ; ) )(number[me], me } הקטע הקריטי ; number[me] = 0 קטע שיורי } ©צבי מלמד 33 פתרון שני באמצעות תכנה • פתרון שני לבעיית התיאום )באמצעות תכנה(: – מבוסס על ההנחה שהתוכנית יכולה לחסום פסיקות ,ועל ידי כך להבטיח שהמעבד לא ייגזל ממנה בין בדיקת תנאי להשמה. – הערה :אם הקוד מבוצע ע"י מערכת ההפעלה ,למשל קריאת מערכת שתבצע את זה – ההנחה הזאת איננה בלתי סבירה.... • )בתנאי ש - ...איזה תנאי? – כלומר ,באיזה מקרה ,גם אם ניתן לחסום פסיקות, זה עדיין לא יבטיח לנו את קיום התנאים הדרושים מהקטע הקריטי( • על-סמך ההנחה נוכל לממש מנעולים ,כפי שתוארו קודם לכן ,באופן הבא: ©צבי מלמד 34 חסימת פסיקות )פתרון שני באמצעות תכנה( בדיקה :כאשר המנעול פנוי .1חוסמים את הפסיקות .2בודקים ומגלים שהמנעול פנוי )לא נכנסים ללולאה( { ) lock_acquire( L ; disable_interrupts { )while (L != free ; enable_interrupts ; disable_interrupts } .3נועלים את המנעול .4ומאפשרים פסיקות. • ; L = busy ; enable_interrupts כלומר בין הבדיקה לנעילה הפסיקות חסומות ולכן המעבד לא ייגזל מאתנו. } ©צבי מלמד 35 חסימת פסיקות )פתרון שני באמצעות תכנה( בדיקה :כאשר המנעול תפוס .1נכנסים ללולאה )שוב ושוב( .2חוסמים הפסיקות בסוף כל סיבוב )לפני בדיקת התנאי בכניסה לסיבוב הבא( { ) lock_acquire( L ; disable_interrupts { )while (L != free ; enable_interrupts ; disable_interrupts .3לכן כאשר נבדק התנאי אחרי שהמנעול שוחרר -הפסיקות תהיינה חסומות. • זהו התרחיש שאוזכר בשקף הקודם • בכל מקרה :בין בדיקת ערך המנעול, לנעילתו -הפסיקות חסומות. } ; L = busy ; enable_interrupts } ©צבי מלמד 36 חסימת פסיקות -מגבלות השיטה • אם התכנית עפה כאשר הפסיקות חסומות צריך לדאוג לאפשר אותן • חסימת פסיקות משמעותה עיכוב הטיפול בהן. – לכל הפחות ,החסימה כאן היא לפרק זמן קצר. • הפתרון אינו ישים למערכת בת מספר מעבדים!! – אין הגנה שתמנע ממעבד אחר לגשת למנעול )בזמן הקריטי בין הנקודה שהוא נבדק ועד הנקודה שהוא ננעל( ©צבי מלמד 37 חומרת סינכרון Synchronization Hardware • רוב מערכות החומרה מספקות תמיכה לבעיית הסנכרון • החומרה מספקת פקודת אסמבלר בודדת אשר מתבצעת באופן אטומי! • פקודה זאת לא יכולה להיקטע באמצעה • הפקודה תבצע: – בדיקת ערך של משתנה וגם... – השמת ערך חדש למשתנה או פעולה שקולה אחרת. • הרעיון דומה למה שראינו קודם – בפתרון חסימה של הפסיקות: דואגים לכך ששום דבר לא יכול להתבצע בזמן שבין בדיקת המנעול לבין שינוי ערכו לערך "נעול". ©צבי מלמד 38 חומרת סינכרון Synchronization Hardware • הפקודה המוכרת ביותר בנושא זה נקראת .TestAndSet • להלן אבסטרקציה של מה שפקודת-המכונה הזאת מבצעת: למשתנה מוכנס "בכל מקרה" הערך נעול { )bool TestAndSet( bool &var ; bool old_val = var ; var = true ; return old_val } מוחזר הערך הקודם של המנעול ©צבי מלמד 39 Synchronization Hardware {)bool TestAndSet( bool &var ; bool old_val = var ; var = true ; return old_val } המנעול מאותחל לערך ) falseלא נעול( { )while (1 ; ))while (TestAndSet(lock קטע קריטי ; lock = false כל עוד מוחזר לי שהמנעול כבר היה נעול ‘אני ממתין )(busy wait קטע שיורי { הפתרון מבטיח בלבדיות ,והתקדמות. הוא אינו מבטיח הוגנות או מניעת הרעבה ©צבי מלמד 40 Synchronization Hardware אטומיתswap() שימוש בפקודת : אטומיתswap מציעה פקודת, בעל תכונות דומות,• פתרון דומה void swap(bool &v1, bool &v2) { bool temp = v1 ; v1 = v2 ; v2 = temp ; } 41 ©צבי מלמד Synchronization Hardware using atomic swap { )void swap(bool &v1, bool &v2 ; bool temp = v1 המנעול מאותחל לערך ) falseלא נעול( ; v1 = v2 ; v2 = temp } אני רוצה לנעול – כלומר, להציב ערך trueלמנעול { )while (1 ; key= true )while (key == true ; )swap(key, lock swapמכניס ל key -את ערכו של ,lockלכן עת ערכו של key הוא falseמשמע 'תפסנו' את המנעול פתוח ,והswap - שהחזיר לנו איתות על כך ,גם נעל את המנעול עבורנו. קטע קריטי ; lock = false קטע שיורי { ©צבי מלמד 42 סינכרון בעזרת חומרה – ללא starvation • הפקודות הנ"ל אינן מבטיחות חופש מהרעבה. • נציע פרוטוקול שישתמש ב TestAndSet -ויממש את כל שלוש הדרישות מפרוטוקול סינכרון. • מבני הנתונים המוכרים לכל התהליכים: ; } bool waiting[n] = { false • מציין מי רוצה להיכנס לקטע הקריטי בעזרתו נעביר את הזכות להיכנס בין המעוניינים בזה אחר זה :כל מי שיצא יאתר את הבא אחריו שמעוניין להיכנס ו- 'יזמין' אותו. ; bool lock = false • משתנה זה יציין האם אין אף תהליך שמעוניין להיכנס לקטע קריטי )ואז הראשון שרוצה יכול להיכנס )ורק הקפד לנעול אחריך את הדלת( • לכל תהליך יוגדר משתנה לוקלי keyכמו קודם. ©צבי מלמד 43 סינכרון בעזרת חומרה ללא – starvationקטע הכניסה { )while (1 ; waiting[me] = true ; key = true "גם אני רוצה להכנס" )while (waiting[me] && key ישמש כמו במקרה הקודם עם TestAndSet ] :waiting[meמשתנה זה עשוי לשנות את ערכו כאשר 'חבר' 'יזמין' אותי להיכנס אחריו ,כאשר הוא יצא וישנה את ערך המשתנה עבורי keyישנה את ערכו אם רק אני רוצה להיכנס לקטע קריטי ומתקיים שlock == false - ; )key = TestAndSet(lock ; waiting[me] = false קטע קריטי קטע היציאה קטע שיורי } נראה שקיימת אפשרות להכנס לקטע הקריטי אפילו אם המנעול תפוס )(lock==true האם זה BUGאם שזאת נקודה מרכזית באלגוריתם? ©צבי מלמד 44 סינכרון בעזרת חומרה – ללא starvation קטע היציאה כאשר אני יוצא אני בודק אם יש מישהו שביקש להיכנס לקטע הקריטי ........... קטע קריטי ; other = (me +1) % n )]while (other != me &&!waiting[other אם מצאתי מישהו כזה ; other = (other +1) % n )if (other != me אני מסמן לו שיכנס... ;waiting[other] = false else ; lock = false אחרת – אני משחרר את המנעול הקטע השיורי } הנקודה המרכזית ...אני גואל את otherמלולאת ההמתנה שהוא מצוי בעיצומה. הוא יוצא ממנה ,מבלי שבעצם שוחרר המנעול! שיטת המיטה החמה. ©צבי מלמד 45 compare & swap : קיימת הפקודה הבאהIA32 -• ב compare&swap(mem, R1, R2) { if (mem == R1) { mem = R2 ; return true ; } return false ; } ?TestAndSet כיצד נממש בעזרתה את:• תרגיל 46 ©צבי מלמד ©צבי מלמד 47 סמפורים Semaphores • • • • • סמפור = שיטת תקשורת המבוססת על שימוש בדגלים לאיתות )לכל אות קיימת תנועת דגל מיוחדת(. בהקשר שלנו :התהליכים יאותתו זה לזה האם ניתן\אסור להיכנס לקטע קריטי מטרה :הסמפור משמש בכדי להשיג מנעול כיצד? – סמפור הוא משתנה שלם )(int – הניתן לאיתחול לערך ) 1או מספר גדול יותר( בנוסף לאיתחול ,מוגדרות עליו רק שתי הפעולות האטומיות (aהמתנה\נעילה (bאיתות\שחרור ©צבי מלמד 48 סמפורים Semaphores (a המתנה\נעילה (a איתות\שחרור { )wait(s )while (s <= 0 ; // no-op ; s-- } { )signal(s ; s++ } דרישות אטומיות: s++, s-- .1 .2כאשר הבדיקה ב wait()-נכשלת אז חובה ש s--תהיה אטומית יחד עם בדיקת התנאי שנכשלה • אין מניעה שתהיינה פסיקות ב busy-wait -של הלולאה הwhile- ©צבי מלמד 49 סמפורים • וכיצד ישמש אותנו הסמפור לצורך הגנה על קטע קריטי • נגדיר משתנה שיוכר לכל התהליכיםmutex = 1; : • כל תהליך יבצע: התהליך חסום כל עוד ערכו של mutexאיננו חיובי .ברגע שנכנסנו )ערכו היה חיובי( הקטנו את ערכו ב1- { )while (1 ;)wait(mutex הקטע הקריטי ;)signal(mutex שחרר את הסמפור לאחרים... פקודת signalמגדילה את ערכו ב1- הקטע השיורי } ©צבי מלמד 50 סמפורים • סמפור יכול לסייע לנו גם במשימות סנכרון אחרות ,לא רק בקטע קריטי • לדוגמה :נניח ש P2 -יכול לבצע קטע קוד רק אחרי ש' P1 -הכין' לו נתונים ,כלומר סיים לבצע קטע קוד. איתחול ;synch = 0 P2מבצע P1מבצע קוד ההכנה ;)wait(synch ;)signal(synch קוד השימוש ©צבי מלמד 51 מימוש סמפורים • הפתרונות שראינו עד כה בין בתכנה ובין בחמרה חייבו busy ) :waitingהתהליך מבצע לולאה ריקה בקטע הכניסה שלו( • אם הקטע הקריטי ארוך – ההמתנה מבזבזת הרבה cpu-time • סמפור עם המתנה עסוקה נקרא גם: spinlock : .the process spins while waiting for a lock • יתכן מצב שנעדיף המתנה עסוקה על-פני שתי החלפות הקשר – לדוגמא ,במערכת בת כמה מעבדים ,עם קטע קריטי קצר יחסית • במערכת עם מעבד יחיד ,או כאשר הקטע-הקריטי ארוך – נעדיף שהתהליך ילך לישון עד שהוא יוכל להיכנס לקטע הקריטי ואז יעירו אותו. ©צבי מלמד 52 מימוש סמפורים • נראה כיצד ניתן להשיג את התוצאה בעזרת סמפור "ישנוני": • נגדיר: { struct Sleepy-Semaphore ; int value ערך הסמפור "כרגיל" // רשימת תהליכים struct Process *waiting ; // שממתינים על הסמפור // } ©צבי מלמד 53 5.2.12 עד כאן מימוש סמפורים signal פעולת wait -פעולת ה void signal(Sleepy-Semaphore s) { void wait(Sleepy-Semaphore s) { s.value++ ; s.value-- ; if (s.value <= 0) { if (s.value < 0) { remove a process from add yourself to s.waiting s.waiting and wake it up go to sleep } } } } וגם,• מכיוון שכל אחת משתי הפעולות גם משנה ערך של משתנה בודקת את ערכו אזי יש לדאוג שהן תבוצענה באופן אטומי 54 ©צבי מלמד מימוש סמפורים – אטומיות בסמפור ישנוני • אם עומד לרשותנו spinlockנוכל להשתמש בו: – נגדיר שהקטעים של ה wait -וה signal -הם קטעים קריטיים, נשתמש ב) spinlock -סמפור "רגיל"( – זה יבטיח לנו שפעולות הסמפור הישנוני תבוצענה באופן אטומי – כלומר ,תבוצענה ברציפות ובלי הפרעה • אבל אז – – מה הועילו חכמים בתקנתם? – אם בכל מקרה אנו משתמשים ב spinlock -בשביל מה לנו הסמפור הנוכחי )הישנוני(? ©צבי מלמד 55 אטומיות בסמפור ישנוני • נניח שהקטע הקריטי )המקורי( עליו ברצוננו להגן כולל אלף פקודות ,ולעומת זאת פעולת ה wait/signal -על הסמפור הישנוני כוללת עשר פקודות. • אם נשתמש רק ב spinlock -תהליך שממתין לקטע הקריטי המקורים יבזבז זמן מעבד רב • אם נשתמש בסמפור הישנוני :תהליך שרוצה להיכנס לקטע הקריטי ,ראשית צריך להיכנס ל"קטעון קריטי" )בן עשר פקודות(. – מתבצע באמצעות ,busy-waitאבל ,סביר שההמתנה הזאת תהיה קצרה. – ההמתנה לקטע קריטי האמיתי – עללה להיות ארוכה ,אבל במקרה הזה הוא ילך לישון )יחסום את עצמו(. ©צבי מלמד 56 אטומיות בסמפור ישנוני • תהליך שיוצא מהקטע הקריטי הארוך: • לו היינו משתמשים רק ב , spinlock -היה עליו לבצע מעט מאוד ) signalשל – (spinlockכלומר להגדיל את ערך הסמפור. • לעומת זאת ,כאשר משתמשים sleepy-semaphore: – ראשית מבצע קטע כניסה לסמפור spinlock – לאחר מכן סיגנל של הסמפור הישנוני )קטע קוד קצר( – לבסוף signal :של .spinlock • עדיין :בהנחה שהקטע הקריטי העיקרי הוא ארוך – הרווחנו ©צבי מלמד 57 Deadlocks and Starvation • נניח שאנו משתמשים בזוג סמפורים: .S, Q • נניח ששני תהליכים המורצים במערכת מבצעים את סדרת הפקודות המתוארת ממול • התוצאה :כל אחד מהתהליכים ממתין ל signal -שישלח לו עמיתו )התקוע( ולכן המערכת מצויה במצב של חסימה הדדית. ©צבי מלמד 58 Deadlocks and Starvation • ) deadlockחסימה הדדית(: – קבוצת תהליכים מצויה ב deadlock-אם כל תהליך בקבוצה ממתין לאירוע אשר יכול להיגרם רק ע"י תהליך אחר בקבוצה. • החסימה הבלתי מוגבלת ) (indefinite blockingאו הרעבה ):(starvation – התהליך ממתין עד בלי די מבלי לקבל את המשאב\שרות שהוא זקוק לו . – )זאת בעיה דומה ,אך שונה מחסימה הדדית( • פתרונות החומרה שתיארנו עלולים ליצור הרעבה ,כך גם סמפור אשר ההמתנה עליו אינה בתור. ©צבי מלמד 59 סמפורים בינאריים Binary Semaphore • סמפורים מונים )(counting semaphore – סמפור שניתן לאתחל לכל ערך טבעי, – )הסמפורים שתיארנו עד כה( • סמפור בינארי – עשוי לקבל את הערך אפס או אחד בלבד. • לעתים ,החומרה או התכנה שעומדות לרשותנו )למשל ,ספריית (POSIXמספקות לנו רק סמפור בינארי. • המטרה – לממש סמפור מונה באמצעות זוג סמפורים בינאריים )ומשתנה שלם( ©צבי מלמד 60 implementing counting semaphore using binary semaphore wait(sem_for_counter); אם יש ממתינים לפתחו של counter++ ; הקטע הקריטי אז הער אחד ואל תעשה,מהם if (counter <= 0) כי זהsignal(sem_for_counter) signal(sem_for_cs) ; , יעשה זאת במקומך,שיתעורר מיד לכשיתעורר else signal(sem_for_counter) ; 61 ©צבי מלמד – wait() פעולת ההמתנה implementing counting semaphore using binary semaphore wait(sem_for_counter) ; counter-- ; לא ניתן להיכנס לקטע:כלומר קריטי if (counter < 0) { signal(sem_for_counter) ; wait(sem_for_cs) ; שחרר הטיפול במונה והמתן עד שיפתח הקטע הקריטי } signal(sem_for_counter) ; 62 ©צבי מלמד :שימו לב אם הקטע הקריטי היה פנוי : יחיד לsignal אנו עושים ואחרת,sem_for_counter )אם המתנו( אנו עושים ... נראה מוזר.שניים signal() פעולת השחרור implementing counting semaphore using binary semaphore wait(sem_for_counter); אם יש ממתינים לפתחו של counter++ ; הקטע הקריטי אז הער אחד ואל תעשה,מהם if (counter <= 0) כי זהsignal(sem_for_counter) signal(sem_for_cs) ; , יעשה זאת במקומך,שיתעורר מיד לכשיתעורר else signal(sem_for_counter) ; 63 ©צבי מלמד בעיות סינכרון קלאסיות • Classic Problems of Synchronization • סעיף זה חורג ממערכות הפעלה ומציג מספר בעיות קלאסיות של בקרה מקבילית ) ,(concurrency controlבעיות עליהן נהוג לבחון מנגנונים ופרוטוקולים של סנכרון )כדוגמת אלה שהוצגו(. ©צבי מלמד 64 בעיות סינכרון קלאסיות • בעיות שיוצגו כאן: – בעית החוצץ המוגבל The Bounded-Buffer Problem – בעית קוראים-כותבים – בעית הפילוסופים הסועדים The Dining Philosophers ©צבי מלמד 65 בעית החוצץ המוגבל The Bounded-Buffer Problem • הבעיה: – בהינתן מאגר של nתאים )כל אחד מכיל פריט בודד( יש לדאוג לסינכרון מילויו וריקונו ע"י יצרנים וצרכנים )אחד או יותר מכל סוג(. • פתרון אפשרי של הבעיה באמצעות סמפורים ישתמש בשלושה סמפורים שמאותחלים כמתואר: • – mutex = 1מבקר את הגישה למאגר. • – empty = nמונה כמה מקומות פנויים קיימים במאגר. • full = 0 – מונה כמה פריטים מצויים במאגר ©צבי מלמד 66 ©צבי מלמד 67 – קוד היצרןbounded buffer while (1) { create_new_element(); wait(empty); // חכה כל עוד אין מקום בחוצץ // והקטן את מספר הפנויים בחוצץ wait(mutex); //המתן לחוצץ עצמו add new element to buffer signal(mutex); // the buffer is free signal(full); // notify consumers - added element } 68 ©צבי מלמד – קוד הצרכןbounded buffer while (1) { wait(full); // חכה כל עוד אין החוצץ ריק // והקטן באחד את מספר האיברים wait(mutex); //המתן לחוצץ עצמו consume/remove element from buffer signal(mutex); // the buffer is free signal(empty); // notify producers - added space to // the buffer } 69 ©צבי מלמד בעיית הקוראים-כותבים The readers-Writers Problem • נניח אובייקט שאליו פונים שני סוגי תהליכים: – קוראים מעוניינים רק לשלוף נתונים מהאובייקט. – כותבים מעוניינים )גם( לעדכן את ערכו של האובייקט. • קוראים רבים עשויים לפנות לאובייקט במקביל • כותב חייב לפנות לאובייקט לבד ,בכדי למנוע מצבים שבהם: – הקוראים קיבלו נתוני עדכון חלקי )שגוי( של האובייקט – או שני כותבים פגעו זה בנתונים של זה ©צבי מלמד 70 בעיית הקוראים-כותבים The readers-Writers Problem • לבעיה שני וריאנטים הנבדלים בקדימות שתינתן לתהליכים השונים: • בעיית הקוראים-כותבים הראשונה: – הקורא לא ימתין למשאב המשותף ,אלא אם כן) ,ברגע הבקשה שלו( כבר קיים כותב על המשאב – משתמע :אם יש כבר קורא שניגש למשאב ,אפילו אם יש כותבים שממתינים ,הקורא החדש יוכל להצטרף לקוראים הקיימים • בעיית הקוראים-כותבים השנייה: – כאשר כותב מעוניין לכתוב הוא יוכל לעשות זאת מהר ככל האפשר – כלומר שום קורא לא יוכל לקבל אפשרות קריאה כל עוד קיים כותב .כותב חדש יקבל זכות קדימה לפני קוראים שכבר ממתינים • בשני הוריאנטים תיתכן הרעבה :בראשון של כותב ,בשני של קורא. ©צבי מלמד 71 solution to First Readers-Writers problem semaphore rc_mutex = 1 wrt = 1, // for readers only // for int readers_count =0 ; // readers count, protected by rc_mutex writers code פעולת wait(wrt) כתוב signal(wrt) wait -פעולת ה המתן עד שאין לא קורא ולא כותב שחרר את המונה 72 readers and writers wait(rc_mutex) המתן בכדי לפנות למונה readers_count++; אם אתה הקורא if (readers_count==1) המתן אם,הראשון wait(wrt) יש כותב פעיל עד signal(rc_mutex) , וכמו כן,שיסיים קריאה חסום קוראים wait(rc_mutex) נוספים readers_count--; if (readcount)==0 אם אתה הקורא האחרון signal (wrt) אז הזמן כותבים signal (rc_mutex) ©צבי מלמד אתנחתא – טיפ תכנותי • באחד האתרים ,שקראתי לאחרונה על הנושאים הללו, ניתקלתי במשפט הבא ,שנכון בהרבה מקרים ,ובפרט בתכנות "עדין ומורכב" כמו תכנות מקבילי. – Make it right then make it fast. • and – It is easier to make a correct program fast than to make a fast program correct. ©צבי מלמד 73 בעיית הפילוסופים הסועדים The Dining-Philosophers Problem • בעיית הפילוסופים הסועדים מנוסחת באופן הבא: – סביב שולחן עגול יושבים חמישה פילוסופים. – הפילוסופים עשויים לחשוב או לאכול. – כאשר פילוסוף שקוע בהגיגיו אין לו אינטראקציה עם עמיתיו. – כאשר הפילוסוף נתקף רעב ,ברצונו לאכול מתוך קערה גדולה של אורז במרכז השולחן .לשם כך הוא נזקק לשני מקלות אכילה. – על גבי השולחן ,בין כל שני פילוסופים ,מונח מקל אכילה יחיד. ©צבי מלמד 74 בעיית הפילוסופים הסועדים The Dining-Philosophers Problem • הפעולה האטומית שמבצע פילוסוף )רעב( :הרמת מקל אכילה יחיד. • כאשר הפילוסוף זכה בשני מקלות הוא יכול לאכול • כאשר הוא מסיים לאכול הוא מניח בנחת את המקלות, וחוזר לסרעפיו. • המטרה: – להביא לסיפוקם הרוחני ,וחשוב מכך הגשמי ,של הפילוסופים ,בלי להיקלע לחסימה הדדית או להרעבה. ©צבי מלמד 75 פתרון אפשרי לבעית הפילוסופיים • עבור כל מקל אכילה נחזיק סמפור שיאפשר להמתין כל עוד הוא אינו פנוי: ; }semaphore chopstick[5] = {1, 1, 1, 1, 1 • כל פילוסוף )שמספרו (iיבצע: { )while (1 המתן למקל שמימינך ; )]wait(chopstick[i המתן לזה שמשמאלך wait(chopstick[(i+1) % 5]) ; אכול אם כל הפילוסופים יתקפו ברעב במקביל ,כל אחד מהם ירים את המקל הימני ,ויחכה עד בלי די למקל השמאלי .הם ישארו רעבים )תרתי משמע( ; )]signal(chopstick[i ; )]signal(chopstick[(i+1) % 5 } ©צבי מלמד 76 שיפורים אפשריים לפתרון לבעית הפילוסופיים • פתרון קל :נוסיף מקל אכילה שישי :כך לפחות אחד הפילוסופים יוכל לאכול, לשחרר את מקלותיו ,ואז לפי תור גם כל האחרים יוכלו לשבוע. • הרמת זוג מקלות האכילה תבוצע בקטע קריטי; אם רק אחד מהם פנוי יש לשחרר את זה שנרכש .פתרנו את בעיית החסימה ההדדית ,אך נקלענו לבעיה חלופית :חשש להרעבה. • נמס ְ פ ֵ ר את הפילוסופים .פילוסוף אי-זוגי יתחיל בהרמת המקל משמאלו והזוגי יתחיל בהרמת המקל שמימינו .בדקו שגם כאן נחלצנו מהחסימה ההדדית ,אבל לא מחשש להרעבה )פילוסוף איטי יושב לצד פילוסוף מהיר וזללן שאוכל לעתים מזומנות(. • ניוותר כרגע רעבים לפתרון. • קיים פתרון שמבוסס על מוניטורים – כנראה שנדלג עליו ©צבי מלמד 77 הבעיה האמיתית של הפילוסופים הסועדים • ננסה לחדד עוד יותר את הבעיה: – מהי הבעיה האמתית של הפילוסופים הסועדים? ©צבי מלמד 78 הבעיה האמיתית של הפילוסופים הסועדים • מסתבר שמדובר בפילוסופים יהודיים • לא יודעים לאכול עם מקלות-סיניים ©צבי מלמד 79 פתרון א' לבעית המקלות תוכנית הדרכה ואימון מצוירת ומפורטת לשימוש במקלות סיניים התוכנית נכשלה כי לפילוסופים "אין ראש לזה" ©צבי מלמד 80 פתרון ב' לבעית המקלות • "הראש היהודי ממציא לנו פטנטים" • עוד חברת סטארט-אפ ישראלית בדרך לאקזיט לאחר שפיתחה מוצר ייחודי שפותר את בעיית הפילוסופים הסועדים ©צבי מלמד 81 פתרון ג' לבעית המקלות שישתמשו בFORKS - ©צבי מלמד 82 אמצעים לסנכרון תהליכים ביוניקס • שניים מהכלים שראינו בדיוננו התיאורטי ממומשים ביוניקס: – סמפורים – משתני תנאי ©צבי מלמד 83 סמפורים ביוניקס/לינוקס • הסמפור מבטיח לנו ששתי הפעולות המבוצעות בו: – בדיקת ערכו ,ובמידת הצורך המתנה עליו. – שינוי ערכו. • תבוצענה באופן אטומי . • ביוניקס :הסמפורים הם "האח השלישי" במשפחת ) – XSI-IPCאחים לתורי- הודעות ולזכרון-משותף( – הטיפול – דומה למשאבים האחרים שהיכרנו – צריך ליצור אותם באופן מפורש ,והם ממשיכים להתקיים עד שמשחררים אותם מפורשות • פקודת – ipcsמציגה את המשאבים במערכת ) – ipcs –sמציגה את מערכי הסמפורים( • פקודת – ipcrmמוחקת משאבים ©צבי מלמד 84 יצירת סמפור בעזרת )(semget • כדי לקבל קבוצת סמפורים נתחיל בכך שנייצר מפתח לקבוצה: ; )'key_t key = ftok("~yoramb", 'y … )if (key == -1 • תוך שימוש במפתח נבקש ליצור קבוצת סמפורים חדשה ,או להיקשר לקבוצה קיימת: = int sem_set_id המפתח החיצוני semget(key, 1, כמה סמפורים אנו רוצים ; )IPC_CREAT | 0600 … )if (sem_set_id == -1 • אם מצטרפים לסמפור קיים ,הארגומנט השני הוא – don’t careלשם הניקיון והקריאות ,נעביר אפס. ©צבי מלמד 85 ©צבי מלמד 86 סמפור ביוניקס -ב .איתחול הסמפור { union semun ; int val ; struct semid_ds *buf ; unsigned short *array ;} לשם האיתחול נזדקק ל union -הבא – עלינו להכריז עליו בתכנית אנו מתענינים רק בחבר .valנאתחל אותו לערך הרצוי: ; union semun sem_val ; sem_val.val = 1 איתחול הסמפור ) ,0#היחידי שיש לנו בקבוצה( תיאור הקבוצה... תיאור הסמפור בתוך הקבוצה איזו פעולה לבצע status = semctl(sem_set_id, 0, SETVAL, ; )sem_val … )if (status == -1 באיזה ערך להשתמש ©צבי מלמד 87 הפונקציה )(semctl • )( semctlמאפשרת לנו לבצע מגוון פעולות על קבוצת הסמפורים )מחיקתה ,בדיקת ערך של סמפור רצוי בקבוצה(. • בפעולות השונות נספק קוד פעולה שונה ,ובמידת הצורך ערכים שונים ב.union - ©צבי מלמד 88 קוד הכניסה )פעולת ה(wait - הגדרת משתנה ואיתחולו: פעל על סמפור מספר sem_num הפעולה :הקטן את ערכו באחד דגלים נוספים :לא מענינים אותנו )למשל אם איננו רוצים להמתין( ; struct sembuf sem_op ; sem_op.sem_num = 0 ; sem_op.sem_op = -1 ; sem_op.flg = 0 ונבצע את הפעולה עם הארגומנטים: מזהה הקבוצה מתאר הפעולה מספר הסמפורים בפעולה הזאת if (semop(sem_set_id, &sem_op, … )1) == -1 • כאשר חלפנו על-פני פעולה זאת בהצלחה אנו בקטע הקריטי. ©צבי מלמד 89 קוד היציאה )פעולת ה(signal - • פעולת היציאה סימטרית לגמרי לפעולת הכניסה .בהבדל שכאן נקבע ערך חיובי )הגדלת ערך הסמפור(: ; sem_op.sem_op = 1 ©צבי מלמד 90 דוגמא IPC Semaphores - • התכנית מבצעת את המשימה הבאה: – הגדר קבוצת סמפורים בת סמפור יחיד. – אתחלו לערך .1 – הולד חמישה ילדים. – כל ילד בלולאה מנסה להוסיף נתונים לקובץ משותף על כן: • ממתין על הסמפור. • פותח את הקובץ. • כותב עליו • סוגר אותו • משחרר את הסמפור ©צבי מלמד 91 IPC Semaphores - דוגמא // file: semaphore1_protect_file.c // Adopted from Guy Keren (actcom) #include #include #include #include #include #include #include #include 92 <stdio.h> <stdlib.h> <unistd.h> <time.h> <sys/types.h> <sys/ipc.h> <sys/sem.h> <sys/wait.h> ©צבי מלמד IPC Semaphores - דוגמא #define NUM_PROCS 5 #define SEM_ID 3879 #define CHILD_PROC 0 #define FILE_NAME "sem_mutex.txt" // ======structs etc. ====================== union semun { int val ; struct semid_ds *buf ; unsigned short *array ; } ; //=====PROTOTYPES ==================== void do_child_loop(int sem_set_id, char* file_name) ; void update_file(int sem_set_id, char* file_path, int number) ; void burn_time(); //===================================== 93 ©צבי מלמד int main() { int sem_set_id; union semun sem_val; int child_pid; int i; int rc; 94 IPC Semaphores דוגמא (ftok יצירת קבוצת הסמפורים )ללא sem_set_id = semget(SEM_ID, 1, IPC_CREAT | 0600); if (sem_set_id == -1) { perror("main: semget failed"); exit(EXIT_FAILURE); } sem_val.val = 1; #0 איתחול הסמפור rc = semctl(sem_set_id, -1 בקבוצה לערך 0, SETVAL, sem_val); if (rc == -1) { perror("main: semctl failed"); exit(EXIT_FAILURE); } ©צבי מלמד דוגמא IPC Semaphores int main() ..................... for (i=0; i<NUM_PROCS; i++) { child_pid = fork(); switch (child_pid) { case -1: perror("fork failed"); exit(EXIT_FAILURE); case CHILD_PROC: do_child_loop(sem_set_id, FILE_NAME); exit(EXIT_SUCCESS); default: break; } } /* wait for all children to finish running */ for (i=0; i<NUM_PROCS; i++) wait(NULL); puts("main: we're done\n"); fflush(stdout); return EXIT_SUCCESS ; } 95 ©צבי מלמד IPC Semaphores דוגמא // repeat: // wait on the sem sem_set_id (on #0 in it) // then open file_name, write on it, and close it void do_child_loop(int sem_set_id, char* file_name) { pid_t pid = getpid(); int i; for (i=0; i<7; i++) update_file(sem_set_id, file_name, pid); } 96 ©צבי מלמד דוגמא IPC Semaphores void update_file(int sem_set_id, char* file_path, int number) { struct sembuf sem_op; FILE* file; // Entry section: wait on semaphore sem_op.sem_num = 0; // num of sem in set sem_op.sem_op = -1; // wait operation sem_op.sem_flg = 0; // flags we leave 0 semop(sem_set_id, &sem_op, 1); 97 // The critical section file = fopen(file_path, "a"); if (file) { setbuf(file, NULL) ; fputs("I ", file) ; fputs("am ", file) ; fputs("process ", file) ; fputs("#", file) ; fprintf(file, "%d\n", number); fclose(file); } ©צבי מלמד כל פעולת כתיבה נשלחת מידית לקובץ ללא חציצה מבצעים מספר פעולות כתיבה שאינן כך שיש גם יש מקום,אטומיות להחלפת הקשר תוך כדי הפעולה IPC Semaphores דוגמא void update_file(int sem_set_id, ............................. // Exit section: signal on semaphore sem_op.sem_num = 0; sem_op.sem_op = 1; // signal operation sem_op.sem_flg = 0; semop(sem_set_id, &sem_op, 1); } 98 ©צבי מלמד IPC Semaphores - דוגמא <205|0>yoramb@inferno-05:~/os$ less sem_mutex.txt I am process #20088 I am process #20087 I am process #20089 I am process #20088 I am process #20090 I am process #20089 I am process #20091 I am process #20090 I am process #20087 I am process #20091 I am process #20088 I am process #20087 I am process #20089 I am process #20088 I am process #20090 I am process #20089 I am process #20091 I am process #20090 <206|0>yoramb@inferno-05:~/os$ I am process #20091 I am process #20087 I am process #20088 I am process #20089 I am process #20090 .הפלט מסודר ויפה I am process #20091 I am process #20087 לשם השוואה נראה מה קורה ללא סמפורים I am process #20088 I am process #20089 :()השימוש בסמפורים נסגר בהערת תיעוד I am process #20090 I am process #20091 I am process #20087 99 ©צבי מלמד IPC Semaphores - דוגמא <206|0>yoramb@inferno-05:~/os$ less sem_mutex.txt.no_sem I am process #20020 I am process #20021 I am process #I am process 20020 I I #I am I am 20022 am process #20021 process am process #I process #20023 I am #20024 am process I 20020 process #am #I 20022 process I 20021 .השימוש בסמפורים נסגר בהערת תיעוד am #I am process 20023 am process #I process #20020 am I #20024 . ממש לא מה שרצינו.הפלט מבולגן ומעורבב process am I 20022 #process #20023 I am process #20021 am I 20020 process I am I am process #20021 #20024 am process process ##I 20023 20022 100 ©צבי מלמד אותו הפלט גם בלי הסמפור? • אני לא הצלחתי לשחזר את פלט ההרצה שיורם הציג) .כלומר ,גם ללא סמפור ,הפלט היה מסודר( שאלות .1מהו השינוי המינמילי שצריך לבצע בתוכנית בכדי לבטל את הסמפור? .2מה יכולה להיות הסיבה לכך שאצלי הפלט היה מסודר? .3איך אני יכול לגרום לכך ,שגם אצלי יהיה הבדל – כלומר יהיה באלאגן בלי סמפור? ©צבי מלמד 101 ©צבי מלמד 102 המינימום הדרוש לבטל את הסמפורים... ©צבי מלמד 103 הפלט עדיין מסודר – למרות שביטלתי את הסמפור ©צבי מלמד 104 תוכנית מעודכנת ליצירת הבדלים בפלט ©צבי מלמד 105 הפלט תוך שימוש בסמפורים ©צבי מלמד 106 הפלט ללא שימוש בסמפורים ©צבי מלמד 107
© Copyright 2024