Download presentation
Presentation is loading. Please wait.
1
מערכות הפעלה תרגול 6 – חוטים ב-Linux
2
מערכות הפעלה - תרגול 62 (c) ארז חדד 2003 תוכן התרגול מבוא לחוטים ב-Linux כיצד גרעין Linux תומך בחוטים עבודה עם חוטים ב-Linux ספריות המממשות תמיכה בחוטים ב-Linux Linux Threads POSIX Threads API – סקירה בסיסית הקשר בין חוטים ותהליכים – כיצד השימוש בחוטים משפיע על פקודות שלמדנו בנושא תהליכים כגון fork()
3
מערכות הפעלה - תרגול 63 (c) ארז חדד 2003 מבוא לחוטים ב-Linux (1) מבחינה לוגית, תהליך ב-Linux יכול לכלול מספר חוטים המשתפים ביניהם את כל משאבי התהליך מרחב הזיכרון, גישה לקבצים והתקני חומרה, מנגנונים שונים של מערכת ההפעלה כל חוט בתהליך מהווה הקשר ביצוע נפרד מחסנית + רגיסטרים (אך לא מרחב זיכרון וקבצים פתוחים) ביצוע בלתי תלוי של חלקים מהמשימה של אותו תהליך אם יש מספר מעבדים במערכת, ניתן להגיע לביצוע חוטים במקביל על מעבדים שונים – שיפור ביצועים ניתן לשפר את ביצוע תהליך באמצעות שימוש בריבוי חוטים גם על מעבד יחיד – חוט אחד יכול לבצע הוראה חוסמת וחוט אחר ימשיך בביצוע חלק אחר של התכנית כשתהליך נוצר לראשונה – יש לו חוט יחיד, הקרוי החוט הראשי (primary thread) חוטים נוספים נוצרים מחוטים קיימים באמצעות קריאות מערכת כדוגמת clone()
4
מערכות הפעלה - תרגול 64 (c) ארז חדד 2003 מבוא לחוטים ב-Linux (2) התקשורת בין חוטים של אותו תהליך היא פשוטה ביותר – קריאה וכתיבה למשתנים משותפים זהו גם חיסרון – יש לתאם את הפעולות בין חוטים הניגשים לאותם משתנים על-מנת למנוע את שיבוש הנתונים הוספת חוט לביצוע תכנית זולה בהרבה מהוספת תהליך לאותה מטרה, מפני שאינה כרוכה בהקצאת משאבים נוספים כגון זיכרון, גישה לחומרה וכו'
5
מערכות הפעלה - תרגול 65 (c) ארז חדד 2003 מבוא לחוטים ב-Linux (3) יישומים שמתאימים במיוחד לריבוי חוטים: תכניות המכילות מספר משימות בלתי תלויות, כגון הדפסת מסמך במקביל לעריכתו שרתים המטפלים במספר בקשות בו זמנית – עדיף להפעיל חוט לכל בקשה מאשר תהליך לכל בקשה יישומים המכילים חישובים "כבדים" הניתנים למיקבול, כאשר המערכת מרובת מעבדים – ניצול החומרה לשיפור הביצועים יישומים שאינם מתאימים לריבוי חוטים: תכניות קטנות ופשוטות – חוטים יוצרים תקורה מיותרת מבלי להוסיף לביצועים יישומים עתירי חישוב במערכת מעבד יחיד – חוטים לא ישפרו ביצועים
6
מערכות הפעלה - תרגול 66 (c) ארז חדד 2003 תמיכה בחוטים בגרעין Linux (1) Linux תומכת בחוטים ברמת גרעין מערכת ההפעלה. חוטי המערכת הם למעשה תהליכים רגילים המשתפים ביניהם משאבים כגון זיכרון וגישה לקבצים וחומרה התמיכה בחוטים ב-Linux שואפת להתאים לתקן הכללי של מימוש חוטים במערכות Unix הקרוי POSIX Threads או POSIX 1003.1c לכל חוט, בהיותו תהליך רגיל, יש מתאר תהליך משלו ו-PID משלו עם זאת, המתכנת, בהתאם לתקן POSIX, מצפה שלכל החוטים השייכים לאותו תהליך ניתן יהיה להתייחס דרך PID יחיד – של התהליך המכיל אותם פעולות על ה-PID של התהליך צריכות להשפיע על כל החוטים בתהליך פעולת getpid() בכל חוט צריכה להחזיר אותו PID – של התהליך המכיל את החוט
7
מערכות הפעלה - תרגול 67 (c) ארז חדד 2003 תמיכה בחוטים בגרעין Linux (2) כדי לאפשר את ההתייחסות לכל החוטים באותו תהליך מוגדר בגרעין של Linux (מגרסת 2.4.X ומעלה) העיקרון של קבוצת חוטים (Thread Group) כל החוטים השייכים לאותו תהליך נמצאים בקבוצה אחת מתארי התהליכים של כל החוטים באותה קבוצה מקושרים באמצעות שדה thread_group במתאר התהליך שדה tgid במתאר התהליך מכיל את ה-PID המשותף לכל החוטים באותה קבוצה. למעשה, זהו ערך ה-PID של החוט הראשון של התהליך פעולת getpid() מחזירה את current->tgid פעולות על ה-PID המשותף מתורגמות לפעולה על קבוצת החוטים המתאימה ל-PID אם לחוט כלשהו יש בנים (תהליכים), הם הופכים להיות בנים של חוט אחר בקבוצת האב לאחר מותו
8
מערכות הפעלה - תרגול 68 (c) ארז חדד 2003 קריאת המערכת clone() (1) קריאת המערכת clone() מאפשרת לתהליך ליצור תהליך נוסף המשתף איתו משאבים ונתונים לפי בחירה קריאת מערכת זאת היא הבסיס לספריות התמיכה בחוטים מתוך user mode תחביר: int clone(int (*fn)(void*), void *child_stack, int flags, void *arg); פרמטרים: fn – מצביע לפונקציה שתהווה את הקוד הראשי של התהליך החדש כשביצוע הפונקציה fn(arg) מסתיים, נגמר התהליך החדש child_stack – מצביע לסוף בלוק זיכרון המוקצה לטובת מחסנית של התהליך החדש תזכורת: המחסנית גדלה לכיוון הכתובות הנמוכות תמיכה בחוטים בגרעין Linux
9
מערכות הפעלה - תרגול 69 (c) ארז חדד 2003 קריאת המערכת clone() (2) flags – מסכת דגלים (OR) הקובעת את צורת השיתוף בין התהליך הקורא והתהליך החדש. להלן מספר דגלים אופייניים: CLONE_VM – שיתוף מרחב הזיכרון CLONE_FILES – שיתוף טבלת הקבצים הפתוחים CLONE_FS – שיתוף טבלת נתוני עבודה עם קבצים, המכילה נתונים כגון ספרית העבודה הנוכחית ועוד CLONE_PARENT – לתהליך החדש יהיה אותו אב כמו התהליך הקורא (אחרת החדש יהיה הבן של הקורא) CLONE_THREAD – התהליך החדש הוא חוט באותה קבוצת חוטים כמו התהליך הקורא (אותו tgid). גורר גם CLONE_PARENT arg – הפרמטר המועבר לפונקציה fn() בתחילת ביצוע התהליך החדש תמיכה בחוטים בגרעין Linux
10
מערכות הפעלה - תרגול 610 (c) ארז חדד 2003 קריאת המערכת clone() (3) ערך מוחזר: במקרה של הצלחה, התהליך הקורא מקבל את ה- PID של התהליך החדש, אחרת -1. לתהליך החדש מוחזר 0. בתוך הגרעין, sys_clone() (קובץ גרעין arch/i386/kernel/process.c) משתמשת למעשה בפונקציה do_fork() עליה למדנו בתרגול קודם, ומעבירה לה את הדגלים על-מנת לקבוע לכל משאב אם לשתף אותו או ליצור אותו כחדש עם שיפור התמיכה בחוטים ב-Linux, צפויה הוספת דגלים ושינויים ב-clone() תמיכה בחוטים בגרעין Linux
11
מערכות הפעלה - תרגול 611 (c) ארז חדד 2003 עבודה עם חוטים ב-Linux (1) הספרייה הנפוצה לעבודה עם חוטים ב-Linux נקרא Linux Threads המנגנון תואם (באופן מסורבל, חלקי ועם הרבה בעיות) את התקן POSIX Threads לדוגמה, מנגנון זה עדיין חושף PID נפרד לכל חוט בקריאה ל- getpid() Linux Threads אינה מנצלת את התמיכה הקיימת בגרעין 2.4.X לחוטים, אלא מפעילה מנגנון ותיק (שקיים כבר בגרסאות ישנות של הגרעין) של שיתוף משאבים בין תהליכים המהווים חוטים של אותו יישום Linux Threads תומך, בגרסאות הנוכחיות, בחוטי מערכת (Kernel Threads / Lightweight Processes) בלבד, כלומר חוטים המנוהלים ומתוזמנים ע"י גרעין מערכת ההפעלה
12
מערכות הפעלה - תרגול 612 (c) ארז חדד 2003 עבודה עם חוטים ב-Linux (2) התמיכה בחוטים צפויה להשתפר משמעותית באמצעות שני פיתוחים עתידיים: NGPT (Next Generation POSIX Threads) ו-NPTL (Native POSIX Thread Library) תמיכה ב-User Threads תאימות מלאה ומסודרת ב-POSIX Threads שיפורים ניכרים בביצועים ביחס ל-Linux Threads בהמשך, אנו נתמקד במימוש הקיים לפי Linux Threads בלבד
13
מערכות הפעלה - תרגול 613 (c) ארז חדד 2003 עבודה עם חוטים ב-Linux (3) כדי להשתמש ב-POSIX Threads דרך ספריית ה- Linux Threads יש לבצע את הפעולות הבאות: בתחילת קוד התכנית (C) יש להוסיף #include יש לחבר את התכנית עם הספריה pthread, לדוגמה: gcc –g –o myprog –l pthread myprog.c הספריה Linux Threads מוסיפה לתוכנית שני שינויים: מממשת ממשק לעבודה עם חוטים הקרוי POSIX Threads API משנה את פעולתן של מספר קריאות מערכת הקשורות לעבודה עם תהליכים, כפי שנראה בהמשך הספרייה מחליפה את ה-wrapper functions של קריאות המערכת הרלוונטיות
14
מערכות הפעלה - תרגול 614 (c) ארז חדד 2003 POSIX Threads API (1) יצירת חוט: pthread_create() תחביר: int pthread_create(pthread_t *thread, pthread_attr_t *attr, void* (*start_routine)(void*), void *arg); פעולה: יוצרת חוט חדש המתבצע במקביל לחוט הקורא בתוך אותו תהליך. החוט החדש מתחיל לבצע את הפונקציה המופיעה בפרמטר start_routine ונהרג בסיום ביצוע הפונקציה
15
מערכות הפעלה - תרגול 615 (c) ארז חדד 2003 POSIX Threads API (2) פרמטרים: thread – מצביע למקום בו יאוחסן מזהה החוט החדש במקרה של סיום הפונקציה בהצלחה attr – מאפיינים המתארים את תכונות החוט החדש, כגון האם החוט הוא חוט מערכת או חוט משתמש, האם ניתן לבצע לו join, כלומר להמתין לסיומו, וכו'. בד"כ נספק ערך NULL המציין חוט מערכת שניתן להמתין לסיומו start_routine – מצביע לפונקציה שתהווה את קוד החוט. הערך המוחזר מפונקציה זו במקרה של סיומה הטבעי הינו ערך הסיום של החוט arg – פרמטר שיסופק לפונקציה עם הפעלתה ערך מוחזר: 0 במקרה של הצלחה, ערך אחר במקרה של כישלון. כמו כן, במקרה של הצלחה מוכנס מזהה החוט החדש למקום המוצבע ע"י thread
16
מערכות הפעלה - תרגול 616 (c) ארז חדד 2003 POSIX Threads API (3) סיום חוט: pthread_exit() תחביר: void pthread_exit(void *retval); פעולה: החוט הקורא מסיים את פעולתו. ערך הסיום יוחזר לחוט שימתין לסיום חוט זה סיום פעולת החוט הראשי ע"י pthread_exit() אינו מסיים את כל החוטים בתהליך פרמטרים: retval – ערך סיום (בדומה לזה של exit()) ערך מוחזר: אין
17
מערכות הפעלה - תרגול 617 (c) ארז חדד 2003 POSIX Threads API (4) חוט יכול להסתיים כתוצאה ממספר אפשרויות שונות: חזרה מהפונקציה הראשית של החוט קריאה ל-pthread_exit() בתוך קוד החוט קריאה ל-exit() ע"י חוט כלשהו בקבוצה של החוט המדובר (כולל סיום "טבעי" של החוט הראשי) הריגת החוט ע"י קריאה ל-pthread_cancel() מחוט אחר כלשהו ביישום
18
מערכות הפעלה - תרגול 618 (c) ארז חדד 2003 POSIX Threads API (5) קבלת מזהה החוט – pthread_self() תחביר: pthread_t pthread_self(); פעולה: החוט הקורא מקבל את המזהה של עצמו. מזהה זה הוא פנימי לספרייה Linux Threads ואינו קשור במישרין ל-PID של החוט פרמטרים: אין ערך מוחזר: מזהה החוט
19
מערכות הפעלה - תרגול 619 (c) ארז חדד 2003 POSIX Threads API (6) המתנה לסיום חוט: pthread_join() תחביר: int pthread_join(pthread_t th, void **thread_return); פעולה: החוט הקורא ממתין לסיום החוט המזוהה ע"י th ניתן להמתין על סיום אותו חוט פעם אחת לכל היותר ביצוע pthread_join על אותו חוט יותר מפעם אחת ייכשל כל חוט יכול להמתין לסיום כל חוט אחר באותו תהליך ההמתנה על סיום החוט משחררת את מידע הניהול של החוט ברמת Linux Threads וברמת הגרעין
20
מערכות הפעלה - תרגול 620 (c) ארז חדד 2003 POSIX Threads API (7) פרמטרים: th – מזהה החוט שממתינים לסיומו לא ניתן להמתין ל"סיום חוט כלשהו" בדומה ל-wait() thread_return – מצביע למקום בו יאוחסן ערך הסיום של החוט עבורו ממתינים ניתן לציין NULL כדי להתעלם מערך הסיום ערך מוחזר: 0 במקרה של הצלחה, וערך שונה מ-0 במקרה כישלון. כמו כן, במקרה הצלחה מוחזר ערך הסיום מוצבע מ-thread_return (אם אינו NULL)
21
מערכות הפעלה - תרגול 621 (c) ארז חדד 2003 POSIX Threads API (8) הריגת חוט: pthread_cancel() תחביר: int pthread_cancel(pthread_t thread); פעולה: סיום ביצוע החוט המזוהה ע"י thread ערך סיום הביצוע של החוט שנהרג יהיה PTHREAD_CANCELED פרמטרים: thread – מזהה החוט המיועד לסיום ערך מוחזר: 0 במקרה של הצלחה, וערך אחר במקרה כישלון
22
מערכות הפעלה - תרגול 622 (c) ארז חדד 2003 תוכנית דוגמה (1) /* tsample.c – thread sample */ #include int int_val[5]; pthread_t th[5]; void *add_to_value(void *arg) { int inval = (int)arg; int i; for(i = 0; i < 10000; i++) int_val[i % 5] += inval; /* ? */ return ((void *)0); } int main(void) { int i; int retcode;
23
מערכות הפעלה - תרגול 623 (c) ארז חדד 2003 תוכנית דוגמה (2) /* Initialize data */ for(i = 0; i < 5; i++) int_val[i] = 0; /* Create the threads */ for(i = 0; i < 5; i++) { retcode = pthread_create(&th[i], NULL, add_to_value, (void *)(2 * i)); if (retcode != 0) printf("Create thread failed with error %d\n", retcode); } /* Wait untill all threads have finished */ for(i = 0; i < 5; i++) pthread_join(th[i], NULL); /* Print the results */ printf("Final values:\n"); for(i = 0; i < 5; i++) printf("Integer value[%d] = \t%d\n", i, int_val[i]); return 0; }
24
מערכות הפעלה - תרגול 624 (c) ארז חדד 2003 תוכנית דוגמה (3) הידור וקישור: gcc –g –l pthread –o tsample tsample.c מה התוכנית אמורה להדפיס? 5 תוצאות של 40000 מה התוכנית תדפיס באמת? 5 תוצאות בין 0 ל-40000 מדוע? הגדלת תאי int_val נעשית בצורה לא מתואמת – עלולה לגרום לאובדן חלק מההגדלות, תלוי בפריסת קוד האסמבלר
25
מערכות הפעלה - תרגול 625 (c) ארז חדד 2003 ביצוע fork() בתוך חוט כאשר חוט קורא ל-fork(), נוצר תהליך חדש שהוא הבן של החוט הקורא בלבד חוט אחר בקבוצה של החוט הקורא לא יכול לבצע wait() על תהליך הבן שנוצר לתהליך הבן החדש יש חוטים משלו. בהתחלה, חוט יחיד – החוט הראשי חוטים נוספים יכולים להיווצר בהמשך בתהליך הבן גם אם תהליך הבן מכיל יותר מחוט אחד, חוט האב יכול לבצע wait() על תהליך הבן פעם אחת בלבד – להמתין לסיום תהליך הבן, כפי שנראה בהמשך חוטים ותהליכים ב-Linux Threads
26
מערכות הפעלה - תרגול 626 (c) ארז חדד 2003 ביצוע execve() בתוך חוט אם קריאה ל-execve() מצליחה, החוט הקורא מתחיל מחדש בתור חוט ראשי בקבוצה חדשה של תהליך חדש כולל הקצאת משאבים מחדש: זיכרון, גישה לקבצים וחומרה, וכו' כל החוטים האחרים מופסקים חוטים ותהליכים ב-Linux Threads
27
מערכות הפעלה - תרגול 627 (c) ארז חדד 2003 סיום ביצוע תהליך אם חוט כלשהו מתוך תהליך קורא ל-exit() או שביצוע אחד החוטים גורם לתקלה לא-מטופלת, מתבצע סיום ביצוע התהליך כולו כל החוטים בקבוצה מופסקים כמו כן, כזכור מהתרגול הקודם, לאחר סיום ביצוע קוד החוט הראשי מתבצעת קריאה אוטומטית –לexit() הגורמת לסיום ביצוע התהליך אם כל החוטים בתהליך מסיימים באמצעות pthread_exit(), אזי סיום החוט האחרון הוא סיום התהליך חוטים ותהליכים ב-Linux Threads
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.