מערכות הפעלה
This presentation is the property of its rightful owner.
Sponsored Links
1 / 27

מערכות הפעלה PowerPoint PPT Presentation


  • 74 Views
  • Uploaded on
  • Presentation posted in: General

מערכות הפעלה. תרגול 6 – חוטים ב- Linux. תוכן התרגול. מבוא לחוטים ב- Linux כיצד גרעין Linux תומך בחוטים עבודה עם חוטים ב- Linux ספריות המממשות תמיכה בחוטים ב- Linux Linux Threads POSIX Threads API – סקירה בסיסית

Download Presentation

מערכות הפעלה

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


4430846

מערכות הפעלה

תרגול 6 – חוטים ב-Linux


4430846

תוכן התרגול

  • מבוא לחוטים ב-Linux

  • כיצד גרעין Linux תומך בחוטים

  • עבודה עם חוטים ב-Linux

    • ספריות המממשות תמיכה בחוטים ב-Linux

    • Linux Threads

      • POSIX Threads API – סקירה בסיסית

      • הקשר בין חוטים ותהליכים – כיצד השימוש בחוטים משפיע על פקודות שלמדנו בנושא תהליכים כגון fork()

מערכות הפעלה - תרגול 6


Linux 1

מבוא לחוטים ב-Linux (1)

  • מבחינה לוגית, תהליך ב-Linux יכול לכלול מספר חוטים המשתפים ביניהם את כל משאבי התהליך

    • מרחב הזיכרון, גישה לקבצים והתקני חומרה, מנגנונים שונים של מערכת ההפעלה

  • כל חוט בתהליך מהווה הקשר ביצוע נפרד

    • מחסנית + רגיסטרים (אך לא מרחב זיכרון וקבצים פתוחים)

    • ביצוע בלתי תלוי של חלקים מהמשימה של אותו תהליך

    • אם יש מספר מעבדים במערכת, ניתן להגיע לביצוע חוטים במקביל על מעבדים שונים – שיפור ביצועים

    • ניתן לשפר את ביצוע תהליך באמצעות שימוש בריבוי חוטים גם על מעבד יחיד – חוט אחד יכול לבצע הוראה חוסמת וחוט אחר ימשיך בביצוע חלק אחר של התכנית

    • כשתהליך נוצר לראשונה – יש לו חוט יחיד, הקרוי החוט הראשי (primary thread)

      • חוטים נוספים נוצרים מחוטים קיימים באמצעות קריאות מערכת כדוגמת clone()

מערכות הפעלה - תרגול 6


Linux 2

מבוא לחוטים ב-Linux (2)

  • התקשורת בין חוטים של אותו תהליך היא פשוטה ביותר – קריאה וכתיבה למשתנים משותפים

    • זהו גם חיסרון – יש לתאם את הפעולות בין חוטים הניגשים לאותם משתנים על-מנת למנוע את שיבוש הנתונים

  • הוספת חוט לביצוע תכנית זולה בהרבה מהוספת תהליך לאותה מטרה, מפני שאינה כרוכה בהקצאת משאבים נוספים כגון זיכרון, גישה לחומרה וכו'

מערכות הפעלה - תרגול 6


Linux 3

מבוא לחוטים ב-Linux (3)

  • יישומים שמתאימים במיוחד לריבוי חוטים:

    • תכניות המכילות מספר משימות בלתי תלויות, כגון הדפסת מסמך במקביל לעריכתו

    • שרתים המטפלים במספר בקשות בו זמנית – עדיף להפעיל חוט לכל בקשה מאשר תהליך לכל בקשה

    • יישומים המכילים חישובים "כבדים" הניתנים למיקבול, כאשר המערכת מרובת מעבדים – ניצול החומרה לשיפור הביצועים

  • יישומים שאינם מתאימים לריבוי חוטים:

    • תכניות קטנות ופשוטות – חוטים יוצרים תקורה מיותרת מבלי להוסיף לביצועים

    • יישומים עתירי חישוב במערכת מעבד יחיד – חוטים לא ישפרו ביצועים

מערכות הפעלה - תרגול 6


Linux 11

תמיכה בחוטים בגרעין Linux (1)

  • Linux תומכת בחוטים ברמת גרעין מערכת ההפעלה. חוטי המערכת הם למעשה תהליכים רגילים המשתפים ביניהם משאבים כגון זיכרון וגישה לקבצים וחומרה

  • התמיכה בחוטים ב-Linux שואפת להתאים לתקן הכללי של מימוש חוטים במערכות Unix הקרוי POSIX Threads או POSIX 1003.1c

  • לכל חוט, בהיותו תהליך רגיל, יש מתאר תהליך משלו ו-PID משלו

  • עם זאת, המתכנת, בהתאם לתקן POSIX, מצפה שלכל החוטים השייכים לאותו תהליך ניתן יהיה להתייחס דרך PID יחיד – של התהליך המכיל אותם

    • פעולות על ה-PID של התהליך צריכות להשפיע על כל החוטים בתהליך

    • פעולת getpid() בכל חוט צריכה להחזיר אותו PID – של התהליך המכיל את החוט

מערכות הפעלה - תרגול 6


Linux 21

תמיכה בחוטים בגרעין Linux (2)

  • כדי לאפשר את ההתייחסות לכל החוטים באותו תהליך מוגדר בגרעין של Linux (מגרסת 2.4.X ומעלה) העיקרון של קבוצת חוטים (Thread Group)

    • כל החוטים השייכים לאותו תהליך נמצאים בקבוצה אחת

    • מתארי התהליכים של כל החוטים באותה קבוצה מקושרים באמצעות שדה thread_group במתאר התהליך

    • שדה tgid במתאר התהליך מכיל את ה-PID המשותף לכל החוטים באותה קבוצה. למעשה, זהו ערך ה-PID של החוט הראשון של התהליך

    • פעולת getpid() מחזירה את current->tgid

    • פעולות על ה-PID המשותף מתורגמות לפעולה על קבוצת החוטים המתאימה ל-PID

    • אם לחוט כלשהו יש בנים (תהליכים), הם הופכים להיות בנים של חוט אחר בקבוצת האב לאחר מותו

מערכות הפעלה - תרגול 6


Clone 1

קריאת המערכת clone() (1)

תמיכה בחוטים בגרעין Linux

  • קריאת המערכת clone() מאפשרת לתהליך ליצור תהליך נוסף המשתף איתו משאבים ונתונים לפי בחירה

    • קריאת מערכת זאת היא הבסיס לספריות התמיכה בחוטים מתוך user mode

  • תחביר:

    int clone(int (*fn)(void*), void *child_stack, int flags, void *arg);

  • פרמטרים:

    • fn – מצביע לפונקציה שתהווה את הקוד הראשי של התהליך החדש

      • כשביצוע הפונקציה fn(arg) מסתיים, נגמר התהליך החדש

    • child_stack – מצביע לסוף בלוק זיכרון המוקצה לטובת מחסנית של התהליך החדש

      • תזכורת: המחסנית גדלה לכיוון הכתובות הנמוכות

מערכות הפעלה - תרגול 6


Clone 2

קריאת המערכת clone() (2)

תמיכה בחוטים בגרעין Linux

  • flags – מסכת דגלים (OR) הקובעת את צורת השיתוף בין התהליך הקורא והתהליך החדש. להלן מספר דגלים אופייניים:

    • CLONE_VM – שיתוף מרחב הזיכרון

    • CLONE_FILES – שיתוף טבלת הקבצים הפתוחים

    • CLONE_FS – שיתוף טבלת נתוני עבודה עם קבצים, המכילה נתונים כגון ספרית העבודה הנוכחית ועוד

    • CLONE_PARENT – לתהליך החדש יהיה אותו אב כמו התהליך הקורא (אחרת החדש יהיה הבן של הקורא)

    • CLONE_THREAD – התהליך החדש הוא חוט באותה קבוצת חוטים כמו התהליך הקורא (אותו tgid). גורר גם CLONE_PARENT

  • arg – הפרמטר המועבר לפונקציה fn() בתחילת ביצוע התהליך החדש

מערכות הפעלה - תרגול 6


Clone 3

קריאת המערכת clone() (3)

תמיכה בחוטים בגרעין Linux

  • ערך מוחזר: במקרה של הצלחה, התהליך הקורא מקבל את ה-PID של התהליך החדש, אחרת -1. לתהליך החדש מוחזר 0.

  • בתוך הגרעין, sys_clone() (קובץ גרעין arch/i386/kernel/process.c) משתמשת למעשה בפונקציה do_fork() עליה למדנו בתרגול קודם, ומעבירה לה את הדגלים על-מנת לקבוע לכל משאב אם לשתף אותו או ליצור אותו כחדש

  • עם שיפור התמיכה בחוטים ב-Linux, צפויה הוספת דגלים ושינויים ב-clone()

  • מערכות הפעלה - תרגול 6


    Linux 12

    עבודה עם חוטים ב-Linux (1)

    • הספרייה הנפוצה לעבודה עם חוטים ב-Linux נקרא Linux Threads

      • המנגנון תואם (באופן מסורבל, חלקי ועם הרבה בעיות) את התקן POSIX Threads

      • לדוגמה, מנגנון זה עדיין חושף PID נפרד לכל חוט בקריאה ל-getpid()

      • Linux Threads אינה מנצלת את התמיכה הקיימת בגרעין 2.4.X לחוטים, אלא מפעילה מנגנון ותיק (שקיים כבר בגרסאות ישנות של הגרעין) של שיתוף משאבים בין תהליכים המהווים חוטים של אותו יישום

    • Linux Threads תומך, בגרסאות הנוכחיות, בחוטי מערכת (Kernel Threads / Lightweight Processes) בלבד, כלומר חוטים המנוהלים ומתוזמנים ע"י גרעין מערכת ההפעלה

    מערכות הפעלה - תרגול 6


    Linux 22

    עבודה עם חוטים ב-Linux (2)

    • התמיכה בחוטים צפויה להשתפר משמעותית באמצעות שני פיתוחים עתידיים: NGPT (Next Generation POSIX Threads) ו-NPTL (Native POSIX Thread Library)

      • תמיכה ב-User Threads

      • תאימות מלאה ומסודרת ב-POSIX Threads

      • שיפורים ניכרים בביצועים ביחס ל-Linux Threads

    • בהמשך, אנו נתמקד במימוש הקיים לפי Linux Threads בלבד

    מערכות הפעלה - תרגול 6


    Linux 31

    עבודה עם חוטים ב-Linux (3)

    • כדי להשתמש ב-POSIX Threads דרך ספריית ה-Linux Threads יש לבצע את הפעולות הבאות:

      • בתחילת קוד התכנית (C) יש להוסיף

        #include <pthread.h>

      • יש לחבר את התכנית עם הספריה pthread, לדוגמה:

        gcc –g –o myprog –l pthread myprog.c

    • הספריה Linux Threads מוסיפה לתוכנית שני שינויים:

      • מממשת ממשק לעבודה עם חוטים הקרוי POSIX Threads API

      • משנה את פעולתן של מספר קריאות מערכת הקשורות לעבודה עם תהליכים, כפי שנראה בהמשך

        • הספרייה מחליפה את ה-wrapper functions של קריאות המערכת הרלוונטיות

    מערכות הפעלה - תרגול 6


    Posix threads api 1

    POSIX Threads API (1)

    • יצירת חוט: pthread_create()

      • תחביר:

        int pthread_create(pthread_t *thread, pthread_attr_t *attr, void* (*start_routine)(void*), void *arg);

      • פעולה: יוצרת חוט חדש המתבצע במקביל לחוט הקורא בתוך אותו תהליך. החוט החדש מתחיל לבצע את הפונקציה המופיעה בפרמטר start_routine ונהרג בסיום ביצוע הפונקציה

    מערכות הפעלה - תרגול 6


    Posix threads api 2

    POSIX Threads API (2)

    • פרמטרים:

      • thread – מצביע למקום בו יאוחסן מזהה החוט החדש במקרה של סיום הפונקציה בהצלחה

      • attr – מאפיינים המתארים את תכונות החוט החדש, כגון האם החוט הוא חוט מערכת או חוט משתמש, האם ניתן לבצע לו join, כלומר להמתין לסיומו, וכו'. בד"כ נספק ערך NULL המציין חוט מערכת שניתן להמתין לסיומו

      • start_routine – מצביע לפונקציה שתהווה את קוד החוט. הערך המוחזר מפונקציה זו במקרה של סיומה הטבעי הינו ערך הסיום של החוט

      • arg – פרמטר שיסופק לפונקציה עם הפעלתה

    • ערך מוחזר: 0 במקרה של הצלחה, ערך אחר במקרה של כישלון. כמו כן, במקרה של הצלחה מוכנס מזהה החוט החדש למקום המוצבע ע"י thread

    מערכות הפעלה - תרגול 6


    Posix threads api 3

    POSIX Threads API (3)

    • סיום חוט: pthread_exit()

      • תחביר:

        void pthread_exit(void *retval);

      • פעולה: החוט הקורא מסיים את פעולתו. ערך הסיום יוחזר לחוט שימתין לסיום חוט זה

        • סיום פעולת החוט הראשי ע"י pthread_exit()אינו מסיים את כל החוטים בתהליך

      • פרמטרים:

        • retval – ערך סיום (בדומה לזה של exit())

      • ערך מוחזר: אין

    מערכות הפעלה - תרגול 6


    Posix threads api 4

    POSIX Threads API (4)

    • חוט יכול להסתיים כתוצאה ממספר אפשרויות שונות:

      • חזרה מהפונקציה הראשית של החוט

      • קריאה ל-pthread_exit() בתוך קוד החוט

      • קריאה ל-exit() ע"י חוט כלשהו בקבוצה של החוט המדובר (כולל סיום "טבעי" של החוט הראשי)

      • הריגת החוט ע"י קריאה ל-pthread_cancel() מחוט אחר כלשהו ביישום

    מערכות הפעלה - תרגול 6


    Posix threads api 5

    POSIX Threads API (5)

    • קבלת מזהה החוט – pthread_self()

      • תחביר:

        pthread_t pthread_self();

      • פעולה: החוט הקורא מקבל את המזהה של עצמו. מזהה זה הוא פנימי לספרייה Linux Threads ואינו קשור במישרין ל-PID של החוט

      • פרמטרים: אין

      • ערך מוחזר: מזהה החוט

    מערכות הפעלה - תרגול 6


    Posix threads api 6

    POSIX Threads API (6)

    • המתנה לסיום חוט: pthread_join()

      • תחביר:

        int pthread_join(pthread_t th, void **thread_return);

      • פעולה: החוט הקורא ממתין לסיום החוט המזוהה ע"י th

        • ניתן להמתין על סיום אותו חוט פעם אחת לכל היותר

          • ביצוע pthread_join על אותו חוט יותר מפעם אחת ייכשל

        • כל חוט יכול להמתין לסיום כל חוט אחר באותו תהליך

        • ההמתנה על סיום החוט משחררת את מידע הניהול של החוט ברמת Linux Threads וברמת הגרעין

    מערכות הפעלה - תרגול 6


    Posix threads api 7

    POSIX Threads API (7)

    • פרמטרים:

      • th – מזהה החוט שממתינים לסיומו

        • לא ניתן להמתין ל"סיום חוט כלשהו" בדומה ל-wait()

      • thread_return – מצביע למקום בו יאוחסן ערך הסיום של החוט עבורו ממתינים

        • ניתן לציין NULL כדי להתעלם מערך הסיום

    • ערך מוחזר: 0 במקרה של הצלחה, וערך שונה מ-0 במקרה כישלון. כמו כן, במקרה הצלחה מוחזר ערך הסיום מוצבע מ-thread_return (אם אינו NULL)

    מערכות הפעלה - תרגול 6


    Posix threads api 8

    POSIX Threads API (8)

    • הריגת חוט: pthread_cancel()

      • תחביר:

        int pthread_cancel(pthread_t thread);

      • פעולה: סיום ביצוע החוט המזוהה ע"י thread

        • ערך סיום הביצוע של החוט שנהרג יהיה PTHREAD_CANCELED

      • פרמטרים:

        • thread – מזהה החוט המיועד לסיום

      • ערך מוחזר: 0 במקרה של הצלחה, וערך אחר במקרה כישלון

    מערכות הפעלה - תרגול 6


    4430846

    תוכנית דוגמה (1)

    /* tsample.c – thread sample */

    #include <pthread.h>

    #include <stdio.h>

    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;

    מערכות הפעלה - תרגול 6


    4430846

    תוכנית דוגמה (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;

    }

    מערכות הפעלה - תרגול 6


    4430846

    תוכנית דוגמה (3)

    • הידור וקישור:

      gcc –g –l pthread –o tsample tsample.c

    • מה התוכנית אמורה להדפיס?

      • 5 תוצאות של 40000

    • מה התוכנית תדפיס באמת?

      • 5 תוצאות בין 0 ל-40000

    • מדוע?

      • הגדלת תאי int_val נעשית בצורה לא מתואמת – עלולה לגרום לאובדן חלק מההגדלות, תלוי בפריסת קוד האסמבלר

    מערכות הפעלה - תרגול 6


    4430846

    ביצוע fork() בתוך חוט

    חוטים ותהליכים ב-Linux Threads

    • כאשר חוט קורא ל-fork(), נוצר תהליך חדש שהוא הבן של החוט הקורא בלבד

      • חוט אחר בקבוצה של החוט הקורא לא יכול לבצע wait() על תהליך הבן שנוצר

    • לתהליך הבן החדש יש חוטים משלו. בהתחלה, חוט יחיד – החוט הראשי

    • חוטים נוספים יכולים להיווצר בהמשך בתהליך הבן

    • גם אם תהליך הבן מכיל יותר מחוט אחד, חוט האב יכול לבצע wait() על תהליך הבן פעם אחת בלבד – להמתין לסיום תהליך הבן, כפי שנראה בהמשך

    מערכות הפעלה - תרגול 6


    Execve

    ביצוע execve() בתוך חוט

    חוטים ותהליכים ב-Linux Threads

    • אם קריאה ל-execve() מצליחה, החוט הקורא מתחיל מחדש בתור חוט ראשי בקבוצה חדשה של תהליך חדש

      • כולל הקצאת משאבים מחדש: זיכרון, גישה לקבצים וחומרה, וכו'

      • כל החוטים האחרים מופסקים

    מערכות הפעלה - תרגול 6


    4430846

    סיום ביצוע תהליך

    חוטים ותהליכים ב-Linux Threads

    • אם חוט כלשהו מתוך תהליך קורא ל-exit() או שביצוע אחד החוטים גורם לתקלה לא-מטופלת, מתבצע סיום ביצוע התהליך כולו

      • כל החוטים בקבוצה מופסקים

    • כמו כן, כזכור מהתרגול הקודם, לאחר סיום ביצוע קוד החוט הראשי מתבצעת קריאה אוטומטית –לexit() הגורמת לסיום ביצוע התהליך

    • אם כל החוטים בתהליך מסיימים באמצעות pthread_exit(), אזי סיום החוט האחרון הוא סיום התהליך

    מערכות הפעלה - תרגול 6


  • Login