OS3_synchronization

Download Report

Transcript OS3_synchronization

‫סינכרון בין תהליכים‬
‫מבוסס ברובו על שקפים מ'מבוא למערכות הפעלה ‪ / '234119‬אייל איצ'קוביץ‬
‫‪1‬‬
‫תהליכים יכולים‪...‬‬
‫‪‬‬
‫‪‬‬
‫להתחרות (על היע"מ או משאבים אחרים)‬
‫‪ ‬כשתהליכים מתחרים‪ ,‬מ"ה מתזמנת את התהליכים ומגנה עליהם‬
‫אחד מהשני‪.‬‬
‫לשתף פעולה ולחלוק משאבים‬
‫‪ ‬כשתהליכים משתפים פעולה‪ ,‬מ"ה מספקת את האמצעים‬
‫(מנגנונים) כדי לחלוק את המשאבים‪ ,‬ולקשר בין התהליכים‪.‬‬
‫‪ ‬שיתוף פעולה הוא או על ידי שיתוף )‪ (shared memory‬או על ידי‬
‫תקשורת (‪.)message passing‬‬
‫‪2‬‬
‫דוגמאות לתחרות על משאבים‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫תהליכים שונים מתחרים על היע"מ‬
‫תהליכים שונים מתחרים על הזיכרון‬
‫משאבים שזקוקים ל ק‪/‬פ כגון מסך או מדפסת‬
‫משאבים שזקוקים לרוחב פס )‪(bandwidth‬‬
‫‪3‬‬
‫בעיות עם תהליכים משתפים‬
‫‪‬‬
‫‪‬‬
‫בעיה‪ :‬סדר ביצוע פעולות של תהליכים משתפי פעולה יכול להיות‬
‫חשוב‪.‬‬
‫תהליך א'‬
‫תהליך ב'‬
‫לא חשוב‬
‫‪A=1‬‬
‫‪B=2‬‬
‫חשוב‬
‫‪A=B+1‬‬
‫‪B=2‬‬
‫דוגמה 'קלאסית'‪ :‬כספומטים‬
‫‪4‬‬
‫דוגמה‪ :‬קניית חלב בבית משותף‬
‫א'‬
‫‪5‬‬
‫ב'‬
‫‪3:00‬‬
‫מסתכל במקרר‪ :‬אין‬
‫חלב‬
‫‪3:05‬‬
‫הולך למכולת‬
‫‪3:10‬‬
‫מגיע למכולת‬
‫מסתכל במקרר‪ :‬אין חלב‬
‫‪3:15‬‬
‫קונה חלב‬
‫הולך למכולת‬
‫‪3:20‬‬
‫עוזב את המכולת‬
‫מגיע למכולת‬
‫‪3:25‬‬
‫חוזר הביתה‪ ,‬שם‬
‫חלב במקרר‬
‫קונה חלב‬
‫‪3:30‬‬
‫עוזב את המכולת‬
‫‪3:35‬‬
‫חוזר הביתה‪ ,‬שם חלב‬
‫במקרר – אההה!‬
‫פתרון נכון‪ :‬אחד קונה חלב‪ ,‬לא שניים‪.‬‬
‫בעיית 'מניעה הדדית' ('‪)'mutual exclusion‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫הדוגמה של קניית החלב ממחישה שדרוש מנגנון תאום בין תהליכים‪.‬‬
‫דרוש מנגנון שמבטיח שרק תהליך אחד עושה דברים מסוימים‪ ,‬וכך‬
‫נמנעת בעיה של חוסר עקביות במידע‪.‬‬
‫השאר – מעוקבים עד שהתהליך הזה מסיים‪.‬‬
‫נבצע הכללה‪ .‬נניח שהתהליך חוזר על עצמו לעד‪ ,‬והמטרה שלנו היא‬
‫שהם לא ייקנו חלב בו זמנית‪ .‬לזה באופן כללי נראה 'הקטע הקריטי'‪.‬‬
‫‪6‬‬
‫הקטע הקריטי‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫חלק של הקוד שאנחנו רוצים להפוך אותו לאטומי‪ :‬רק תהליך אחד‬
‫יכול לבצע אותו – מתחילתו ועד סופו – בו‪-‬זמנית‪.‬‬
‫ש‪ :‬כיצד נבטיח את האטומיות של הקטע הקריטי ?‬
‫ת‪ :‬בעזרת אלגוריתמים למניעה הדדית‪.‬‬
‫‪7‬‬
‫גישות ליצירת תאום‬
‫‪‬‬
‫‪‬‬
‫ללא עזרה ממערכת ההפעלה‬
‫‪ ‬כך עבדו פעם‪...‬‬
‫‪ ‬כך עובדים היום במערכות הפעלה (ייעודיות) מסוימות‪.‬‬
‫‪ ‬אלגוריתמים "מפורסמים"‪ ,‬מבהירים את הבעיתיות‪.‬‬
‫עם עזרה ממערכת ההפעלה‬
‫‪ ‬כך עובדים היום‪...‬‬
‫‪ ‬שימוש ב"סמפורים"‪.‬‬
‫‪8‬‬
‫פתרון ‪1#‬‬
‫{ )‪if (no Milk‬‬
‫{ )‪if (no Note‬‬
‫;‪Leave note‬‬
‫;‪Buy milk‬‬
‫;‪Remove note‬‬
‫}‬
‫}‬
‫‪‬‬
‫‪‬‬
‫טוב לאנשים‪ ,‬כי הפעולות הראשונות (לבדוק אם יש חלב‪ ,‬אם יש‬
‫פתק‪ ,‬להשאיר פתק) – הן אטומיות‪.‬‬
‫עבור פעולה ממוחשבת‪ ,‬זה לא מספיק טוב כי אין את ההנחה הזאת‪.‬‬
‫‪9‬‬
‫נשתמש בשני פתקים‬: 2# ‫פתרון‬
Process A
Process B
while true{
Leave NoteA;
if ( no NoteB ) {
if ( no Milk ) {
Buy Milk;
}
}
Remove NoteA;
}
while true{
Leave NoteB;
if ( no NoteA ) {
if ( no Milk ) {
Buy Milk;
}
}
Remove NoteB;
}
.‫קיים 'חישוב' (מסלול חוקי) בו הם לעולם לא נכנסים לקטע הקריטי‬
10

#3 ‫פתרון‬
Process A
Process B
while true{
Leave NoteA;
if ( no NoteB ) {
if ( no Milk ) {
Buy Milk;
}
}
Remove NoteA;
}
while true{
Leave NoteB;
while ( NoteA );
if ( no Milk ) {
Buy Milk;
}
Remove NoteB;
}
‫במקרה של 'תיקו' – התהליך השני ייקנה את החלב‬
11
‫חסרונות של פתרון ‪#3‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫מסובך‪...‬‬
‫תהליך ב' דורש משאבי מחשב בזמן המתנה )‪(busy wait‬‬
‫תהליך לא סימטרי‬
‫קשה להרחבה למספר תהליכים (למשל‪...)4 ,‬‬
‫‪12‬‬
‫נבצע הכללה‬
‫‪‬‬
‫‪.1‬‬
‫‪.2‬‬
‫‪.3‬‬
‫דרישות בסיסיות מתהליכים‪ ,‬על מנת שישתפו פעולה נכון וביעילות‬
‫בהסתמך על ‪ data‬משותף‪:‬‬
‫מניעה הדדית )‪ :(mutual exclusion‬אם תהליך אחד מבצע את‬
‫הקטע הקריטי‪ ,‬אף תהליך אחר לא עושה זאת‪.‬‬
‫התקדמות (אין חֶ בֶק – ‪ :)deadlock‬אם אף תהליך לא נמצא בקטע‬
‫הקריטי‪ ,‬ויש תהליך שרוצה להיכנס לקטע הקריטי‪ ,‬אז בסופו של דבר‬
‫התהליך הזה ייכנס לקטע הקריטי‪.‬‬
‫הוגנות‪ :‬אם תהליך מעוניין להיכנס לקטע הקריטי וגם אחרים‬
‫מעוניינים‪ ,‬לא ייתקיים מצב בו הוא ימתין לנצח‪.‬‬
‫‪13‬‬
‫הנחות‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫נניח שמתקיימת הוגנות תהליכים )‪ :(process fairness‬אם תהליך‬
‫מבקש להתקדם )‪ (enabled‬אין סוף פעמים‪ ,‬הוא מתקדם‪.‬‬
‫קיים קטע קריטי – עליו יש להגן‪ .‬זמן בקטע הקריטי הוא סופי‪.‬‬
‫קיים קטע לא קריטי – יכול להימשך לנצח‪.‬‬
‫‪ ‬הנמשל בבעית החלב הוא‪...‬‬
‫אין הנחות לגבי זמן הביצוע היחסי בין התהליכים‪.‬‬
‫‪14‬‬
1# ‫ נסיון‬.‫ ננסה לפתור‬,‫שוב‬
initially: A_inside = B_inside = false;
Process A
Process B
while( TRUE ) {
< non-critical >
while ( B_inside );
A_inside = TRUE;
< criticalA >
A_inside = FALSE;
...
}
...
while( TRUE ) {
< non-critical >
while ( A_inside );
B_inside = TRUE;
< criticalB >
B_inside = FALSE;
...
}
...
A ‫ עד ש‬false ‫ מושם‬B_inside ‫ מהרגע ש‬:‫ למשל‬.‫ עלול להפר מניעה הדדית‬:‫בעיה‬
.‫ נכנס שוב לקטע הקריטי‬B ,A_inside = true ‫משים‬
15

2# ‫ניסיון‬
Process A
while( TRUE ) {
< non-critical >
while( turn == B );
< criticalA >
turn = B;
...
}
...
Process B
while( TRUE ) {
< non-critical >
while( turn == A );
< criticalB >
turn = A;
...
}
...
.‫ ביצוע מתחיל במקביל‬.B ‫ או‬A ‫ הוא משתנה גלובאלי מאותחל ל‬turn
non-critical ‫ יכול להישאר לעד ב‬A ‫ למשל תהליך‬.‫ התקדמות‬:‫בעיה‬
16


Peterson ‫ האלגוריתם של‬:3# ‫ניסיון‬
initially, turn = A (or B), A-trying = B-trying = false
Process A
...
while( TRUE ) {
< non-critical >
A-trying = TRUE;
turn = B;
while( B-trying &&
turn == B );
< criticalA >
A-trying = FALSE;
...
}
...
Process B
...
while( TRUE ) {
< non-critical >
B-trying = TRUE;
turn = A;
while( A-trying &&
turn == A );
< criticalB >
B-trying = FALSE;
...
}
...
2# ‫שימו לב שזאת הרחבה פשוטה של ניסיון‬
17
‫מה לגבי מספר רב של תהליכים?‬
‫‪‬‬
‫‪‬‬
‫האלגוריתם של ‪ Peterson‬הוא נכון‪ ,‬אבל סובל משתי בעיות‪:‬‬
‫‪ ‬קשה להרחיב אותו ליותר משני תהליכים (ניתן‪ ,‬אבל מסובך)‬
‫‪ ‬דורש לדעת מראש כמה תהליכים יש‪.‬‬
‫נראה אלטרנטיבה שמתאימה למספר רב ולא ידוע מראש של‬
‫משתמשים‪.‬‬
‫‪18‬‬
ִ
(Bakery) ‫המַאפיה‬
‫אלגוריתם‬
Boolean entering[n];
int number[n];
...
while (TRUE) {
entering[i] = TRUE;
number[i] = max(number[0],..., number[n-1])+1;
entering[i] = FALSE;
for ( j = 0; j < n; j++ ) { // why n and not i ? see next slide
while (entering[j] ) ; // Wait until thread j receives its number:
while (number[j] != 0 &&
( number[j] < number[i] || (number[j] == number[i] && j < i)) ) ;
}
< critical >
‫המתן עד שכל התהליכים האחרים עם‬
number[i] = 0;
‫ או עם מספר שווה‬,‫מספר נמוך יותר‬
< non-critical >
.‫ מסיימים‬,‫אבל עדיפות גבוהה יותר‬
19
}
‫ִ‬
‫המַאפיה )‪(Bakery‬‬
‫אלגוריתם‬
‫‪‬‬
‫לא כותבים למשתנה משותף‪ ,‬רק קוראים ממשתנה משותף‪.‬‬
‫‪.1‬‬
‫מה לעשות בקשר לזה שהמונה הוא סופי? (מסוג ‪.)int‬‬
‫‪‬‬
‫האם אפשר בלולאת ה ‪ for‬לעשות את הטווח עד ‪? i‬‬
‫‪.2‬‬
‫‪‬‬
‫‪.3‬‬
‫שאלה טובה‪...‬‬
‫לא‪ ,‬כי התהליכים נכנסים לפי סדר שרירותי‪ .‬יכול להיות שתהליך ‪ 8‬ייכנס לפני‬
‫תהליך ‪ .4‬מה שחשוב זה הסדר בו הם מקבלים את המספרים‪ ,‬לא המספר‬
‫הסידורי שלהם‪.‬‬
‫באיזה מקרה צריך את התנאי ]‪? number[j] = number[i‬‬
‫‪‬‬
‫‪20‬‬
‫ייתכן סירוג בו שניים קוראים את)‪ max(...‬ואז מקבלים ערך‪.‬‬
‫בפועל המצב מסובך יותר בגלל פסיקות‪.‬‬
‫‪‬‬
‫ברמת החומרה‪ ,‬יש שני כלים בסיסיים שיכולים לעזור לנו‪:‬‬
‫‪interrupt masking ‬‬
‫‪ ‬פתרון לא טוב ובדרך כלל לא משתמשים בו‪.‬‬
‫‪‬‬
‫‪atomic read-modify-write‬‬
‫‪ ‬רב המעבדים היום מאפשרים לקרוא‪ ,‬לשנות ולכתוב מילה‬
‫באופן אטומי‪ .‬פקודות מכונה‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪21‬‬
‫)‪TAS—test-and-set (Motorola 68K‬‬
‫)‪CAS—compare-and-swap (IBM 370 and Motorola 68K‬‬
‫)‪XCHG—exchange (x86‬‬
‫נראה מספר שימושים‪.‬‬
TAS ‫דוגמה לשימוש ב‬
‫ למימוש מניעה הדדית‬TAS ‫שימוש ב‬
boolean lock = false;
function Critical() {
while TestAndSet(lock);

//spin until lock
// is acquired
<critical section>
lock = false;
}
‫ (לא תפוס) הופך אותו ל‬lock = false ‫אם‬
.‫ בפעולה אטומית אחת‬0 ‫ ומחזיר‬true
.1 ‫ מחזיר‬lock = true ‫אם‬
.‫ סטנדרטי אין גישה לפקודה זו‬C – ‫ב‬
)‫ (קריאה לאסמבלי‬asm() ‫רק דרך‬
22
‫שימוש נוסף‪ :‬סמפורים‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫בעזרת ‪ TAS‬ניתן לממש סמפורים‪.‬‬
‫סמפור הינו מנגנון לתאום בין תהליכים‪.‬‬
‫רוב מערכות ההפעלה תומכות במנגנון זה‪.‬‬
‫פשוטים‪ ,‬ומאפשרים פתרון להרבה בעיות מעניינות‪.‬‬
‫‪23‬‬
‫סמפורים‬
‫‪‬‬
‫‪‬‬
‫המנגנון מאפשר לתהליכים לחכות למשאבים ללא ‪busy wait‬‬
‫המנגנון מורכב מ‪:‬‬
‫‪ ‬מונה‬
‫‪ ‬פעולת ‪wait‬‬
‫‪ ‬פעולת ‪signal‬‬
‫‪ ‬כמה פקודות נוספות לאתחול‪ ,‬איפוס וכו'‪.‬‬
‫‪24‬‬
‫סמפורים‬
‫‪‬‬
‫בשקפים הבאים הפקודות הן לפי ההגדרה ה'קלאסית' של סמפורים‬
‫בספרות‪ .‬המימוש ב ‪ C‬הוא מעט יותר מסובך )‪ ,(sem.h‬אבל בסך‬
‫הכל דומה‪.‬‬
‫‪‬‬
‫נגדיר משתנה מסוג סמפור‪:‬‬
‫‪‬‬
‫שתי פעולות בסיסיות ואטומיות‪:‬‬
‫;‪Semaphore s‬‬
‫)‪wait(S‬‬
‫)‪signal(S‬‬
‫‪25‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫פקודת ‪wait‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫מורידה את מונה הסמפור באחד‬
‫אם המונה שלילי‬
‫‪ ‬משנה את מצבו של התהליך הנוכחי ל‪WAIT -‬‬
‫‪ ‬מכניסה את התהליך לתור הממתינים על הסמפור‬
‫‪( … ‬מספר דברים נוספים‪ :‬נראה מאוחר יותר)‪.‬‬
‫ש‪ :‬מה משמעות המונה כשהמספר חיובי ? שלילי ?‬
‫‪26‬‬
‫פקודת ‪signal‬‬
‫‪‬‬
‫‪‬‬
‫מעלה את מונה הסמפור באחד‬
‫אם לפני ההוספה היה הסמפור שלילי‪,‬‬
‫‪ ‬מוציאה את הראשון בתור הסמפור‪ ,‬מכניסה אותו לתור ה‪.ready -‬‬
‫‪27‬‬
...‫נפתור את בעיית קניית החלב‬
initially:
1
2
3
4
5
OKToBuyMilk = 1
Wait(OKToBuyMilk);
if ( NoMilk ) {
Buy Milk;
// critical section
}
Signal(OKToBuyMilk);
28
‫כשיש יותר ממשאב אחד‪...‬‬
‫‪‬‬
‫‪‬‬
‫יכולים להיות הרבה 'חלקים קריטיים'‪ ,‬לכל אחד סמפור משלו‬
‫אפשר לאפשר מספר גדול מאחד אך חסום של תהליכים שייכנסו‬
‫לקטע הקריטי‪.‬‬
‫‪29‬‬
‫דוגמה‪ :‬משאבים רבים‬
‫‪ ‬שני תהליכים‬
‫‪ ‬אחד כותב‬
‫‪a a a a‬‬
‫‪ ‬אחד קורא ומוחק‬
‫‪ ‬כיצד הכותב יידע אם נשאר מקום לכתיבה ?‬
‫‪ ‬כיצד הקורא יידע מהיכן לקרוא את האות הבאה ?‬
‫(כאן המקומות הפנויים הם המשאבים שלנו)‪.‬‬
‫‪‬‬
‫האם ניתן לפתור את הבעיה עם משתנה גלובאלי (מונה‪ ,‬או מצביע) ?‬
‫‪30‬‬
‫דוגמה‬
‫{ )( ‪Buffer_Remove‬‬
‫;)‪Wait(avail‬‬
‫;)‪chr = get(array‬‬
‫;)‪Signal(places‬‬
‫}‬
‫{)‪Buffer_Insert(char chr‬‬
‫;)‪Wait(places‬‬
‫;)‪put(array, chr‬‬
‫;)‪Signal(avail‬‬
‫}‬
‫בדוגמה הזאת ‪ places‬ו ‪ avail -‬הם מונים (מייצגים כמה מקומות פנויים יש‪ ,‬וכמה‬
‫תווים יש בחוצץ)‪.‬‬
‫‪ ‬מאפשר למספר כותבים בו זמנית ומספר קוראים בו זמנית‪.‬‬
‫‪‬‬
‫אם מתחילים למשל מ ‪ places = 5‬אז לא ניתן יהיה להכניס יותר מ ‪ ,5‬כי הסמפור‬
‫יורד ב ‪ 1‬כל פעם‪.‬‬
‫‪‬‬
‫‪31‬‬
‫ועכשיו‪...‬‬
‫‪‬‬
‫נראה מימוש של סמפורים במערכת הפעלה דומה ל ‪.unix‬‬
‫‪ ‬כלומר‪ ,‬נראה את הפונקציות הנגישות למשתמש‪ ,‬ודוגמה לשימוש‬
‫בהן‪.‬‬
‫‪32‬‬
‫ישום אפשרי של סמפורים‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫מוגדר מצב חדש במערכת בשם ‪( PR_WAIT‬בנוסף למצב ה ‪wait‬‬
‫הרגיל ממנו יוצאים בעקבות פסיקה)‪.‬‬
‫לכל סמפור קיים תור של תהליכים הממתינים לו‪.‬‬
‫‪ signal‬ו‪ wait -‬מעבירות תהליכים בין תור ה‪ ready -‬ותורי‬
‫הסמפורים‪.‬‬
‫‪33‬‬
‫מבנה נתונים אפשרי ליישום סמפורים‬
‫‪‬‬
‫‪‬‬
‫שמור טבלה של סמפורים כאשר המזהה של הסמפור הינו האינדקס‬
‫שלו בטבלה‬
‫בכל כניסה בטבלה נשמרת רשומה המכילה‪:‬‬
‫‪ ‬מצב הסמפור (האם הוא בשימוש)‬
‫‪ ‬הערך המשויך לסמפור‬
‫‪ ‬מצביעים לראש וזנב התור של הממתינים לאותו סמפור‬
‫‪34‬‬
‫מוגדרים המשתנים הבאים‬
#define
N_SEM 45
#define
S_FREE '\01'
#define
S_USED '\02'
struct s_entry {
char
s_state;
short sem_cnt;
short s_q_head;
short s_q_tail;
};
extern struct s_entry
extern int next_sem;
/*
/*
/*
/*
/*
/*
/*
/*
number of semaphores,
*/
this semaphore is free */
this semaphore is used */
semaphore table entry
*/
the state S_FREE or S_USED */
count for this semaphore */
queue index of head of list */
queue index of tail of list */
semaph[];
35
‫יצירת סמפורים וביטולם‬
‫‪‬‬
‫)‪ s = s_create (count‬יוצר סמפור בעל ערך התחלתי‬
‫‪ ,count‬ומחזיר את המציין שלו ‪.s‬‬
‫‪‬‬
‫)‪ s_delete(S‬מבטל את הסמפור ‪.s‬‬
‫‪‬‬
‫המערכת קובעת את המספר המזהה של הסמפור‪.‬‬
‫‪36‬‬
‫כיצד פועלות ‪ wait‬ו‪signal -‬‬
‫‪‬‬
‫‪‬‬
‫‪ wait‬ו‪ signal -‬מכניסות ומוציאות תהליכים מתור הממתינים‬
‫לסמפור‬
‫‪ wait‬ו‪ signal -‬דואגות שהכלל הבא יהיה תמיד נכון‪:‬‬
‫‪ .1‬כשמונה הסמפור אינו שלילי תור הממתינים לסמפור ריק‪.‬‬
‫‪ .2‬כאשר המונה שלילי‪ ,‬הערך המוחלט שלו מציין את מספר‬
‫הממתינים בתור‪.‬‬
‫‪37‬‬
‫מה עושה ‪wait‬‬
‫‪‬‬
‫‪‬‬
‫מורידה את מונה הסמפור באחד‬
‫פעולה אטומית!‬
‫אם המונה שלילי‬
‫‪ ‬משנה את מצבו של התהליך הנוכחי ל‪PR_WAIT -‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫מכניסה לרשומת התהליך את מזהה הסמפור‬
‫‪ ‬כך שאם למשל יהרגו את התהליך תדע ‪ kill‬להוציא אותו‬
‫מהתור ולהגדיל את מונה הסמפור‬
‫מכניסה את התהליך לתור הממתינים על הסמפור‬
‫קוראת ל‪ resched -‬כדי להוציא את התהליך הנוכחי מהמעבד‬
‫פונקציית ‪.rescheduling‬‬
‫‪38‬‬
‫מה עושה ‪signal‬‬
‫‪‬‬
‫‪‬‬
‫מוסיפה אחד למונה הסמפור‬
‫אם לפני ההוספה היה הסמפור שלילי‬
‫‪ ‬מוציאה את הראשון בתור הסמפור ומכניסה אותו לתור ה‪ready -‬‬
‫‪ ‬קוראת ל‪resched -‬‬
‫פעולה אטומית!‬
‫‪39‬‬
‫מה עושה ‪s_create‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫קוראת לפונקציה ‪ new_sem‬על מנת לקבל מזהה של סמפור פנוי‪.‬‬
‫אם לא קיים סמפור פנוי תחזיר הפונקציה שגיאה‪.‬‬
‫אם נמצא מזהה לסמפור‪ ,‬מעדכנים את המונה שלו‪.‬‬
‫אין צורך באתחול תור הסמפור שכן הדבר נעשה בזמן אתחול‬
‫המערכת‪.‬‬
‫‪40‬‬
‫מה עושה ‪s_delete‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫בודקת שהסמפור חוקי ואינו כבר משוחרר‬
‫מסמנת בטבלת הסמפורים שהסמפור משוחרר‬
‫עוברת על תור הממתינים לסמפור ומעבירה אותם לתור ה‪ready -‬‬
‫קוראת ל‪resched -‬‬
‫‪41‬‬
‫מה עושה ‪signal_n‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪ signal_n‬מקבלת מזהה סמפור ומספר ‪ n‬ופעולתה שקולה‬
‫לקריאה ל‪ n signal -‬פעמים‬
‫קריאה ל‪ signal_n -‬יותר יעילה מ ‪ n -‬קריאות ל ‪ signal‬מכיוון‬
‫שהיא מבצעת ‪ resched‬רק פעם אחת‪.‬‬
‫‪ signal_n‬מעלה את המונה ב‪ n -‬ומכניסה את ‪ n‬התהליכים‬
‫הראשונים בתור הממתינים (או את כל הממתינים אם מספרם קטן מ‪-‬‬
‫‪ )n‬לתור ה‪ready -‬‬
‫קוראת ל‪.resched -‬‬
‫‪42‬‬
‫מה עושה ‪s_reset‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫לעתים רוצים לשנות ערך של סמפור לערך חיובי כלשהו‬
‫‪ ‬לא ניתן לשנות אותו לערך שלילי כיוון שערך שלילי אמור לשקף‬
‫את מספר הממתינים על הסמפור‬
‫דרך אחת לעשות זאת הינה ע"י קריאה ל‪ s_delete -‬מלווה‬
‫בקריאה ל‪s_create -‬‬
‫הפונקציה ‪ s_reset‬מבצעת פעולה שקולה בצורה יעילה יותר‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪43‬‬
‫הפונקציה מעבירה את כל הממתינים מתור הסמפור לתור ה‪-‬‬
‫‪ready‬‬
‫מונה הסמפור מעודכן לערכו החדש‪.‬‬
‫הפונקציה קוראת ל‪.resched -‬‬
‫דוגמא‬
‫‪‬‬
‫‪‬‬
‫תור משותף בין כמה תהליכים‪:‬‬
‫נכניס ונוציא איברים מהתור ע"י קריאה לפונקציות ‪ insert‬ו‪-‬‬
‫‪.remove‬‬
‫‪44‬‬
:‫נכתוב פונקציות גישה חדשות‬
Struct safeQ {
Sem sem;
element *head, *tail;
}
safeQ_init(struct safeQ sq) {
sq->sem = s_create(1);
sq->head = sq->tail = NULL;
}
safeQ_insert_first(struct safeQ *sq, element e) {
wait(sq->sem);
insert(sq->head, e);
signal(sq->sem);
}
safeQ_remove_last(struct safeQ *sq, element e) {
wait(sq->sem);
remove(sq->tail);
signal(sq->sem);
}
45