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