Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

מערכות הפעלה תרגול 9 – סינכרוניזציה ב-Linux. 2 (c) ארז חדד 2003 תוכן התרגול מבוא לסינכרוניזציה ב-Linux סנכרון בין חוטי POSIX  mutex  סמפורים  condition.

Podobné prezentace


Prezentace na téma: "מערכות הפעלה תרגול 9 – סינכרוניזציה ב-Linux. 2 (c) ארז חדד 2003 תוכן התרגול מבוא לסינכרוניזציה ב-Linux סנכרון בין חוטי POSIX  mutex  סמפורים  condition."— Transkript prezentace:

1 מערכות הפעלה תרגול 9 – סינכרוניזציה ב-Linux

2 2 (c) ארז חדד 2003 תוכן התרגול מבוא לסינכרוניזציה ב-Linux סנכרון בין חוטי POSIX  mutex  סמפורים  condition variable דוגמה: מימוש מנעול קוראים-כותבים.

3 תרגול 9 – סינכרוניזציה ב-Linux3 (c) ארז חדד 2003 מבוא לסינכרוניזציה ב-Linux הצורך בסנכרון הקשרי ביצוע שונים (כגון חוטים ותהליכים) הינו חיוני בכל מערכת הפעלה. Linux מספקת מגוון רחב של מנגנונים לסנכרון בין חוטים ותהליכים, הן כשירות לקוד משתמש והן לצורך שימוש עצמי בספריות ובגרעין. בתרגול זה נציג את עיקר מנגנוני הסנכרון עבור חוטים ברמת המשתמש (API).

4 תרגול 9 – סינכרוניזציה ב-Linux4 (c) ארז חדד 2003 סנכרון בין חוטי POSIX ב-Linux Linux Threads תומכת בחלק ממנגנוני הסנכרון המוגדרים ב-POSIX  mutexes  סמפורים (POSIX b)...רק עבור חוטים (לא בין תהליכים)  condition variables  תמיכה ב-monitors  אין תמיכה במנעולי קוראים-כותבים...אבל ניתן לממש מנעולים כאלה, כפי שנראה בהמשך

5 תרגול 9 – סינכרוניזציה ב-Linux5 (c) ארז חדד 2003 מנעולי mutex מנעול mutex מאפשר לחוט אחד בדיוק להחזיק בו (לנעול אותו).  כל חוט אחר המבקש להחזיק במנעול ייחסם עד אשר המנעול ישוחרר  רק החוט המחזיק במנעול אמור לשחרר אותו (בעלות על המנעול) מנעולי mutex משמשים בדרך-כלל להגנה על גישה לנתונים משותפים, בתוך קטע קוד קריטי, ע"י נעילת המנעול בכניסה לקטע הקריטי ושחרורו בסופו. סנכרון בין חוטי POSIX ב-Linux

6 תרגול 9 – סינכרוניזציה ב-Linux6 (c) ארז חדד 2003 פעולות על מנעולי mutex (1) #include אתחול mutex לפני השימוש: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr); פינוי mutex בתום השימוש int pthread_mutex_destroy(pthread_mutex_t *mutex);  הפעולה נכשלת אם ה-mutex מאותחל, אבל נעול סנכרון בין חוטי POSIX ב-Linux

7 תרגול 9 – סינכרוניזציה ב-Linux7 (c) ארז חדד 2003 פעולות על מנעולי mutex (2) נעילת mutex: int pthread_mutex_lock(pthread_mutex_t *mutex);  הפעולה חוסמת עד שה-mutex מתפנה ואז נועלת אותו נסיון לנעילת mutex: int pthread_mutex_trylock(pthread_mutex_t *mutex);  הפעולה נכשלת אם ה-mutex כבר נעול, אחרת נועלת אותו. שחרור mutex נעול: int pthread_mutex_unlock(pthread_mutex_t *mutex); סנכרון בין חוטי POSIX ב-Linux

8 תרגול 9 – סינכרוניזציה ב-Linux8 (c) ארז חדד 2003 פעולות על מנעולי mutex (3) פרמטרים:  mutex – המנעול עליו מבוצעת הפעולה  mutexattr – מגדיר את תכונות ה-mutex ב-Linux Threads מוגדרת רק תכונת "סוג" ה-mutex, המתבטאת בהתנהגות ה-mutex בנעילה ובשחרור, כמוצג בשקף הבא ברירת המחדל – NULL – mutex מסוג "מהיר" מומלץ לעבוד עם mutex מסוג "בודק שגיאות", כדי למנוע מצבים בעייתיים כגון אלו המסומנים באדום בשקף הבא פרטים כיצד לבחור את סוג ה-mutex ב-man pages ערך מוחזר: 0 בהצלחה, ערך אחר בכישלון סנכרון בין חוטי POSIX ב-Linux

9 תרגול 9 – סינכרוניזציה ב-Linux9 (c) ארז חדד 2003 סוגים של מנעולי mutex סוג ה-mutexנעילה חוזרת ע"י החוט המחזיק במנעול שחרור מנעול ע"י חוט שאינו מחזיק במנעול שחרור מנעול ע"י החוט המחזיק במנעול mutex מהירDEADLOCKמותר mutex רקורסיבימגדיל מונה נעילה עצמית ב-1 מותרמקטין מונה נעילה עצמית ב-1. ה- mutex נשאר נעול עד שהמונה מגיע ל-0 mutex בודק שגיאות כשלון מותר סנכרון בין חוטי POSIX ב-Linux

10 תרגול 9 – סינכרוניזציה ב-Linux10 (c) ארז חדד 2003 דוגמה: מנעולי mutex pthread_mutex_t m; int count; void update_count() { pthread_mutex_lock(&m); count = count * 5 + 2; pthread_mutex_unlock(&m); } int get_count() { int c; pthread_mutex_lock(&m); c = count; pthread_mutex_unlock(&m); return c; } 1.מדוע צריך להגן על הגישה ל-count בתוך update_count()? כדי למנוע שיבוש ערך count בעדכונים מחוטים שונים. 2.מדוע צריך להגן על הגישה ל-count בתוך get_count()? כדי למנוע קבלת תוצאות חלקיות הנוצרות במהלך העדכון שימו לב! גם אם ביטוי ההגדלה היה count++, לא מובטח שהקוד הנפרש באסמבלר הינו אטומי, ולכן יש להפעיל מנגנון סנכרון לפי הצורך. סנכרון בין חוטי POSIX ב-Linux להלן דוגמה המשתמשת ב-mutex:

11 תרגול 9 – סינכרוניזציה ב-Linux11 (c) ארז חדד 2003 סמפורים כלליים סמפור ממומש ב-Linux Threads בערך כמו שתואר בהרצאה:  לכל סמפור יש מונה, תור ומנעול שתפקידו להגן על הקוד של הפעולות של הסמפור (  פעולות אטומיות) שתי פעולות בסיסיות על הסמפור:  wait: אם המונה גדול מ-0, מקטינה אותו ב-1, אחרת החוט נכנס להמתנה בתור. בהרצאה הקטנו גם אם המונה קטן מ-0.  post: אם תור הממתינים לא ריק, מוציאה ומעירה את החוט הראשון בתור, אחרת מגדילה את המונה ב-1 בהרצאה קראנו ל-post בשם signal. תור הממתינים הוגן (FIFO) לפי עדיפויות, כלומר חוט בעדיפות גבוהה יצא מהתור לפני חוט בעדיפות נמוכה סנכרון בין חוטי POSIX ב-Linux

12 תרגול 9 – סינכרוניזציה ב-Linux12 (c) ארז חדד 2003 פעולות על סמפורים (1) #include  לא לשכוח לקשר לספרייה pthread! אתחול סמפור לפני השימוש: int sem_init(sem_t *sem, int pshared, unsigned int value); פינוי סמפור בתום השימוש: int sem_destroy(sem_t *sem); סנכרון בין חוטי POSIX ב-Linux

13 תרגול 9 – סינכרוניזציה ב-Linux13 (c) ארז חדד 2003 פעולות על סמפורים (2) ביצוע wait על סמפור: int sem_wait(sem_t *sem); ביצוע post על סמפור: int sem_post(sem_t *sem); גרסה לא-חוסמת של wait: int sem_trywait(sem_t *sem);  אם המונה של הסמפור אינו גדול מ-0, חוזרת מיד ונכשלת קריאת ערך מונה הסמפור: int sem_getvalue(sem_t *sem, int *sval); סנכרון בין חוטי POSIX ב-Linux

14 תרגול 9 – סינכרוניזציה ב-Linux14 (c) ארז חדד 2003 פעולות על סמפורים (3) פרמטרים:  sem – הסמפור עליו מבוצעות הפעולות.  pshared – אם ערכו גדול מ-0, מציין שהסמפור יכול להיות משותף למספר תהליכים. תכונה זו אינה נתמכת, ולכן נציב בו 0 קבוע.  value – ערכו ההתחלתי של מונה הסמפור.  sval – מצביע למקום בו יאוחסן ערך מונה הסמפור. ערך מוחזר: 0 בהצלחה, (1-) בכישלון  הפונקציות sem_wait() ו-sem_getvalue() תמיד מצליחות. סנכרון בין חוטי POSIX ב-Linux

15 תרגול 9 – סינכרוניזציה ב-Linux15 (c) ארז חדד 2003 הערות על סמפורים סמפור המאותחל עם מונה בערך 1 נקרא סמפור בינארי. סמפור בינארי יכול לשמש להגנה על קטע קריטי (ליצור מניעה הדדית בין החוטים הניגשים). סמפור בינארי אינו מנעול mutex, משום שכל חוט יכול לבצע post על סמפור, גם אם לא ביצע wait על הסמפור קודם לכן (אין "בעלות" על הסמפור). סנכרון בין חוטי POSIX ב-Linux

16 תרגול 9 – סינכרוניזציה ב-Linux16 (c) ארז חדד 2003 משתני תנאי נתבונן בבעיית מימוש תור. נרצה לסנכרן את פעולות ההכנסה וההוצאה מהתור, כך שחוט יוכל להוציא איבר מתור רק אם התור אינו ריק. בבעיה זו יש צורך לסנכרן בין חוטים המבצעים הוצאת והכנסת איברים כדי לשמור על תקינות מבנה הנתונים, וכן להמתין לאירוע כדי למנוע הוצאת איבר מתור ריק. משתנה תנאי (condition variable) – מאפשר לחוט להמתין לאירוע כלשהו, כפי שמוגדר ע"י המתכנת. משתנה תנאי תמיד קשור ל-mutex מסוים. mutex + 0 או יותר משתני תנאי ≈ מוניטור. סנכרון בין חוטי POSIX ב-Linux

17 תרגול 9 – סינכרוניזציה ב-Linux17 (c) ארז חדד 2003 פעולות על משתני תנאי (1) #include איתחול משתנה תנאי לפני השימוש: int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); פינוי משתנה תנאי בסיום השימוש: int pthread_cond_destroy(pthread_cond_t *cond);  הפעולה נכשלת אם יש חוטים הממתינים על משתנה התנאי סנכרון בין חוטי POSIX ב-Linux

18 תרגול 9 – סינכרוניזציה ב-Linux18 (c) ארז חדד 2003 פעולות על משתני תנאי (2) המתנה על משתנה תנאי: int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);  החוט הממתין חייב להחזיק במנעול mutex לפני הקריאה  פעולה אטומית: משחררת את המנעול mutex ומעבירה את החוט להמתין על משתנה התנאי סנכרון בין חוטי POSIX ב-Linux

19 תרגול 9 – סינכרוניזציה ב-Linux19 (c) ארז חדד 2003 פעולות על משתני תנאי (3) שחרור חוט ממתין על משתנה תנאי: int pthread_cond_signal(pthread_cond_t *cond);  אם אין חוט שממתין כרגע על משתנה התנאי cond – הפעולה חסרת השפעה (ואינה נזכרת הלאה).  אם קיימים חוטים ממתינים על משתנה התנאי cond – אחד החוטים הממתינים (הגינות לא מובטחת) מפסיק להמתין על משתנה התנאי.החוט הנ"ל עובר להמתין על ה-mutex ויחזור לפעילות (יעבור את ה- pthread_cond_wait()) לאחר שינעל מחדש את ה-mutex (סגנון Mesa). סנכרון בין חוטי POSIX ב-Linux

20 תרגול 9 – סינכרוניזציה ב-Linux20 (c) ארז חדד 2003 פעולות על משתני תנאי (4) שחרור כל החוטים הממתינים על משתנה תנאי: int pthread_cond_broadcast(pthread_cond_t *cond);  אם אין חוט שממתין כרגע על משתנה התנאי cond – הפעולה חסרת השפעה (ואינה נזכרת הלאה).  אם קיימים חוטים ממתינים על משתנה התנאי cond – כל החוטים הממתינים מפסיקים להמתין על משתנה התנאי ועוברים להמתין על ה-mutex.החוטים יחזרו לפעילות בזה אחר זה (בסדר כלשהו, לאו דווקא הוגן) לאחר שינעלו מחדש את ה-mutex. סנכרון בין חוטי POSIX ב-Linux

21 תרגול 9 – סינכרוניזציה ב-Linux21 (c) ארז חדד 2003 פעולות על משתני תנאי (5) פרמטרים:  cond – משתנה התנאי עליו מבוצעת הפעולה  cond_attr – מגדיר את תכונות משתנה התנאי ב-Linux Threads אין משמעות לפרמטר זה. ערך NULL  mutex – המנעול אליו משויך משתנה התנאי בביצוע pthread_cond_wait() ערך מוחזר: 0 בהצלחה, ערך אחר בכישלון  הפונקציות init, signal, broadcast, wait תמיד מחזירות ערך הצלחה. סנכרון בין חוטי POSIX ב-Linux

22 תרגול 9 – סינכרוניזציה ב-Linux22 (c) ארז חדד 2003 קישור בין אירוע ומשתנה תנאי (1) הדרך הפשוטה ביותר לקשר בין אירוע המציין קיום מצב רצוי כלשהו למשתנה תנאי היא בקוד כדלקמן:  אצל הממתין לאירוע (החוט שרוצה להתקדם רק כאשר יושג המצב הרצוי): if pthread_cond_wait(, );  אצל יוצר האירוע (החוט שיוצר את המצב הרצוי ומסמן לחוטים הממתינים להתקדם): if אם זה ודאי ifאפשר לוותר על ה- pthread_cond_signal( );

23 תרגול 9 – סינכרוניזציה ב-Linux23 (c) ארז חדד 2003 קישור בין אירוע ומשתנה תנאי (2) נתבונן במימוש של תור כפי שהופיע בהרצאה pthread_mutex_t qlock; pthread_cond_t notEmpty; /* … initialization code … */ void enqueue(item x) { pthread_mutex_lock(&qlock); /* … add x to queue … */ pthread_cond_signal(¬Empty); pthread_mutex_unlock(&qlock); } item dequeue() { pthread_mutex_lock(&qlock); if pthread_cond_wait(¬Empty,&qlock); /* … remove item from queue … */ pthread_mutex_unlock(&qlock); /*.. return removed item */ } האם מימוש זה פותר את הבעיה ? לא ! לפי המימוש הנ"ל יתכן מצב של הוצאת איבר מתור ריק. באו נראה איך...

24 תרגול 9 – סינכרוניזציה ב-Linux24 (c) ארז חדד 2003 קישור בין אירוע ומשתנה תנאי (3) פעולת signal לא מאפשרת לחוט הממתין להמשיך מיד. החוט הממתין צריך תחילה לנעול את ה-mutex מחדש (סגנון Mesa). ייתכן שלפני שהחוט הממתין ינעל את ה-mutex מחדש יספיק להכנס חוט נוסף שישנה את הנתונים כך שהמצב הרצוי כבר לא מתקיים.

25 תרגול 9 – סינכרוניזציה ב-Linux25 (c) ארז חדד 2003 קישור בין אירוע ומשתנה תנאי (4) נניח, לפי הדוגמה שהתור ריק וחוט t1 קורא ל-dequeue(), ולכן ממתין כעת חוט t2 קורא ל-enqueue(), מכניס איבר לתור ומבצע signal  חוט t1 עובר להמתין לשחרור המנעול כעת מגיע חוט t3, וקורא ל-dequeue()  חוט t3 נחסם בהמתנה למנעול בתחילת הקוד חוט t2 משחרר את המנעול ומסיים את enqueue() חוט t3 מקבל את המנעול, נכנס, מוציא איבר ומסיים חוט t1 מקבל את המנעול, ממשיך לבצע את הקוד ומנסה להוציא איבר מתור ריק!

26 תרגול 9 – סינכרוניזציה ב-Linux26 (c) ארז חדד 2003 קישור בין אירוע ומשתנה תנאי (5) ממה נבעה הבעיה בדוגמה?  מכך שהתנאי של האירוע לא נבדק אחרי ה-wait כיצד ניתן לפתור את הבעיה?  ע"י בדיקה חוזרת של תנאי האירוע והמתנה נוספת לפי הצורך. לדוגמה: while pthread_cond_wait(…) האם הבעיה יכלה לקרות גם אם המנעול היה הוגן (תור FIFO)?  כן! אם t3 היה קורא ל-dequeue() ומחכה למנעול לפני ש-t2 הגיע ל-signal

27 תרגול 9 – סינכרוניזציה ב-Linux27 (c) ארז חדד 2003 מימוש מוניטור בנוסח Hoare במימוש לפי סגנון Hoare (נדיר מאוד), החוט שמבצע signal גם מוותר על המנעול ומעביר אותו לחוט הממתין.  באופן זה מצב הנתונים לא משתנה, והחוט הממתין ממשיך ללא הפרעה במצב הרצוי. מצד שני, בסגנון Hoare החוט שביצע signal נענש על כך שסייע לחוט אחר להתקדם  מוותר על ההתקדמות שלו וממתין עד שהמנעול משוחרר. האם הבעיה שראינו בדוגמה של התור יכולה לקרות אם ה-monitor היה בסגנון Hoare?  לא! כי t1 היה מקבל את המנעול לפני t3

28 תרגול 9 – סינכרוניזציה ב-Linux28 (c) ארז חדד 2003 מנעול קוראים-כותבים משאב עם שתי פעולות: קריאה וכתיבה  מספר חוטים קוראים יכולים לגשת למשאב בו-זמנית  חוט כותב צריך גישה בלעדית למשאב בשקפים הבאים נדגים כיצד ניתן לממש מנעול קוראים-כותבים באמצעות monitor  בהרצאה ראינו איך להשתמש בסמפור למטרה זו. סנכרון בין חוטי POSIX ב-Linux

29 תרגול 9 – סינכרוניזציה ב-Linux29 (c) ארז חדד 2003 מימוש מנעול קוראים-כותבים (1) הגדרת המנגנון: #include int number_of_readers; pthread_cond_t readers_condition; int number_of_writers; pthread_cond_t writers_condition; mutex_t global_lock; void readers_writers_init() { number_of_readers = 0; pthread_cond_init(&readers_condition, NULL); number_of_writers = 0; pthread_cond_init(&writers_condition, NULL); pthread_mutex_init(&global_lock, NULL); } מספר החוטים שקוראים מהמשאב מספר החוטים שכותבים למשאב אירוע: מותר לקרוא אירוע: מותר לכתוב סנכרון בין חוטי POSIX ב-Linux

30 תרגול 9 – סינכרוניזציה ב-Linux30 (c) ארז חדד 2003 מימוש מנעול קוראים-כותבים (2) void read_lock() { pthread_mutex_lock(&global_lock); while (number_of_writers > 0) pthread_cond_wait(&readers_condition, &global_lock); number_of_readers++; pthread_mutex_unlock(&global_lock); } void read_unlock() { pthread_mutex_lock(&global_lock); number_of_readers--; if (number_of_readers == 0) pthread_cond_signal(&writers_condition); pthread_mutex_unlock(&global_lock); } סנכרון בין חוטי POSIX ב-Linux צריך לבדוק את תנאי הבדיקה גם לאחר wait 1

31 תרגול 9 – סינכרוניזציה ב-Linux31 (c) ארז חדד 2003 מימוש מנעול קוראים-כותבים (3) void write_lock() { pthread_mutex_lock(&global_lock); while ((number_of_writers > 0) || (number_of_readers > 0)) pthread_cond_wait(&writers_condition, &global_lock); number_of_writers++; pthread_mutex_unlock(&global_lock); } void write_unlock() { pthread_mutex_lock(&global_lock); number_of_writers--; if (number_of_writers == 0) { pthread_cond_broadcast(&readers_condition); pthread_cond_signal(&writers_condition); } pthread_mutex_unlock(&global_lock); } למעשה, אין צורך ב-if.. עדיפות לקוראים ממתינים על-פני כותבים ממתינים? סנכרון בין חוטי POSIX ב-Linux צריך לבדוק את תנאי הבדיקה גם לאחר wait 2

32 תרגול 9 – סינכרוניזציה ב-Linux32 (c) ארז חדד 2003 חסרונות של המימוש כל עוד המנעול אצל הקוראים, קורא חדש שמגיע יצליח להיכנס ויעקוף כותבים שהגיעו לפניו. חוסר הוגנות והרעבה אפשרית של הכותבים. לא ניתן לדעת האם הקוראים או הכותב יכנסו לקטע הקריטי תלוי מי יצליח לתפוס ראשון את המנעול. האם ואיך אפשר לפתור בעיות אלו? 1 2


Stáhnout ppt "מערכות הפעלה תרגול 9 – סינכרוניזציה ב-Linux. 2 (c) ארז חדד 2003 תוכן התרגול מבוא לסינכרוניזציה ב-Linux סנכרון בין חוטי POSIX  mutex  סמפורים  condition."

Podobné prezentace


Reklamy Google