1 / 35

מערכות הפעלה

מערכות הפעלה. תרגול 8 – ק/פ ותקשורת תהליכים ב- Linux. תוכן התרגול. קלט/פלט של תהליכים תקשורת וסנכרון בין תהליכים מבוא pipes הכוונת קלט ופלט FIFOs signals. קלט/פלט של תהליכים ב- Linux (1). ב- Linux פעולות הקלט/פלט של תהליך מול התקן מבוצעות באמצעות descriptors .

patty
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. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

  2. תוכן התרגול • קלט/פלט של תהליכים • תקשורת וסנכרון בין תהליכים • מבוא • pipes • הכוונת קלט ופלט • FIFOs • signals מערכות הפעלה - תרגול 8

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

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

  5. קריאות מערכת בסיסיות (1) קלט/פלט של תהליכים ב-Linux פתיחת התקן לגישה – open() #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *path, int flags [, mode_t mode]); • פעולה: ההתקן המבוקש (דרך path) נפתח לגישה לפי התכונות המוגדרות ב-flags ולפי הרשאות המוגדרות ב-mode • ערך מוחזר: במקרה של הצלחה – ה-descriptor המקושר להתקן שנפתח (ערך זה הוא ה-descriptor הנמוך ביותר שהיה סגור לפני פתיחת ההתקן), במקרה של כישלון – (-1) מערכות הפעלה - תרגול 8

  6. קריאות מערכת בסיסיות (2) קלט/פלט של תהליכים ב-Linux • פרמטרים: • path – מסלול להתקן (או קובץ) לפתיחה. לדוגמה: • "file1" לציון הקובץ file1 בספריית העבודה הנוכחית • "/dev/ttyS0" לציון התקן תקשורת טורית המחובר למחשב • flags – תכונות לאפיון פתיחת הקובץ. חייב להכיל אחת מהאפשרויות הבאות: • O_RDONLY – הקובץ נפתח לקריאה בלבד • O_WRONLY – הקובץ נפתח לכתיבה בלבד • _RDWRO– הקובץ נפתח לקריאה ולכתיבה כמו כן, ניתן לצרף תכונות אופציונליות שימושיות נוספות באמצעות OR (|) עם הדגל המתאים, למשל: • O_CREAT – צור את הקובץ אם אינו קיים • O_APPEND – שרשר מידע בסוף קובץ קיים • mode – פרמטר אופציונלי המגדיר את הרשאות הקובץ, במקרה שפתיחת הקובץ גורמת ליצירת קובץ חדש. מערכות הפעלה - תרגול 8

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

  8. קריאות מערכת בסיסיות (4) קלט/פלט של תהליכים ב-Linux קריאת נתונים מהתקן – read() #include <unistd.h> ssize_t read(int fd, void *buf, size_t count); • פעולה: מנסה לקרוא עד count בתים מתוך ההתקן המקושר ל-fd לתוך החוצץ buf • פרמטרים: • fd – ה-descriptor המקושר להתקן ממנו מבקשים לקרוא • buf – מצביע לחוצץ בו יאוחסנו הנתונים שייקראו מההתקן • count – מספר הבתים המבוקש • ערך מוחזר:בהצלחה – מספר הבתים שנקרא בפועל מההתקן לתוך buf. אם read() נקראה עם count = 0, יוחזר 0 ללא קריאה, בכישלון – (-1) מערכות הפעלה - תרגול 8

  9. קריאות מערכת בסיסיות (5) קלט/פלט של תהליכים ב-Linux כתיבת נתונים להתקן – write() #include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); • פעולה: מנסה לכתוב עד count בתים מתוך buf להתקן המקושר ל-fd • פרמטרים: • fd – ה-descriptor המקושר להתקן אליו מבקשים לכתוב • buf – מצביע לחוצץ בו מאוחסנים הנתונים שייכתבו להתקן • count – מספר הבתים המבוקש לכתיבה • ערך מוחזר: • בהצלחה – מספר הבתים שנכתב בפועל להתקן מתוך buf. אם write() נקראה עם count = 0, יוחזר 0 ללא כתיבה • בכישלון – (-1) מערכות הפעלה - תרגול 8

  10. קריאות מערכת בסיסיות (6) קלט/פלט של תהליכים ב-Linux • הערות ל-read() : • ייתכן שייקראו פחות מ-count בתים (למשל, אם נותרו פחות מ-count בתים בקובץ ממנו קוראים), וייתכן אף שלא ייקראו בתים כלל (למשל, אם בקריאה מקובץ, מחוון הקובץ הגיע לסוף הקובץ (EOF)) • מחוון הקובץ מקודם בכמות הבתים שנקראו בפועל מההתקן, כך שבפעולת הגישה הבאה לקובץ (קריאה, כתיבה וכד') ניגש לנתונים שאחרי הנתונים שנקראו בפעולה הנוכחית • פעולת הקריאה תחסום את התהליך הקורא (תוריד אותו להמתנה) עד שיהיו נתונים זמינים לקריאה בהתקן • הערות ל-write() : • כתלות בהתקן, ייתכן שייכתבו בין 0 ל-count בתים (למשל, אם אין מספיק מקום פנוי) • בדומה ל-read(), מחוון הקובץ מקודם בכמות הבתים שנכתבו בפועל, והגישה הבאה לקובץ תהיה לנתונים שאחרי אלו שנכתבו • גם פעולת write() יכולה לחסום את התהליך בפעולה על התקנים מסוימים – למשל, עד שיתפנה מקום לכתיבה מערכות הפעלה - תרגול 8

  11. שיתוף ק/פ בין חוטים ותהליכים (1) קלט/פלט של תהליכים ב-Linux • חוטים: טבלת ה-descriptors גלובלית לתהליך, כלומר משותפת לכל החוטים שבו • מתארי התהליכים של כל החוטים מצביעים על אותו PDT • תהליכים: פעולת fork() יוצרת עותק נוסף של טבלת ה-descriptors אצל תהליך הבן. • תהליך הבן יכול לגשת לאותם התקנים אליהם ניגש האב • ה-descriptors ששוכפלו הינם שותפים, כלומר מצביעים לאותו אובייקט ניהול, ובפרט, חולקים את אותו מחוון קובץ. מערכות הפעלה - תרגול 8

  12. שיתוף ק/פ בין חוטים ותהליכים (2) קלט/פלט של תהליכים ב-Linux • תהליכים (וחוטים) המשתמשים ב-descriptors שותפים או משותפים צריכים לתאם את פעולות הגישה להתקן על-מנת שלא לשבש זה את פעולת זה • הבעיה נפוצה במיוחד ב"שושלות משפחתיות" של תהליכים: אבות ובנים, אחים, נכדים וכו' • פעולת execv()ודומותיה אינן משנות את טבלת ה-descriptors של התהליך, למרות שהתהליך מאותחל מחדש • קבצים והתקנים פתוחים אינם נסגרים • שימושי ביותר להכוונת קלט ופלט של תוכניות אחרות מערכות הפעלה - תרגול 8

  13. תקשורת בין תהליכים ב-Linux (1) • בדומה למערכות הפעלה מודרניות אחרות, Linux מציעה מגוון מנגנונים לתקשורת וסנכרון בין תהליכים • IPC = Inter-Process Communication • המנגנונים ש-Linux מציעה למטרת תקשורת בין תהליכים כוללים: • pipesו-FIFOs (named pipes): ערוצי תקשורת בין תהליכים באותה מכונה בסגנון יצרן-צרכן. • signals: איתותים - הודעות אסינכרוניות הנשלחות בין תהליכים באותה מכונה (וגם ממערכת ההפעלה לתהליכים) על-מנת להודיע לתהליך המקבל על אירוע מסוים. • sockets: מנגנון סטנדרטי המאפשר יצירת ערוץ תקשורת דו-כיווני (duplex) בין תהליכים היכולים להימצא גם במכונות שונות. מערכות הפעלה - תרגול 8

  14. תקשורת בין תהליכים ב-Linux (2) • System V IPC: שם כולל לקבוצה של מנגנוני תקשורת שהופיעו לראשונה בגרסאות UNIX של חברת AT&T ואומצו במהרה על-ידי מרבית סוגי ה-UNIX הקיימים כולל Linux. במסגרת קבוצה זו נכללים: • סמפורים • תורי הודעות (message queues) – מנגנון המאפשר להגדיר "תיבות דואר" וירטואליות הנגישות לכל התהליכים באותה מכונה. תהליכים יכולים לתקשר באמצעות הכנסה והוצאה של הודעות מאותה תיבת דואר • זיכרון משותף – יצירת אזור זיכרון מיוחד המשותף למרחבי הזיכרון של מספר תהליכים. זו צורת התקשורת היעילה ביותר והמקובלת ביותר עבור יישומים הדורשים כמות גדולה של IPC • במהלך תרגול זה אנו נסקור את מנגנוני ה-pipesוה-signals כדוגמאות למנגנוני IPC מערכות הפעלה - תרגול 8

  15. pipes ב-Linux (1) • pipes (צינורות) הם ערוצי תקשורת חד-כיווניים המאפשרים העברת נתונים לפי סדר FIFO (First-In-First-Out). • pipes משמשים גם לסנכרון תהליכים, כפי שנראה בהמשך. • המימוש של pipes ב-Linux הוא כאובייקטים של מערכת הקבצים, למרות שאינם צורכים שטח דיסק כלל ואינם מופיעים בהיררכיה של מערכת הקבצים • הגישה ל-pipe היא באמצעות שני descriptors: אחד לקריאה ואחד לכתיבה. מערכות הפעלה - תרגול 8

  16. pipes ב-Linux (2) • היצירה של pipe היא באמצעות קריאת המערכת pipe(). • ה-pipe הנוצר הינו פרטי לתהליך, כלומר אינו נגיש לתהליכים אחרים במערכת. • הדרך היחידה לשתף pipe בין תהליכים שונים היא באמצעות קשרי משפחה • תהליך אב יוצר pipe ואחריו יוצר תהליך בן באמצעות fork() – לאב ולבן יש גישה ל-pipe באמצעות ה-descriptors שלו, המצויים בשניהם • לאחר סיום השימוש ב-pipe מצד כל התהליכים (סגירת כל ה-descriptors) מפונים משאבי ה-pipe באופן אוטומטי. מערכות הפעלה - תרגול 8

  17. pipes ב-Linux (3) • למי מהתהליכים הבאים יש גישה ל-pipe שיוצר תהליך A? • לכל התהליכים פרט ל-B Process A fork() pipe() fork() Process B Process C fork() fork() Process D Process E מערכות הפעלה - תרגול 8

  18. קריאת המערכת pipe() pipes ב-Linux • תחביר: #include <unistd.h> int pipe(int filedes[2]); • פעולה: יוצרת pipe חדש עם שני descriptors: אחד לקריאה מה-pipe ואחד לכתיבה אליו • פרמטרים: • filedes– מערך בן שני תאים. ב-filedes[0] יאוחסן ה-descriptor לקריאה מה-pipe שנוצר וב-filedes[1] יאוחסן ה-descriptor לכתיבה • ערך מוחזר: 0 בהצלחה ו- (-1) בכישלון מערכות הפעלה - תרגול 8

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

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

  21. pipe – תכנית דוגמה pipes ב-Linux #include <stdio.h> #include <unistd.h> 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*sizeof(char)); printf("Got from pipe: %s\n", father_buff); exit(0); } } מה לא בסדר בתכנית? הקריאות ל-read()ו- write() צריכות להיות רב-פעמיות מהו הפלט של התכנית הנ"ל (בהנחה שהיא עובדת)? Got from pipe: Hello מערכות הפעלה - תרגול 8

  22. הכוונת קלט ופלט (1) • אחד השימושים הנפוצים ביותר ב-descriptors בכלל וב-pipes בפרט הינו הכוונת הקלט והפלט של תכניות (Input/Output Redirection) – • תוכנות ה-shell ב-Linux תומכות בהכוונת קלט ופלט באופן מובנה בפקודות שלהן: לדוגמה, הפקודה הבאה תגרום להדפסת רשימת הקבצים בספריה הנוכחית לתוך קובץ myfile במקום ה-stdout:$ ls > myfile • מה (בערך...) ביצע ה-shell בתגובה לפקודה הקודמת? status = fork(); if (status == 0) { close(1); fd = open(“myfile”, O_WRONLY…); execv(“/bin/ls”, …); { מערכות הפעלה - תרגול 8

  23. הכוונת קלט ופלט (2) • קריאת המערכת dup() - שימושית במיוחד לפעולות הכוונת קלט ופלט #include <unistd.h> int dup(int oldfd); • פעולה: מעתיקה את ה-descriptoroldfd ל-descriptor אחר פנוי (סגור) בטבלה. • ה-descriptor החדש הינו ה-descriptor הסגור בעל הערך הנמוך ביותר בטבלה • לאחר פעולה מוצלחת, oldfdוה-descriptor החדש הם שותפים • פרמטרים: oldfd – ה-descriptor המיועד להעתקה – חייב להיות פתוח לפני ההעתקה. • ערך מוחזר:בהצלחה, מוחזר הערך של ה-descriptor החדש,בכישלון מוחזר (-1). • דוגמה מפורטת לשימוש ב-dup()וב-pipe() להכוונת קלט ופלט נמצאת בקבצים המצורפים לתרגול: master.c, father.c, son.c– נסו להבין ולהריץ אותה! מערכות הפעלה - תרגול 8

  24. הכוונת קלט ופלט (3) • כאשר רוצים שהקלט יבוא מתוך (או הפלט ישלח אל( תהליך אחר, מחליפים את התקן הקלט או הפלט ב-pipe בין התהליכים בתור התקן • לדוגמה: אם נרצה שהפלט של ls יודפס בעמודים עם הפסקות: $ ls | more • מה יבצע ה-shell בתגובה לפקודה הנ"ל? (בערך..) status = fork(); if (status == 0) { /* second child */ close(0); dup(fd[0]); close(fd[0]); close(fd[1]); execv(“/bin/more”,..); } close(fd[0]); close(fd[1]); int fd[2]; pipe(fd); status = fork(); if (status == 0) { /* first child */ close(1); dup(fd[1]); close(fd[0]); close(fd[1]); execv(“/bin/ls”,…); } מערכות הפעלה - תרגול 8

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

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

  27. FIFOs ב-Linux (3) • קריאת המערכת mkfifo() #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); • פעולה: יוצרת FIFO המופיע במערכת הקבצים במסלול pathname והרשאות הגישה שלו הן mode • פרמטרים: • pathname – שם ה-FIFO וגם המסלול לקובץ במערכת הקבצים • mode – הרשאות הגישה ל-FIFOשנוצר. ניתן להכניס ערך 0777 (777 אוקטאלי) כדי לקבל הרשאות ברירת מחדל. • ערך מוחזר: 0 בהצלחה, (-1) בכישלון מערכות הפעלה - תרגול 8

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

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

  30. טיפול ב-signals (1) signals ב-Linux • תהליך יכול לטפל ב-signal מסוג מסוים בכמה אופנים שונים: • terminate – סיום התהליך בתגובה ל-signal • dump – בתגובה ל-signalיווצר קובץ dump בשם "core" בספריית העבודה הנוכחית והתהליך ייסתיים • ignore – התעלמות - התגובה ל-signal תהיה המשך הביצוע הרגיל • stop – עצירת תהליך במצב TASK_STOPPED (בד"כ בשליטת debugger) • continue – המשך ביצוע תהליך שהיה במצב TASK_STOPPED (בד"כ בשליטת debugger) • signal handler – הפעלת שגרת משתמש מיוחדת בתגובה ל-signal מערכות הפעלה - תרגול 8

  31. טיפול ב-signals (2) signals ב-Linux • מתכנת יכול לגרום לתהליך להגיב על קבלת signal בביצוע שגרה מוגדרת מראש הקרויה signal handler. • שגרה זו מוגדרת בקוד התכנית • השגרה מבוצעת ב-User Mode, בהקשר של התהליך שקיבל את ה-signal. • הקשר הביצוע של התהליך נשמר לפני התחלת ביצוע השגרה ומשוחזר אחרי סיומה, אך הפעלת השגרה לא גורמת להחלפת הקשר התהליך • במהלך ביצוע השגרה נחסם זמנית (masking) טיפול ב-signals מהסוג שגרם לביצוע השגרה, על-מנת למנוע בעיות של re-enterancy • המתכנת יכול גם לחסום (block) את הטיפול ב-signals מסוגים מסוימים באמצעות קריאות מערכת כדוגמת sigprocmask(). • את ה-signals מהסוג SIGKILL (אילוץ סיום תכנית – ברירת מחדל terminate) ו-SIGSTOP (עצירת תכנית בשליטת debugger – ברירת מחדל stop) לא ניתן לתפוס או לחסום. מערכות הפעלה - תרגול 8

  32. קריאות מערכת בסיסיות (1) signals ב-Linux • שליחת signal לתהליך – kill() #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); • פעולה: ה-signalsig נשלח לתהליך המזוהה ע"י pid • הפעולה תיכשל אם אין תהליך עם מזהה pid • אם sig==0 אז הפעולה רק בודקת שהתהליך pid קיים מבלי לשלוח signal – שימושי לבדיקת תקפות pid • פרמטרים: • pid – התהליך אליו מיועד ה-signal • sig – סוג ה-signal • ערך מוחזר: 0 בהצלחה, -1 בכישלון • תכנית המערכת kill מורכבת למעשה מקריאה לפונקציה kill() מערכות הפעלה - תרגול 8

  33. קריאות מערכת בסיסיות (2) signals ב-Linux • שינוי הטיפול ב-signal – signal() #include <signal.h> 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 handler (פונקציה קודמת / SIG_DFL / SIG_IGN), בכישלון, SIG_ERR מערכות הפעלה - תרגול 8

  34. תכנית דוגמה signals ב-Linux #include <stdio.h> #include <signal.h> 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 לתהליך שיצרנו מערכות הפעלה - תרגול 8

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

More Related