Presentation is loading. Please wait.

Presentation is loading. Please wait.

מערכות הפעלה תרגול 7 – ק/פ ותקשורת תהליכים ב-Linux.

Similar presentations


Presentation on theme: "מערכות הפעלה תרגול 7 – ק/פ ותקשורת תהליכים ב-Linux."— Presentation transcript:

1 מערכות הפעלה תרגול 7 – ק/פ ותקשורת תהליכים ב-Linux

2 מערכות הפעלה - תרגול 72 (c) ארז חדד 2003 תוכן התרגול קלט/פלט של תהליכים תקשורת וסנכרון בין תהליכים  מבוא  pipes  הכוונת קלט ופלט  FIFOs  signals

3 מערכות הפעלה - תרגול 73 (c) ארז חדד 2003 קלט/פלט של תהליכים ב-Linux (1) ב-Linux, כמו בכל מערכות UNIX, מבוצעות פעולות הקלט/פלט של תהליך מול התקן באמצעות descriptors  descriptor הוא אינדקס של כניסה בטבלה מיוחדת, ששמה העממי PDT (Process Descriptor Table) ומוצבעת ממתאר התהליך. כל כניסה בטבלה מצביעה על אוביקט ניהולfile) object) מטעם התהליך עבור ההתקן המקושר ל-descriptor קוד תהליך ניגש להתקן דרך ה-descriptor. אוביקט הניהול מתופעל ע"י גרעין מערכת ההפעלה בלבד בפרט, אוביקט הניהול מכיל את "מחוון הקובץ" המצביע לנתונים הבאים לקריאה / כתיבה מההתקן  ההתקן המופעל באמצעות ה-descriptor יכול להיות קובץ, התקן חומרה, ערוץ תקשורת או כל דבר שניתן לכתוב ו/או לקרוא נתונים אליו וממנו  ממשק ההתקשרות עם ההתקן דרך ה-descriptor הינו אחיד, ללא תלות בסוג ההתקן

4 מערכות הפעלה - תרגול 74 (c) ארז חדד 2003 קלט/פלט של תהליכים ב-Linux (2)  ערכי ה-descriptors הבאים מקושרים להתקנים הבאים כברירת מחדל: 0 (stdin) - מקושר לקלט הסטנדרטי, בדרך-כלל המקלדת  פעולות הקלט המוכרות, כדוגמת scanf() ודומותיה, הן למעשה פעולות של קריאה מהתקן הקלט הסטנדרטי דרך stdin 1 (stdout) - מקושר לפלט הסטנדרטי, בדרך-כלל תצוגת טקסט במסוף  פעולות הפלט המוכרות, כדוגמת printf() ודומותיה, הן למעשה פעולות של כתיבה להתקן הפלט הסטנדרטי דרך stdout 2 (stderr) - מקושר לפלט השגיאות הסטנדרטי, בדרך-כלל גם הוא תצוגת טקסט במסוף  ניתן לשנות את קישור ה-descriptors להתקנים באופן דינמי, כפי שנראה בהמשך

5 מערכות הפעלה - תרגול 75 (c) ארז חדד 2003 קלט/פלט של תהליכים ב-Linux (3)  תחילת עבודה עם התקן היא ע"י קישור ההתקן ל-descriptor בפעולת open() ה-descriptor "פתוח", כלומר מקושר להתקן ה-descriptors 0,1,2 פתוחים מתחילת ביצוע התהליך  בסיום העבודה עם ההתקן מנותק ה-descriptor מההתקן בפעולת close() ה-descriptor "סגור", כלומר לא מקושר להתקן  כל ה-descriptors הפתוחים של תהליך נסגרים באופן אוטומטי ע"י מערכת ההפעלה בסיום התהליך עם זאת, מומלץ לבצע סגירה מסודרת בקוד בכל סיום שימוש ב- descriptor אין צורך לסגור descriptors פתוחים המקושרים לערוצי הקלט / פלט / שגיאות הסטנדרטיים (אלא אם רוצים לחבר אותם להתקנים אחרים)

6 מערכות הפעלה - תרגול 76 (c) ארז חדד 2003 קריאות מערכת בסיסיות (1) פתיחת התקן לגישה – open() #include int open(const char *path, int flags [, mode_t mode]); פעולה: ההתקן המבוקש (דרך path) נפתח לגישה לפי התכונות המוגדרות ב-flags ולפי הרשאות המוגדרות ב-mode פרמטרים:  path – מסלול להתקן (או קובץ) לפתיחה. לדוגמה: "file1" לציון הקובץ file1 בספריית העבודה הנוכחית "/home/yossi/file1" לציון הקובץ file1 בספריית הבית של יוסי "/dev/ttyS0" לציון התקן תקשורת טורית המחובר למחשב קלט/פלט של תהליכים ב-Linux

7 מערכות הפעלה - תרגול 77 (c) ארז חדד 2003 קריאות מערכת בסיסיות (2)  flags – תכונות לאפיון פתיחת הקובץ. חייב להכיל אחת מהאפשרויות הבאות: O_RDONLY – הקובץ נפתח לקריאה בלבד O_WRONLY – הקובץ נפתח לכתיבה בלבד O_RDWR – הקובץ נפתח לקריאה ולכתיבה כמו כן, ניתן לצרף תכונות אופציונליות שימושיות נוספות באמצעות OR (|) עם הדגל המתאים, למשל: O_CREAT – צור את הקובץ אם אינו קיים O_APPEND – שרשר מידע בסוף קובץ קיים  mode – פרמטר אופציונלי המגדיר את הרשאות הקובץ, במקרה שפתיחת הקובץ גורמת ליצירת קובץ חדש. הסבר על הערכים ניתן בתרגול על מערכות קבצים ערך מוחזר:  במקרה של הצלחה – ה-descriptor המקושר להתקן שנפתח. ערך זה הוא ה-descriptor הנמוך ביותר שהיה סגור לפני פתיחת ההתקן  במקרה של כישלון – (-1) קלט/פלט של תהליכים ב-Linux

8 מערכות הפעלה - תרגול 78 (c) ארז חדד 2003 קריאות מערכת בסיסיות (3) סגירת גישה להתקן – close() #include int close(int fd); פעולה: סגירת ה-descriptor fd. לאחר הסגירה לא ניתן לגשת להתקן דרך fd פרמטרים:  fd – ה-descriptor המיועד לסגירה ערך מוחזר: הצלחה – 0. כישלון – (-1) קלט/פלט של תהליכים ב-Linux

9 מערכות הפעלה - תרגול 79 (c) ארז חדד 2003 קריאות מערכת בסיסיות (4) קריאת נתונים מהתקן – read() #include ssize_t read(int fd, void *buf, size_t count); פעולה: מנסה לקרוא עד count בתים מתוך ההתקן המקושר ל-fd לתוך החוצץ buf  ייתכן שייקראו פחות מ-count בתים למשל, אם נותרו פחות מ-count בתים בקובץ ממנו קוראים  ייתכן אף שלא ייקראו בתים כלל למשל, אם בקריאה מקובץ, מחוון הקובץ הגיע לסוף הקובץ (EOF)  מחוון הקובץ מקודם בכמות הבתים שנקראו בפועל מההתקן, כך שבפעולת הגישה הבאה לקובץ (קריאה, כתיבה וכד') ניגש לנתונים שאחרי הנתונים שנקראו בפעולה הנוכחית  פעולת הקריאה תחסום את התהליך הקורא (תוריד אותו להמתנה) עד שיהיו נתונים זמינים לקריאה בהתקן למשל: בקובץ, תהליך ימתין עד שהנתונים ייקראו מהדיסק קלט/פלט של תהליכים ב-Linux

10 מערכות הפעלה - תרגול 710 (c) ארז חדד 2003 קריאות מערכת בסיסיות (5) פרמטרים:  fd – ה-descriptor המקושר להתקן ממנו מבקשים לקרוא  buf – מצביע לחוצץ בו יאוחסנו הנתונים שייקראו מההתקן  count – מספר הבתים המבוקש ערך מוחזר:  בהצלחה – מספר הבתים שנקרא מההתקן לתוך buf. אם read() נקראה עם count = 0, יוחזר 0 ללא קריאה  בכישלון – (-1) קלט/פלט של תהליכים ב-Linux

11 מערכות הפעלה - תרגול 711 (c) ארז חדד 2003 קריאות מערכת בסיסיות (6) כתיבת נתונים להתקן – write() #include ssize_t write(int fd, const void *buf, size_t count); פעולה: מנסה לכתוב עד count בתים מתוך buf להתקן המקושר ל-fd  בתלות בהתקן, ייתכן שייכתבו בין 0 ל-count בתים למשל, אם יש מספיק מקום פנוי  בדומה ל-read(), מחוון הקובץ מקודם בכמות הבתים שנכתבו בפועל, והגישה הבאה לקובץ תהיה לנתונים שאחרי אלו שנכתבו  גם פעולת write() יכולה לחסום את התהליך בפעולה על התקנים מסוימים – למשל, עד שייתפנה מקום לכתיבה קלט/פלט של תהליכים ב-Linux

12 מערכות הפעלה - תרגול 712 (c) ארז חדד 2003 קריאות מערכת בסיסיות (7) פרמטרים:  fd – ה-descriptor המקושר להתקן אליו מבקשים לכתוב  buf – מצביע לחוצץ בו מאוחסנים הנתונים שייכתבו להתקן  count – מספר הבתים המבוקש לכתיבה ערך מוחזר:  בהצלחה – מספר הבתים שנכתב להתקן מתוך buf. אם write() נקראה עם count = 0, יוחזר 0 ללא כתיבה  בכישלון – (-1) קלט/פלט של תהליכים ב-Linux

13 מערכות הפעלה - תרגול 713 (c) ארז חדד 2003 קלט ופלט – סגנון עבודה (1) הפעלה יחידה של קריאה או כתיבה מ-descriptor אינה מבטיחה העברת כל כמות הנתונים הרצויה, ולכן יש לבצע מספר הפעלות לפי הצורך  לדוגמה: אם רוצים לקרוא k תוים לתוך החוצץ buf, נבצע את הקוד הבא: while (k > 0) { r = read(fd, buf, k); if (r < 0) { /* handle error */ } else k = k – r; }  ניתן בקלות להתאים את דוגמת הקוד שלעיל לטפל במצבים נוספים (EOF, סוף מחרוזת וכו')  כדי לעבוד בצורה מסודרת מומלץ להגדיר פונקציות קריאה וכתיבה המכילות קוד כנ"ל ולהשתמש בהן קלט/פלט של תהליכים ב-Linux

14 מערכות הפעלה - תרגול 714 (c) ארז חדד 2003 קלט ופלט – סגנון עבודה (2) ניתן לעבוד עם קבצים והתקנים גם דרך ה-API של ספריית libc, המוכר משיעורי מבוא לתכנות:  fopen(), fclose(), fread(), fwrite(), [f]printf(), [f]scanf(), וכו' עם זאת, כדאי מאוד להימנע מלערבב קריאות מערכת יחד עם קריאות לפונקציות libc בעבודה על אותו התקן  הסיבה: libc מנהלת מנגנוני בקרה משלה הכוללים חוצצים וכו' מעל לקריאות המערכת הבסיסיות, ו"עקיפת" מנגנונים אלו יכולה לגרום לשיבוש הנתונים קלט/פלט של תהליכים ב-Linux

15 מערכות הפעלה - תרגול 715 (c) ארז חדד 2003 שיתוף ק/פ בין חוטים ותהליכים (1) חוטים: טבלת ה-descriptors גלובלית לתהליך, כלומר משותפת לכל החוטים שבו  מתארי התהליכים של כל החוטים מצביעים על אותו PDT תהליכים: פעולת fork() יוצרת עותק נוסף של טבלת ה- descriptors אצל תהליך הבן  תהליך הבן יכול לגשת לאותם התקנים אליהם ניגש האב  ה-descriptors ששוכפלו הינם שותפים, כלומר מצביעים לאותו אובייקט ניהול, ובפרט, חולקים את אותו מחוון קובץ לדוגמה: אם האב קורא 10 בתים מה-descriptor ואחריו הבן קורא 3 בתים, אז הבן יקרא את 3 הבתים שאחרי ה-10 של האב. הכתיבה מושפעת באופן דומה קלט/פלט של תהליכים ב-Linux

16 מערכות הפעלה - תרגול 716 (c) ארז חדד 2003 שיתוף ק/פ בין חוטים ותהליכים (2) תהליכים (וחוטים) המשתמשים ב-descriptors שותפים או משותפים צריכים לתאם את פעולות הגישה להתקן על- מנת שלא לשבש זה את פעולת זה  הבעיה נפוצה במיוחד ב"שושלות משפחתיות" של תהליכים: אבות ובנים, אחים, נכדים וכו' פעולת execve() ודומותיה אינן משנות את טבלת ה- descriptors של התהליך, למרות שהתהליך מאותחל מחדש  קבצים והתקנים פתוחים אינם נסגרים  שימושי ביותר להכוונת קלט ופלט של תוכניות אחרות קלט/פלט של תהליכים ב-Linux

17 מערכות הפעלה - תרגול 717 (c) ארז חדד 2003 תקשורת בין תהליכים ב-Linux (1) בדומה למערכות הפעלה מודרניות אחרות, Linux מציעה מגוון מנגנונים לתקשורת וסנכרון בין תהליכים  IPC = Inter-Process Communication לכאורה, ניתן היה להסתפק בתקשורת בין תהליכים דרך קבצים משותפים, אך צורת תקשורת זו יקרה מבחינת משאבים ואיטית  שימוש במשאבים של מערכת הקבצים למטרות שאינן עבודה עם קבצים המנגנונים ש-Linux מציעה למטרת תקשורת בין תהליכים כוללים:  pipes ו-FIFOs (named pipes): ערוצי תקשורת בין תהליכים באותה מכונה בסגנון יצרן-צרכן: תהליכים מסוימים מייצרים נתונים לתוך ה-pipe ותהליכים אחרים צורכים את הנתונים  signals: איתותים - הודעות אסינכרוניות הנשלחות בין תהליכים באותה מכונה (וגם ממערכת ההפעלה לתהליכים) על-מנת להודיע לתהליך המקבל על אירוע מסוים

18 מערכות הפעלה - תרגול 718 (c) ארז חדד 2003 תקשורת בין תהליכים ב-Linux (2)  System V IPC: שם כולל לקבוצה של מנגנוני תקשורת שהופיעו לראשונה בגרסאות UNIX של חברת AT&T ואומצו במהרה על-ידי מרבית סוגי ה-UNIX הקיימים כולל Linux. במסגרת קבוצה זו נכללים: סמפורים תורי הודעות (message queues) – מנגנון המאפשר להגדיר "תיבות דואר" וירטואליות הנגישות לכל התהליכים באותה מכונה. תהליכים יכולים לתקשר באמצעות הכנסה והוצאה של הודעות מאותה תיבת דואר זיכרון משותף – יצירת איזור זיכרון מיוחד המשותף למרחבי הזיכרון של מספר תהליכים. זו צורת התקשורת היעילה ביותר והמקובלת ביותר עבור יישומים הדורשים כמות גדולה של IPC

19 מערכות הפעלה - תרגול 719 (c) ארז חדד 2003 תקשורת בין תהליכים ב-Linux (3)  sockets: מנגנון סטנדרטי המאפשר יצירת ערוץ תקשורת דו- כיווני (duplex) בין תהליכים היכולים להמצא גם במכונות שונות. מנגנון זה מסתיר את התפעול הפיזי של התקשורת בין המחשבים (רשת, כבלים וכו'). על sockets ניתן ללמוד בהרחבה במסגרת הקורס "תקשורת באינטרנט" וכן דרך מגוון אתרים, כדוגמת: http://www.ecst.csuchico.edu/~beej/guide/net במהלך תרגול זה אנו נסקור את מנגנוני ה-pipes וה- signals כדוגמאות למנגנוני IPC

20 מערכות הפעלה - תרגול 720 (c) ארז חדד 2003 pipes ב-Linux (1) pipes (צינורות) הם ערוצי תקשורת חד-כיווניים המאפשרים העברת נתונים לפי סדר FIFO (First-In-First-Out)  הנתונים נקראים בסדר בו הם נכתבים pipes משמשים גם לסנכרון תהליכים, כפי שנראה בהמשך המימוש של pipes ב-Linux הוא כאובייקטים של מערכת הקבצים, למרות שאינם צורכים שטח דיסק כלל ואינם מופיעים בהיררכיה של מערכת הקבצים הגישה ל-pipe היא באמצעות שני descriptors: אחד לקריאה ואחד לכתיבה

21 מערכות הפעלה - תרגול 721 (c) ארז חדד 2003 pipes ב-Linux (2) היצירה של pipe היא באמצעות קריאת המערכת pipe(), וה-pipe הנוצר הינו פרטי לתהליך, כלומר אינו נגיש לתהליכים אחרים במערכת לפיכך, הדרך היחידה לשתף pipe בין תהליכים שונים היא באמצעות קשרי משפחה  תהליך אב יוצר pipe ואחריו יוצר תהליך בן באמצעות fork() – לאב ולבן יש גישה ל-pipe באמצעות ה-descriptors שלו, המצויים בשניהם לאחר סיום השימוש ב-pipe מצד כל התהליכים (סגירת כל ה-descriptors) מפונים משאבי ה-pipe באופן אוטומטי

22 מערכות הפעלה - תרגול 722 (c) ארז חדד 2003 pipes ב-Linux (3) למי מהתהליכים הבאים יש גישה ל-pipe שיוצר תהליך A?  לכל התהליכים פרט ל-B fork() Process A pipe() fork() Process B Process C Process D Process E

23 מערכות הפעלה - תרגול 723 (c) ארז חדד 2003 קריאת המערכת pipe() תחביר: #include int pipe(int filedes[2]); פעולה: יוצרת pipe חדש עם שני descriptors: אחד לקריאה מה-pipe ואחד לכתיבה אליו פרמטרים:  filedes – מערך בן שני תאים. ב-filedes[0] יאוחסן ה- descriptor לקריאה מה-pipe שנוצר וב-filedes[1] יאוחסן ה- descriptor לכתיבה ערך מוחזר: 0 בהצלחה ו- (-1) בכישלון pipes ב-Linux

24 מערכות הפעלה - תרגול 724 (c) ארז חדד 2003 קריאה וכתיבה ל-pipe (1) פעולות קריאה וכתיבה מתבצעות באמצעות read() ו-write() על ה-descriptors של ה-pipe ניתן להסתכל על pipe כמו על תור FIFO עם מצביע קריאה יחיד (להוצאת נתונים) ומצביע כתיבה יחיד (להכנסת נתונים)  כל קריאה (מכל תהליך שהוא) מה-pipe מקדמת את מצביע הקריאה. באופן דומה, כל כתיבה מקדמת את מצביע הכתיבה בדרך-כלל, תהליך מבצע רק אחת מהפעולות (קריאה או כתיבה) ולכן נהוג לסגור את ה-descriptor השני שאינו בשימוש ה-descriptors של הקריאה בכל התהליכים הם שותפים, ולכן יש לתאם בין הקוראים. כנ"ל לגבי ה-descriptors של הכתיבה pipes ב-Linux

25 מערכות הפעלה - תרגול 725 (c) ארז חדד 2003 קריאה וכתיבה ל-pipe (2) קריאה מ-pipe תחזיר:  את כמות הנתונים המבוקשת אם היא נמצאת ב-pipe  פחות מהכמות המבוקשת אם זו הכמות הזמינה ב-pipe בזמן הקריאה  0 (EOF) כאשר כל ה-write descriptors נסגרו וה-pipe ריק  תחסום את התהליך אם יש כותבים (write descriptors) ל-pipe וה-pipe ריק. כאשר תתבצע כתיבה, יוחזרו הנתונים שנכתבו עד לכמות המבוקשת כתיבה ל-pipe תבצע:  כתיבה של כל הכמות המבוקשת אם יש מספיק מקום פנוי ב-pipe  אם יש קוראים (read descriptors) ואין מספיק מקום פנוי ב-pipe, הכתיבה תחסום את התהליך עד שניתן יהיה לכתוב את כל הכמות הדרושה  ה-pipe מוגבל בגודלו (כ-4K) ולכן כתיבה ל-pipe שאין בו מספיק מקום פנוי ושאין עבורו קוראים (read descriptors פתוחים) תיכשל pipes ב-Linux

26 מערכות הפעלה - תרגול 726 (c) ארז חדד 2003 pipe – תכנית דוגמה #include int main() { int my_pipe[2]; int status; char father_buff[6]; int index = 0; status = pipe(my_pipe); if (status == -1) { printf("Unable to open pipe\n"); exit(-1); } status = fork(); if (status == -1) { printf("Unable to fork\n"); exit(-1); } if (status == 0) { /* son process */ close(my_pipe[0]); write(my_pipe[1], "Hello", 6 * sizeof(char)); exit(0); } else { /* father process */ close(my_pipe[1]); wait(&status); /* wait until son process finishes */ read(my_pipe[0], father_buff, 6); printf("Got from pipe: %s\n", father_buff); exit(0); } pipes ב-Linux מה לא בסדר בתכנית? הקריאות ל-read() ו-write() צריכות להיות רב-פעמיות מהו הפלט של התכנית הנ"ל (בהנחה שהיא עובדת)? Got from pipe: Hello

27 מערכות הפעלה - תרגול 727 (c) ארז חדד 2003 הכוונת קלט ופלט (1) אחד השימושים הנפוצים ביותר ב-descriptors בכלל וב-pipes בפרט הינו הכוונת הקלט והפלט של תכניות (Input/Output Redirection)  ניתוב המידע המיועד להתקן פלט לתוך התקן אחר (למשל קובץ) או החלפת מקור המידע מהתקן קלט אחד באחר תוכנות ה-shell ב-Linux (ובמערכות הפעלה רבות אחרות) תומכות בהכוונת קלט ופלט באופן מובנה בפקודות שלהן  לדוגמה: התכנית ls מדפיסה את רשימת הקבצים בספרייה הנוכחית לתוך stdout. ניתן להריץ את ls דרך פקודת ה-shell הבאה: $ ls  הפקודה הבאה תגרום ל-shell להכניס את הפלט של ls לקובץ myfile $ ls > myfile

28 מערכות הפעלה - תרגול 728 (c) ארז חדד 2003 הכוונת קלט ופלט (2) מה למעשה ביצע ה-shell בתגובה לפקודה הקודמת? (בערך..) status = fork(); if (status == 0) { close(1); fd = open(“myfile”, O_WRONLY…); execve(“/bin/ls”, …); { באופן דומה ניתן לכוון את הקלט של תכנית להגיע מקובץ  לדוגמה: התכנית more קוראת נתונים מ-stdin ומדפיסה אותם עם הפסקה בין עמוד לעמוד. הפקודה הבאה תגרום לה להדפיס את הקובץ myfile more < myfile

29 מערכות הפעלה - תרגול 729 (c) ארז חדד 2003 הכוונת קלט ופלט (3) קריאת המערכת dup() - שימושית במיוחד לפעולות הכוונת קלט ופלט #include int dup(int oldfd); פעולה: מעתיקה את ה-descriptor oldfd ל-descriptor אחר פנוי (סגור) בטבלה.  ה-descriptor החדש הינו ה-descriptor הסגור בעל הערך הנמוך ביותר בטבלה  לאחר פעולה מוצלחת, oldfd וה-descriptor החדש הם שותפים פרמטרים:  oldfd – ה-descriptor המיועד להעתקה – חייב להיות פתוח לפני ההעתקה ערך מוחזר:  בהצלחה, מוחזר הערך של ה-descriptor החדש  בכישלון מוחזר (-1) קריאה דומה: int dup2(oldfd, newfd) – סוגרת את newfd אם צריך ומעתיקה את oldfd ל-newfd ומחזירה את newfd דוגמה מפורטת לשימוש ב-dup() וב-pipe() להכוונת קלט ופלט נמצאת בקבצים המצורפים לתרגול: master.c, father.c, son.c - נא לנסות אותה ולהבינה

30 מערכות הפעלה - תרגול 730 (c) ארז חדד 2003 הכוונת קלט ופלט (4) כאשר רוצים לכוון את הקלט או הפלט לבוא מתוך או להשלח אל תהליך אחר, משתמשים ב-pipe בין התהליכים בתור התקן המחליף את התקן הקלט או הפלט  לדוגמה: אם נרצה שהפלט של ls יודפס בעמודים עם הפסקות: $ ls | more מה יבצע ה-shell בתגובה לפקודה הנ"ל? (בערך..) pipe(fd); status = fork(); if (status == 0) { /* first child */ close(1); dup(fd[1]); close(fd[0]); close(fd[1]); execve(“/bin/ls”,…); } status = fork(); if (status == 0) { /* second child */ close(0); dup(fd[0]); close(fd[0]); close(fd[1]); execve(“/bin/more”,..); } close(fd[0]); close(fd[1]);

31 מערכות הפעלה - תרגול 731 (c) ארז חדד 2003 FIFOs ב-Linux (1) FIFO הוא למעשה pipe. ההבדל העיקרי בינו לבין pipe הוא של- FIFO יש "שם" גלובלי שדרכו יכולים כל התהליכים במכונה לגשת אליו – pipe "ציבורי"  נקרא גם named pipe השימוש העיקרי של FIFO (או של כל אובייקט תקשורת בעל "שם") הוא כאשר תהליכים רוצים לתקשר דרך ערוץ קבוע מראש מבלי שיהיה ביניהם קשרי משפחה  למשל, כאשר תהליכי לקוח צריכים לתקשר עם תהליך שרת FIFO נוצר באמצעות קריאת המערכת mkfifo(), שמעניקה לו את שמו שם ה-FIFO הוא כשם קובץ במערכת הקבצים, למרות שאיננו קובץ כלל  למשל "/home/yossi/myfifo"  ה-FIFO מופיע במערכת הקבצים בשם שנבחר

32 מערכות הפעלה - תרגול 732 (c) ארז חדד 2003 FIFOs ב-Linux (2) לאחר היווצרו, ניתן לגשת ל-FIFO באמצעות פקודת open() ולעבוד איתו כרגיל (קריאה וכתיבה)  ניתן לבצע הן קריאה והן כתיבה ל-FIFO דרך אותו descriptor (ערוץ תקשורת דו-כיווני) תהליך שפותח את ה-FIFO לקריאה בלבד נחסם עד שתהליך נוסף יפתח את ה-FIFO לכתיבה, וההפך  פתיחת ה-FIFO לכתיבה וקריאה (O_RDWR) איננה חוסמת. עם זאת, יש לוודא שלכל קריאה יש כותב (אחרת תהליך יחיד ייחסם - קיפאון) כאובייקטים ציבוריים רבים, FIFO אינו מפונה אוטומטית לאחר שהמשתמש האחרון בו סוגר את הקובץ, ולכן יש לפנותו בצורה מפורשת באמצעות פקודות או קריאות מערכת למחיקת קבצים (למשל, פקודת rm או הקריאה unlink())

33 מערכות הפעלה - תרגול 733 (c) ארז חדד 2003 FIFOs ב-Linux (3) קריאת המערכת mkfifo() #include int mkfifo(const char *pathname, mode_t mode); פעולה: יוצרת FIFO המופיע במערכת הקבצים במסלול pathname והרשאות הגישה שלו הן mode פרמטרים:  pathname – שם ה-FIFO וגם המסלול לקובץ במערכת הקבצים  mode – הרשאות הגישה ל-FIFO שנוצר. נלמד על הרשאות בתרגול על מערכת הקבצים. בינתיים, ניתן להכניס ערך 0777 (777 אוקטאלי) כדי לקבל הרשאות ברירת מחדל) ערך מוחזר: 0 בהצלחה, (-1) בכישלון

34 מערכות הפעלה - תרגול 734 (c) ארז חדד 2003 signals ב-Linux (1) signal (אות/איתות) הינו הודעה על אירוע הנשלחת לתהליך באופן אסינכרוני  ה-signal יכול להשלח לתהליך בכל נקודה בזמן ללא תלות במצב התהליך signals משמשים הן לתקשורת בין תהליכים והן להודעות ממערכת ההפעלה לתהליך על אירועים שונים  כל סוג signal מוגדר לפי שם מתאים וערך מספרי מתאים ומיועד להודעה על סוג מסוים של אירועים  למשל: חלוקה ב-0 בקוד תהליך גורמת למערכת ההפעלה לשלוח signal מסוג SIGFPE (מספר 8 - Floating Point Exception) לתהליך  תהליך אחד יכול לשלוח signal לתהליך אחר באמצעות קריאת מערכת כדוגמת kill() ישנם 31 signals מוגדרים ב-Linux (ערכים מספריים 1-31) כתאימות הסטורית ל-UNIX

35 מערכות הפעלה - תרגול 735 (c) ארז חדד 2003 signals ב-Linux (2) signal שנשלח לתהליך כלשהו אבל עדיין לא טופל נקרא pending signal (אות ממתין)  כל ה-pending signals של אותו תהליך נשמרים ע"י מערכת ההפעלה עד לטיפול בהם ואח"כ נמחקים יכול להיות לכל היותר pending signal אחד מאותו סוג (אותו ערך מספרי) לתהליך נתון  אם יתקבלו signals נוספים מאותו ערך לפני שהראשון טופל, הם יבוטלו ע"י מערכת ההפעלה בדיקה וטיפול ב-pending signals מבוצעים בכל פעם שהתהליך עובר מ-Kernel Mode ל-User Mode במהלך הריצה שלו תהליך ממתין במצב TASK_INTERRUPTIBLE יוחזר למצב ריצה (TASK_RUNNING) אם יש לו pending signals  אם ההמתנה היתה כתוצאה מקריאה חוסמת כלשהי, הקריאה תחזור עם תוצאת כשלון עקב הפרעה

36 מערכות הפעלה - תרגול 736 (c) ארז חדד 2003 טיפול ב-signals (1) תהליך יכול לטפל ב-signal מסוג מסוים בכמה אופנים שונים:  terminate – סיום התהליך בתגובה ל-signal  dump – בתגובה ל-signal יווצר קובץ dump בשם "core" בספריית העבודה הנוכחית והתהליך ייסתיים הקובץ core מכיל תמונת מצב של התהליך (תכולת זיכרון, רגיסטרים ועוד). קובץ זה משמש למטרות debugging – ניתוח מצב התהליך לאחר המוות (post-mortem)  ignore – התעלמות - התגובה ל-signal תהיה המשך הביצוע הרגיל  stop – עצירת תהליך במצב TASK_STOPPED (בד"כ בשליטת debugger)  continue – המשך ביצוע תהליך שהיה במצב TASK_STOPPED (בד"כ בשליטת debugger)  signal handler – הפעלת שגרת משתמש מיוחדת בתגובה ל-signal signals ב-Linux

37 מערכות הפעלה - תרגול 737 (c) ארז חדד 2003 טיפול ב-signals (2) באמצעות קריאות מערכת כדוגמת signal(), המתכנת יכול לבחור אם אופן טיפול של תהליך ב-signal יהיה לפי ברירת המחדל הקבועה במערכת (SIG_DFL), התעלמות (SIG_IGN) או הפעלת signal handler שהוא כותב  לדוגמה: ברירת המחדל לאופן הטיפול ב-SIGFPE היא dump. באמצעות הקריאה signal() יכול המתכנת להחליף את אופן הטיפול בהפעלת signal handler שתדפיס הודעת שגיאה ותמשיך את הביצוע הרגיל של התכנית  החלפת טיפול ב-signal כלשהו ל-signal handler נקראת גם "תפיסת ה- signal" (catching signals) המתכנת יכול גם לחסום (block) את הטיפול ב-signals מסוגים מסוימים באמצעות קריאות מערכת כדוגמת sigprocmask()  אם מתקבל signal מסוג שהוא חסום, ה-signal ימתין עד שתוסר החסימה ורק אחר-כך יטופל (אם התהליך עדיין לא הסתיים) את ה-signals מהסוג SIGKILL (אילוץ סיום תכנית – ברירת מחדל terminate) ן-SIGSTOP (עצירת תכנית בשליטת debugger – ברירת מחדל stop) לא ניתן לתפוס או לחסום signals ב-Linux

38 מערכות הפעלה - תרגול 738 (c) ארז חדד 2003 שגרת טיפול – signal handler כאמור, מתכנת יכול לגרום לתהליך להגיב על קבלת signal בביצוע שגרה מוגדרת מראש הקרויה signal handler  שגרה זו מוגדרת בקוד התכנית  השגרה מבוצעת ב-User Mode, בהקשר של התהליך שקיבל את ה-signal  הקשר הביצוע של התהליך נשמר לפני התחלת ביצוע השגרה ומשוחזר אחרי סיומה, אך הפעלת השגרה לא גורמת להחלפת הקשר התהליך  במהלך ביצוע השגרה נחסם זמנית (masking) טיפול ב- signals מהסוג שגרם לביצוע השגרה, על-מנת למנוע בעיות של re-enterancy signals ב-Linux

39 מערכות הפעלה - תרגול 739 (c) ארז חדד 2003 קריאות מערכת בסיסיות (1) שליחת signal לתהליך – kill() #include int kill(pid_t pid, int sig); פעולה: ה-signal sig נשלח לתהליך המזוהה ע"י pid  הפעולה תיכשל אם אין תהליך עם מזהה pid  אם sig==0 אז הפעולה רק בודקת שהתהליך pid קיים מבלי לשלוח signal – שימושי לבדיקת תקפות pid פרמטרים:  pid – התהליך אליו מיועד ה-signal  sig – סוג ה-signal ערך מוחזר: 0 בהצלחה, -1 בכישלון תכנית המערכת kill מורכבת למעשה מקריאה לפונקציה kill() signals ב-Linux

40 מערכות הפעלה - תרגול 740 (c) ארז חדד 2003 קריאות מערכת בסיסיות (2) שינוי הטיפול ב-signal – signal() #include typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); פעולה: התקנת handler לטיפול ב-signal שמספרו signum  כאשר מתקבל signal מתאים, תופעל פונקצית ה-handler עם מספר ה-signal כפרמטר פרמטרים:  signum יכול להיות כל מספר signal פרט ל-SIGKILL (9) ו-SIGSTOP (17)  handler יכול להיות מצביע לפונקצית משתמש מתאימה או הערך SIG_DFL שפירושו טיפול ב-signal לפי ברירת המחדל הקבועה במערכת הערך SIG_IGN שפירושו התעלמות – טיפול ב-signal ללא פעולה והמשך הקוד הרגיל ערך מוחזר:  בהצלחה, ערכו הקודם של ה-signal handler (פונקציה קודמת / SIG_DFL / SIG_IGN)  בכישלון, SIG_ERR signals ב-Linux

41 מערכות הפעלה - תרגול 741 (c) ארז חדד 2003 תכנית דוגמה #include void catcher1(int signum) { printf("\nHi"); kill(getpid(), 22); } void catch22(int signum) { printf("\nBye\n"); exit(0); } main() { signal(SIGTERM, catcher1); signal(22, catch22); printf("\nLook & Listen\n"); while(1); } בניית התכנית: $ gcc –g –o signal1 signal1.c הרצת התכנית: $./signal1 & [1] 3189 Look & Listen $ ps PID TTY TIME CMD 3157 pts/2 00:00:00 bash 3189 pts/2 00:00:02 signal1 3190 pts/2 00:00:00 ps $ kill 3189 Hi Bye [1]+ Done./signal1 $ שליחת signal מסוג SIGTERM לתהליך שיצרנו signals ב-Linux

42 מערכות הפעלה - תרגול 742 (c) ארז חדד 2003 חוטים ו-signals קריאת המערכת clone(), כפי שהיא מופעלת מתוך Linux Threads, משתפת את הקישור הקיים ל-signal handlers המותקנים  כל חוט באותו יישום יגיב על אותו signal באותו אופן  קריאות ל-signal() מחוט אחד ישפיעו על תגובות כל החוטים האחרים של אותו יישום עם זאת, יש לזכור שב-Linux כל חוט הוא למעשה תהליך נפרד, וב-Linux Threads יש לכל חוט PID משלו, ולכן kill() תשפיע רק על החוט עם ה-PID המתאים כדי לעבוד עם signals בחוטים, מומלץ להשתמש ב-POSIX API  קריאות כדוגמת pthread_kill() וכו' – נא לקרוא ב-man signals ב-Linux


Download ppt "מערכות הפעלה תרגול 7 – ק/פ ותקשורת תהליכים ב-Linux."

Similar presentations


Ads by Google