שבוע #10 פרקThreads : חלק א קורס מערכות הפעלה א' מכללת הדסה /מכללה חרדית צבי מלמד Tzvi.Melamed@gmail.com הרצאות הקורס מבוססות במידה רבה ביותר על ההרצאות של ד"ר יורם ביברמן © כל הזכויות שמורות לד"ר יורם ביברמן ולצבי מלמד ©צבי מלמד 1 למה ) ? Threadsמוטיבציה( • נניח שעלינו לכתוב תכנית המבצעת מספר פעילויות במקביל: – קוראת קלט מהמקלדת ,מהעכבר ,ממספר קבצים ,מהרשת )ממספר שרתים שונים במקביל(; מציגה תמונה על המסך ,וכן הלאה. • הרצתה של התכנית כתהליך יחיד תפגע ביכולתה של התכנית לבצע את המשימות הללו במקביל: – המתנה לקלט )או לסיום פעולת פלט( מהתקן כלשהו -תכנית חסומה )(blocked – אינה יכולה בינתיים לבצע פעולה אחרת ©צבי מלמד 2 פתרון אפשרי :ריבוי תהליכים • הצורך אם כן: – מקביליות רבה של התוכנית – שחסימה/המתנה לא תעצור את המשך הפעילות • פתרון אפשרי: – האפליקציה )תכנית( שלנו תיצור מספר תהליכים – כל-אחד מהם יהיה אחראי על תת-משימה אחרת של התכנית. – כאשר אחד התהליכים ממתין\חסום האחרים יכולים להמשיך בפעולתם. ©צבי מלמד 3 מגבלות הפתרון .1חוסר יכולת לחלוק מידע "בקלות ובטבעיות": – בהנחה שהתהליכים השונים מבצעים משימה שלמה אחת יהיה עליהם לחלוק מידע. – כלים שראינו לשיתוף מידע בין תהליכים – לכל אחד המגבלה שלו – במקום שצריכים קריאות מערכת – האטה בתוכנית .2ייצור תהליך – פעולה ארוכה ויקרה .3בזבוז זיכרון – לתהליכים שמיוצרים אותו קטע קוד )שמבצע פעולות דומות עבור תהליכים שונים( .4הצפת המערכת בתהליכים – לדוגמא ,אם התכנית שלנו היא שרת שמייצר מספר רב-מאוד של תהליכים -אזי הבזבוז בסעיפים ) (2ו (3) -יגדל ©צבי מלמד 4 • מסקנה :נשמח לפתרון פחות יקר. • התשובה לצורך הזה: – פתיל או חוט – Thread – נקרא גם תהליכון או ).lightweight process (LWP – קיים בכל מערכות ההפעלה המודרניות ) Linux, Unix, )… (Windows, Mac OS X, iOS, ©צבי מלמד 5 – Threadsהרעיון הכללי • תהליך יחיד: – מרחב כתובות יחיד, – מוקצה לו PCBיחיד, – )עד כאן כרגיל -כפי שהיכרנו( – יוכל להריץ פונקציות שונות )של אותה תוכנית( במקביל זו לזו. – "הדברים האלו" שרצים במקביל – )בדומה לתהליכים שרצים במקביל( – נקראים Threadsאו "חוטים" • כלומר :התהליך )או ליתר דיוק :החוט הראשי בתהליך( יכול לזמן פונקציה )( – fאבל ,לא להמתין לסיומה )עד לחזרה ממנה( אלא להמשיך בביצוע הפקודה הבאה לאחר הזימון ,במקביל לביצוע של הפונקציה )(f ©צבי מלמד 6 – Threadsהרעיון הכללי • כמובן ,שאם המחשב שלנו כולל מעבד יחיד אזי המקביליות היא רק במובנים: – החוטים השונים רצים במקביל ,כשם שתהליכים רצים במקביל – זאת אומרת ,בפועל רצים סדרתית ,אבל הזמן מתחלק ביניהם – חסימת הפתיל שמבצע את )( fאינה צריכה לגרום לחסימתו של הפתיל הראשי. – אם המחשב שלנו כולל מספר מעבדים המקביליות תוכל להיות 'אמיתית' )בדרגה של מספר המעבדים(. • כלל החוטים שמרכיבים אפליקציה= .Thread Group ©צבי מלמד 7 – Threadsהרעיון הכללי • התוצאה :על אותם נתונים – אותו מרחב כתובות – מקטע קוד – מקטע נתונים=משתנים גלובליים – קבצים פתוחים ,טפלי סיגנלים וכו' • מתבצעות במקביל סדרות פעולות שונות )כלומר מורצות פונקציות שונות(. • כל סדרה מהווה .thread ©צבי מלמד 8 ) processתזכורת( ©צבי מלמד 9 process and threads כדי שכל פתיל יוכל לבצע את הפונקציה 'עליה הוא מופקד' מוקצה לכל פתיל PCמשלו ,אזור משלו על המחסנית התהליך )קטע בו יישמרו פרמטרי הפונקציה ,המשתנים הלוקליים ,וכל המידע שנשמר בעת זימון פונקציה .המידע אודות ה Threads-נשמר במבנהThread : Control Block ©צבי מלמד 10 שימוש בפתילים – יתרונות וחסרונות יתרונות • מהירות יצירת Threads • מהירות החלפת הקשר • תקשורת מהירה יותר בין threads • תקשורת קלה יותר )הזכרון משותף( – גישה למשתנים גלובליים – גישה לקבצים – כולם משתמשים באותה טבלת מתארי הקבצים חסרונות • ללא ההגנות שמערכת ההפעלה מספקת בין תהליכים )הפרדה מוחלטת של מרחב הזיכרון( – פגיעות רבה :ניתן "בקלות" לקלקל את תוכן הזיכרון • לכן יש להגן על הפתילים – אחד מפני משנהו • פתילים של אותו תהליך חייבים לרוץ על אותה מכונה ©צבי מלמד 11 process מולThread השוואת זמנים – יצירת לא להתעמק המספרים האלו הם ,רק לסבר את העין לגבי ההבדלים בזמנים Note: don't expect the system and user times to add up to real time, because these are SMP systems with multiple CPUs working on the problem at the same time . (SMP: Symmetric Multi-Processor systems – ) מחשב מרובה מעבדים 12 ©צבי מלמד שימוש בפתילים – אתגרים נוספים כמו בכל תיכנות מקבילי: • יש חשש ממצב תחרות ) :(race conditionתוצאת ריצתה של התכנית תקבע )גם( על-פי האופן המקרי בו מערכת ההפעלה מזמנת את הפתילים השונים ,ועל-כן עלולה להשתנות מהרצה להרצה ,או לא להיות בהתאם למצופה\נדרש )דוגמאות בהמשך(. ©צבי מלמד 13 User Threads, Kernel Threads • קיימת אבחנה בין: – – user threadsאינם מוכרים למערכת ההפעלה – - kernel threadsמוכרים לגרעין )נוצרים על ידי קריאות מערכת(. • user threads – כשתכניתנו מייצרת User-Threadהיא מזמנת פונקצית ספריה במרחב המשתמש ,שמייצרת threadשאינו מוכר למערכת ההפעלה. – מבחינת מערכת ההפעלה :התוכנית )ליתר דיוק :תהליך( הוא אחד ..רצף אחד של קוד הרצה. • אם כך – כיצד קוראת המקביליות? ©צבי מלמד 14 User Threads, Kernel Threads • אם כך – כיצד קוראת המקביליות? – אנו משתמשים בספרית פתילים – thread library – ספרית הפתילים לוקחת על עצמה תפקיד שדומה במקצת )רק במקצת( למערכת ההפעלה .היא מחלקת את הזמן )שמוקצה לכל התהליך( לחריצי זמן time-slotsשמוקצים לפתילים השונים – בדומה ל'החלפת הקשר' ) (context switchשל תהליכים – מתקיימת החלפת הקשר של פתילים ...אבל – החלפת ההקשר בין הפתילים שקופה לגרעין ,הינה באחריות הספרייה ,אשר מממשת בעצמה את הפעולה: • שמירת בזכרון של מצבו של פתיל א' שמופסק לרוץ )מצבו :תוכן האוגרים( • וטעינת המצב בו הופסק פתיל ב' למעבד לשם הרצתו ©צבי מלמד 15 משמעויות של user threads .1כאשר אחד הפתילים בתכניתנו מזמן קריאת מערכת חוסמת )לדוגמה :כדי לקרוא נתון( ,מערכת ההפעלה ,שאינה מודעת לכך שתכניתנו מורכבת ממספר פתילים ,חוסמת את )כלל( התהליך, וגם פתילים אחרים בתכנית לא יוכלו להמשיך בפעולתם. .2במערכת רבת מעבדים ,פתילים שונים הנכללים באותה תכנית לא יוכלו לרוץ במקביל. .3ומנגד :כפי שציינו ,פעולת ייצור פתיל ,או החלפת הקשר בין פתילים ,אינן מצריכות שרות של מערכת ההפעלה על כן הינן מהירות מאוד באופן יחסי. ©צבי מלמד 16 משמעויות של user threads .4לפחות לכאורה ,ספריית הפתילים עשויה לחלק את חריץ הזמן בין הפתילים השונים הנכללים בתהליך בצורה מותאמת יותר מאשר מערכת ההפעלה .5ספריית פתילי משתמש אינה בהכרח תלוית מערכת ההפעלה ולכן קל יותר לעשות ) portingהמרה למכונה או למערכת הפעלה אחרת( של קוד שנכתב באמצעות ספריית הפתילים ©צבי מלמד 17 פתילי גרעין לעומת פתילי משתמש • פתילי גרעין הם החלופה האחרת :הם נתמכים ע"י גרעין מערכת ההפעלה אשר מייצר ,מתזמן ,ומנהל אותם. • כפי שכבר אמרנו הם איטיים יותר ביצירה ובניהול )שכן מחייבים קבלת שירות מהגרעין( ,אבל :חסימת פתיל א' של האפליקציה אינה מונעת מפתיל ב' להמשיך לרוץ ,ואם המחשב כולל מספר מעבדים ניתן להריץ פתילים שונים במקביל. • המונח lightweight processמתייחס בדרך כלל לפתילי גרעין דווקא. ©צבי מלמד 18 מודלים של ריבוי פתילים • שימוש ב user threads-נקרא -מודל של רבים ליחיד )Many-to- :(One Model – פתילי משתמש רבים בה ממופים לפתיל גרעין יחיד • כאשר כל פתיל משתמש ממופה לפתיל גרעין יחיד :מוד יחיד-ליחיד ),(One-to-One Model • מודל של רבים לרבים ) .(many-to-Many Modelעל-פי מודל זה n פתילי משתמש ממופים ל (n ≥) m :פתילי גרעין m) .עשוי להיות תלוי מכונה ,או תלוי אפליקציה(. ©צבי מלמד 19 מודלים של ריבוי פתילים • היתרון והרעיון במודל many-to-many • מצד אחד :חסימת פתיל כלשהו באפליקציה לא תחסום אותה לחלוטין m-1 ,פתילים אחרים בה יוכלו לרוץ; • מצד שני :גם אם האפליקציה תייצר שפע של פתילים הדבר לא 'יציף' את הגרעין שמגביל את מספר הפתילים מהאפליקציה אליהם הוא מוכן להתייחס • )בפרק #10נלמד מדוע הגרעין רוצה להגן על עצמו מפני 'הצפה' שכזאת ,במילים אחרות :מה רע בכך שהמערכת תריץ מספר רב של פתילים?( ©צבי מלמד 20 - Threadsהמשך ...חלק ב' יום א'15.1.2012 , ©צבי מלמד 21 חזרה על פתילים – עד כה • מוטיבציה – – הרבה פעילויות במקביל – מניעת חסימה )פעולה חוסמת נקודתית ,ולא את "השאר"( – חסכון בתקורה שנובעת מריבוי תהליכים – שיתוף נתונים • פתרון: – "תהליך קל" LWP = Light Weight Process – אם יהיה לנו עותק של רגיסטרים ומחסנית )רגיסטרים כוללים גם – (IPאזי ניתן לקיים רצף-ביצועים ,חוט-ביצועים נוסף – שאר המשאבים של התהליך – משותפים )זיכרון ,טבלת קבצים, ,PCBוכו'( ©צבי מלמד 22 חזרה על פתילים – עד כה ©צבי מלמד 23 חזרה על פתילים – עד כה • כאשר ביצענו )( forkמה שקרה – התהליך שוכפל במלואו ,כולל כל משאביו – גם האב וגם הבן המשיכו בביצוע "כרגיל" – כלומר שניהם בצעו את הפקודה הבאה אחרי הfork()- – כיצד נקבע שזאת הפקודה שתתבצע? כי שכפלנו גם את ה- (insruction pointer) IP • יצירת :threadקריאה לפונקציה )( pthread_createכאשר – מעבירים לה )בין היתר(: א -מצביע לפונקציה לביצוע )שצריכה להיות בפרוטוטייפ מסוים( ב -פרמטר לאותה פונקציה )מטיפוס * (void ג -ה thread-החדש יתחיל את חייו בפונקציה הזאת ©צבי מלמד 24 חזרה על פתילים – עד כה • סיום הביצוע של תהליך: – הפונקציה )(exit – returnמהmain - – הורגים את התהליך מבחוץ • סיום הביצוע של :thread א -הפונקציה )(pthread_exit ב – thread-cancellation -פתיל אחד מצווה על השני להסתיים ג return -מהפונקציה שבה הפתיל התחיל את חייו • )( pthread_exitבתהליך עם פתיל יחיד – דינו כדין )(exit ©צבי מלמד 25 חזרה על פתילים user versus kernel threads – many to one 26 ©צבי מלמד חזרה על פתילים user versus kernel threads – one to one 27 ©צבי מלמד היבטים נוספים של Threads • בהמשך נדון בהיבטים נוספים של Threadsכגון: – – threads & forkמה קורה כשאחד הפתילים בתוכנית שלנו ביצע )(?fork – – threads & execמה קורה כשאחד הפתילים בתוכנית שלנו ביצע )(?exec – – thread cancellationביטול פתילים ,שהיא פעולה אנלוגית להריגת תהליך – טיפול בסיגנלים – – Thread Specific Dataהאם באמת כל המידע משותף ,או שאפשר )ורצוי( שחלק מהמידע יהיה פרטי לפתיל? – ועוד כהנה... • אבל לפני כן ..נראה כיצד דוגמא ראשונה של פתילים ©צבי מלמד 28 קפיצה ...נראה תוכנית ראשונה )ואולי גם יותר( ©צבי מלמד 29 POSIX Threads - Pthreads • pthreadהוא תקן של POSIXשמגדיר APIליצירת וסינכרון פתילים – אינו קובע כיצד ימומשו הפתילים – אינו קובע באיזה מידה הם יוכרו ע"י הגרעין – בלינוקס pthread -משמש ליצירת פתילי משתמש. • ה include -הדרוש: >#include <pthread.h • ופקודת הקומפילציה: gcc –Wall –o my_prog –l pthread my_prog.c ©צבי מלמד 30 כיצד )היכן( Threadמתחיל את חייו? • תזכורת :בפקודת )( - forkהתהליך החדש שנוצר מתחיל את ריצתו בפקודה העוקבת לפקודת הfork() - • ולהבדיל Thread :נוצר על ידי )(pthread_create – )( pthread_createמקבלת כארגומנט מצביע לפונקציה – ה thread-החדש מתחיל את ריצתו בפונקציה זאת – הארגומנטים לפונקציה הזאת? -גם כן מועברים ל- )(pthread_create ©צבי מלמד 31 צעדים ראשונים :ייצור וסיום פתיל טיפוס )מספר שלם או מבנה( שמכיל את מזהה הפתיל יחסית לתהליך )ולא כללית במערכת( ; pthread_t thread_data int a = 1, ; b = 2 ; int status status = pthread_create (&thread_data, דגלים המציינים כיצד יש לייצר NULL, את הפתיל .נהוג להעביר NULL בארגומנט זה ,כלומר לבחור my_func, במחדלים )=פתיל משתמש ,שניתן ;)(void *) &a להמתין לסיומו( { )if (status != 0 ; )"perror("pthread_create failed in main ; )exit(EXIT_FAILURE } ©צבי מלמד 32 צעדים ראשונים :ייצור וסיום פתיל מצביע לפונקציה שמהווה את קוד הפתיל .מכאן תתחיל ריצת הפתיל מחזירה אפס בהצלחה ,ערך שונה מאפס במקרה שקרתה שגיאה ; pthread_t thread_data int a = 1, ; b = 2 ; int status status = pthread_create (&thread_data, NULL, מצביע לארגומנט שמועבר my_func, לפונקציה my_func ;)(void *) &a )שמהווה את קוד הפתיל( { )if (status != 0 ; )"perror("pthread_create failed in main ; )exit(EXIT_FAILURE } ©צבי מלמד 33 צעדים ראשונים :ייצור וסיום פתיל • הפרמטר האחרון הוא מצביע מטיפוס * voidל ַארגומנטים ל ַפונקציה שמהווה את קוד הפתיל. • כלומר זאת חייבת להיות פונקציה שמקבלת ארגומנט יחיד - מצביע מטיפוס * void • הפונקציה מן הסתם ,תעשה לארוגמנט הזה castלטיפוס הרצוי לה • אם דרוש יותר מארגומנט אחד :יוצרים structאן מערך שמכיל את הארגומנטים ,ומעבירים לפונקציה מצביע אליו • בנוסף ,היא חייבת להיות פונקציה שמחזירה ערך מטיפוס * void • כלומר ,הפרוטוטייפ של הפונקציה הוא: )void *f (void *params ©צבי מלמד 34 מיד אחרי )(pthread_create • אחרי שבוצעה בהצלחה הקריאה ל,pthread_create() - בתכנית שלנו רצים במקביל שני פתילים: – פתיל ראשי ,הממשיך בביצוע ה) main -או הקוד שביצע באותו רגע( הפונ' שהורצה קודם לכן( ,ופתיל משני המריץ את .my_func – שני הפתילים נכללים באותו תהליך ולכן יש להם אותו ,pid – לכל אחד מהם יש מספר פתיל שונה )שאינו מוכר למערכת ההפעלה ,אלא רק לספריית הפתילים(. ©צבי מלמד 35 יציאה מהThread- • הפקודה: ; )pthread_exit(NULL – מסיימת את ביצוע הפתיל )שמריץ את הפקודה הזאת( – נכון גם עבור הפתיל הראשי – אם כל הפתילים בתכנית מבצעים פקודה זאת אזי התהליך מסתיים ,ערך ההחזרה שלו הוא אפס • הפונקציה הראשית של כל פתיל מחזירה בהכרח *,void ובפקודת ה pthread_exit -אנו מחזירים את המצביע הדרוש )בפרט NULLאם אין לנו מה להחזיר(. ©צבי מלמד 36 pthread1.c התוכנית // file: pthread1.c // a first thread example. // compile: gcc -Wall -lpthread pthread1.c #include #include #include #include #include <stdio.h> <stdlib.h> <pthread.h> <unistd.h> <time.h> void* my_func(void *) ; //----------------------------------------- 37 ©צבי מלמד pthread1.c התוכנית int main() { pthread_t thread_data ; int a = 1, b = 2 ; int status ; srand((unsigned)time(NULL)) ; ולא להשתמשfputs() status = pthread_create(&thread_data, ? perror() -ב NULL, my_func, void *) &a) ; if (status != 0) { fputs("pthread_create failed in main", stderr) ; exit(EXIT_FAILURE) ; למה { my_func( (void *) &b) ; puts("we do not reach this place") ; return(EXIT_SUCCESS) ; } 38 ©צבי מלמד למה לא מגיעים ? לכאן pthread1.c התוכנית void* my_func(void * args) { int i, sl=0; int * val = (int *) args ; - ב2 val ערכו של 1 - הראשי וthread שנוצרthread-ב for (i= 0; i< 5; i++) { printf("process = %d thread= %d (%u) i= %d (slept: %d)\n", getpid(),* val, (unsigned int) pthread_self(), i, sl) ; sleep(sl=rand()%8) ; } -שקול ל getpid() pthread_exit(NULL) ; } שני הפתילים יסתיימו כאן 39 ©צבי מלמד pthread1.c הרצת התוכנית 3:51:20pm-root: ~/workspace/os/thread > 3:51:31pm-root: ~/workspace/os/thread > gcc –Wall \ pthread1.c -lpthread 3:52:20pm-root: ~/workspace/os/thread > a.out process = 29190 thread= 2 (3086808768) i= 0 (slept: process = 29190 thread= 1 (3086805920) i= 0 (slept: process = 29190 thread= 1 (3086805920) i= 1 (slept: process = 29190 thread= 2 (3086808768) i= 1 (slept: process = 29190 thread= 2 (3086808768) i= 2 (slept: process = 29190 thread= 2 (3086808768) i= 3 (slept: process = 29190 thread= 1 (3086805920) i= 2 (slept: process = 29190 thread= 2 (3086808768) i= 4 (slept: process = 29190 thread= 1 (3086805920) i= 3 (slept: process = 29190 thread= 0 (3086805920) i= 4 (slept: 40 ©צבי מלמד 0) 0) 3) 7) 0) 2) 6) 5) 6) 7) הרצת התוכנית pthread1.c האם הכל נראה תקין? ) -רמז -התשובה חיובית ,לעת עתה( ©צבי מלמד 41 צביעת הטקסט ©צבי מלמד 42 הפונקציה )( + my_funcצביעת הטקסט ©צבי מלמד 43 הערות לתוכנית .1הטיפוס ) pthread_tשומר את מזהה הפתיל(: – – עשוי להיות מבנה או פרימיטיבי ).(int למשל כדי לדעת האם הפּ ְ נ ִיה הנוכחית אם צריכים להשוות מספרי פתילים ) היא "אלי"(: ; )int pthread_equal(pthread_t tid1, pthread_t tid1 – משווה מספרי פתילים ,מחזירה אפס אם הם שווים ,ואחרת מחזירה ≠ 0 .2בדוגמה שראינו לשני הפתילים היה אותו מספר תהליך .בלינוקס זה לא חייב להיות כך .מימוש pthread_createעשוי להיות באמצעות קריאת מערכת )( cloneהייחודית ללינוקס ,עליה נדבר בהמשך ,ואשר עשויה ליצור תהליך חדש החולק משאבים עם אביו כך שהינו למעשה פתיל .בלינוקס זה נקרא task ©צבי מלמד 44 הערות לתוכנית exit() .3מסיימת תהליך שלם – אם אחד הפתילים יבצע אותה כלל התהליך יסתיים ,כולל כל הפתילים הנכללים בו. – שימו לב שבדוגמה שלנו גם הפתיל הראשי מבצע )( pthread_exitכאשר הוא מריץ את ,my_funcבפרט הוא לא מגיע לבצע את פקודת הפלט שבסופו ,ואת ה- ;)return(EXIT_SUCCESS ©צבי מלמד 45 הרצת התוכנית pthread1.c האם הכל נראה תקין? ) -כבר לא כל כך( נשים לב לאפס הזה ..מה בעצם קרה כאן? ©צבי מלמד 46 הרצות נוספות ...מה קורה כאן? ומה קורה כאן?? ©צבי מלמד 47 נשים לב לאפס הזה ..מה בעצם קרה כאן? הפתיל מספר 1הוא הפתיל החדש שנוצר ,והפתיל מספר 2הוא הפתיל הראשי )שימו לב לזה בקוד של התוכנית(. כלומר – מקורו של המספר 1הוא במשתנה a ששלחנו כפרמטר לפונקציה .למעשה ,לא שלחנו את by-value - aאלא שלחנו מצביע אליו – כלומר .by-referenceשימו לב שמשתנה זה הוא משתנה לוקלי של main רצה הגורל ...ובגלל ההגרלות ,הפתיל הראשי, מספר ,2הסתיים ראשון .ברגע שהוא הסתיים, התקפלה המחסנית שלו ,והתקפלה גם הפונקציה .mainולכן ,המשתנה aהפסיק להיות ולידי. בכל אופן ,פתיל מספר 1נשאר עם המצביע למשתנה הזה – וקרה ,שאותו שטח נכתב עליו הערך אפס ...אז זה מה שהודפס. ©צבי מלמד 48 עוד הערות לתוכנית .4 • ראינו את ) pthread_exit(NULLהמסיימת פתיל )תוך החזרת ערך מטיפוס * .(void – כאלטרנטיבה הפתיל רשאי לבצע פקודת returnמהפונקציה שלתוכה הוא נוצר )ולהחזיר ערך מטיפוס * . (void – לדוגמהreturn( (void *) &num) : יש רק לתת את הדעת היכן הוקצה numומה קורה לו אחרי סיום הפתיל .אך זה נכון גם לגבי )( .pthread_exitלכן אולי סביר שנרצה להגדיר: ))int *ret_val = (int *) malloc(sizeof(int הכנס את ערך ההחזרה של הפתיל *ret_val = 0 ; // להחזיר תוך ביצוע ההמרה הדרושה pthread_exit((void *) ret_val) ; // הסיבה להקצאת באמצעות – mallocשלא יהיה לנו ערך מקומי על המחסנית ...שלא תקרה התופעה שהסברנו קודם :בשקף עם הכותרת "נשים לב לאפס הזה ...מה בעצם קרה כאן( ©צבי מלמד 49 עוד על ספרית הפתילים ומקביליות • • • • POSIXקובע סטנדרט ל . APIהמימוש של הסטנדרט הזה – כלומר ,מימוש ספרית הפתילים – שונה בין ספריות שונות המימוש קובע למשל – כיצד תמומש המקביליות ,כיצד מתנהלות עדיפויות ,ומה קורה בסיטואציות שונות מכיוון שהמקביליות מתבצעת על ידי ספרית הפתילים ,ולא על ידי הגרעין –בד"כ מתבצע non-preemptive schedulingאלא cooperative time-sharing – לא מחויב המציאות – משתנה בין מחשבים ובין גרסאות ספרית הפתילים משתמש בטיימרים ,סיגנלים ,וכו' בכדי להשיג את יעדיה – בעצם היא משתמשת )בצורה אינטנסיבית( בכלים שמספקת מערכת ההפעלה ©צבי מלמד 50 הערות לשאלות שסטודנטים העלו בעבר • האם user threadsיכולים לרוץ על מעבדים שונים? – לא ,כי זה תפקיד של מערכת ההפעלה לשייך מעבד לתהליך • מה המשמעות שמעבד תומך ב ?MT – לאינטל אגב יש טכנולוגיה שהם קוראים לה Hyper- = HT ) Threadingשם שיווקי ...לא להתרגש( – למעבד יש העתקים )כפילויות( של הרגיסטרים וה PC -והSP- ) – (Stack Pointerזהו ה Context-של הthread- – ובכך הוא מייצר מעבד לוגי נוסף ...אבל באופן פיזי=ממשי אין לו מעבד נוסף. – הוא יכול בפקודת מכונה אחת )פחות או יותר( להחליף את ההקשר – אין צורך לשמור ולטעון את ההקשר – הם נשמרים על ההעתק הפיזי ©צבי מלמד 51 )(Threads and fork • נניח שתכניתנו מורכבת ממספר פתילים ואחד מהם ביצע )(fork מה ייוולד? • תשובות אפשריות: – תהליך המריץ את אותם פתילים כפי שהיו באב .כלומר כלל פתילי האב ישוכפלו. – תהליך המריץ רק את הפתיל שביצע את הfork() - • בלינוקס :ה fork() -משכפל רק את הפתיל שביצע אותו – אם ה fork() -בוצע ע"י פתיל משני המריץ פונקציה )( ,fנקבל: • תהליך א' הכולל את הפתיל הראשי )המריץ ,למשל את (mainופתיל נוסף )המריץ את )((f • תהליך ב' שכולל פתיל יחיד )המריץ את )(.(f ©צבי מלמד 52 )(Threads and exec • נניח שאחד הפתילים מבצע execומחליף את מרחב הכתובות )שלו?( .מה יהיה האפקט על התכנית? • תשובות אפשריות: (a רק הפתיל הנוכחי מחליף את מרחב הכתובות שלו ,ועובר להריץ תכנית חדשה; כל יתר הפתילים ממשיכים לרוץ כרגיל. (bכל הפתילים הנכללים בתהליך מופסקים ,והתהליך בכללותו 'משנה עורו', ועובר להריץ את התכנית החדשה. • בלינוקס חל מקרה )(b – אם פתיל ראשי הוליד פתיל משני ,והפתיל המשני ביצע execאזי גם ביצועו של הפתיל הראשי ייקטע – "השטיח נשמט מתחת לכפות רגליו" ') -השטיח' = מרחב הכתובות( – כל מרחב הכתובות מוחלף ©צבי מלמד 53
© Copyright 2024