Download presentation
Presentation is loading. Please wait.
1
מערכות הפעלה תרגול 7 – ק/פ ותקשורת תהליכים ב-Linux
2
מערכות הפעלה - תרגול 72 (c) ארז חדד 2003 תוכן התרגול קלט/פלט של תהליכים תקשורת וסנכרון בין תהליכים מבוא pipes הכוונת קלט ופלט FIFOs signals
3
מערכות הפעלה - תרגול 73 (c) ארז חדד 2003 קלט/פלט של תהליכים ב-Linux (1) ב-Linux פעולות הקלט/פלט של תהליך מול התקן מבוצעות באמצעות descriptors. descriptor הוא אינדקס של כניסה בטבלה מיוחדת, ששמה העממי PDT (Process Descriptor Table). כל כניסה בטבלה מצביעה על אוביקט ניהול file object)) מטעם התהליך עבור ההתקן המקושר ל-descriptor. הטבלה מוצבעת ממתאר התהליך. Linux מפעיל את כל התקני קלט/פלט האפשריים (התקני חומרה, ערוצי תקשורת, קובצים וכ"ו) באותו אופן באמצעות ממשק תקשורת אחיד דרך ה- descriptor.
4
מערכות הפעלה - תרגול 74 (c) ארז חדד 2003 קלט/פלט של תהליכים ב-Linux (2) ערכי ה-descriptors הבאים מקושרים להתקנים הבאים כברירת מחדל: 0 (stdin) - מקושר לקלט הסטנדרטי, בדרך-כלל המקלדת פעולות הקלט המוכרות, כדוגמת scanf() ודומותיה, הן למעשה פעולות של קריאה מהתקן הקלט הסטנדרטי דרך stdin 1 (stdout) - מקושר לפלט הסטנדרטי, בדרך-כלל תצוגת טקסט במסוף פעולות הפלט המוכרות, כדוגמת printf() ודומותיה, הן למעשה פעולות של כתיבה להתקן הפלט הסטנדרטי דרך stdout 2 (stderr) - מקושר לפלט השגיאות הסטנדרטי, בדרך-כלל גם הוא תצוגת טקסט במסוף ניתן לשנות את קישור ה-descriptors להתקנים באופן דינמי, כפי שנראה בהמשך
5
מערכות הפעלה - תרגול 75 (c) ארז חדד 2003 קריאות מערכת בסיסיות (1) פתיחת התקן לגישה – open() #include int open(const char *path, int flags [, mode_t mode]); פעולה: ההתקן המבוקש (דרך path) נפתח לגישה לפי התכונות המוגדרות ב-flags ולפי הרשאות המוגדרות ב-mode ערך מוחזר: במקרה של הצלחה – ה-descriptor המקושר להתקן שנפתח ( ערך זה הוא ה-descriptor הנמוך ביותר שהיה סגור לפני פתיחת ההתקן), במקרה של כישלון – (-1) קלט/פלט של תהליכים ב-Linux
6
מערכות הפעלה - תרגול 76 (c) ארז חדד 2003 קריאות מערכת בסיסיות (2) פרמטרים: path – מסלול להתקן (או קובץ) לפתיחה. לדוגמה: "file1" לציון הקובץ file1 בספריית העבודה הנוכחית "/dev/ttyS0" לציון התקן תקשורת טורית המחובר למחשב flags – תכונות לאפיון פתיחת הקובץ. חייב להכיל אחת מהאפשרויות הבאות: O_RDONLY – הקובץ נפתח לקריאה בלבד O_WRONLY – הקובץ נפתח לכתיבה בלבד _RDWR O– הקובץ נפתח לקריאה ולכתיבה כמו כן, ניתן לצרף תכונות אופציונליות שימושיות נוספות באמצעות OR (|) עם הדגל המתאים, למשל: O_CREAT – צור את הקובץ אם אינו קיים O_APPEND – שרשר מידע בסוף קובץ קיים mode – פרמטר אופציונלי המגדיר את הרשאות הקובץ, במקרה שפתיחת הקובץ גורמת ליצירת קובץ חדש. הסבר על הערכים ניתן בתרגול על מערכות קבצים קלט/פלט של תהליכים ב-Linux
7
מערכות הפעלה - תרגול 77 (c) ארז חדד 2003 קריאות מערכת בסיסיות (3) סגירת גישה להתקן – close() #include int close(int fd); פעולה: סגירת ה-descriptor fd. לאחר הסגירה לא ניתן לגשת להתקן דרך fd פרמטרים: fd – ה-descriptor המיועד לסגירה ערך מוחזר: הצלחה – 0. כישלון – (-1) קלט/פלט של תהליכים ב-Linux
8
מערכות הפעלה - תרגול 78 (c) ארז חדד 2003 קריאות מערכת בסיסיות (4) קריאת נתונים מהתקן – read() #include ssize_t read(int fd, void *buf, size_t count); פעולה: מנסה לקרוא עד count בתים מתוך ההתקן המקושר ל-fd לתוך החוצץ buf פרמטרים: fd – ה-descriptor המקושר להתקן ממנו מבקשים לקרוא buf – מצביע לחוצץ בו יאוחסנו הנתונים שייקראו מההתקן count – מספר הבתים המבוקש ערך מוחזר:בהצלחה – מספר הבתים שנקרא בפועל מההתקן לתוך buf. אם read() נקראה עם count = 0, יוחזר 0 ללא קריא, בכישלון – (-1) קלט/פלט של תהליכים ב-Linux
9
מערכות הפעלה - תרגול 79 (c) ארז חדד 2003 קריאות מערכת בסיסיות (5) כתיבת נתונים להתקן – write() #include ssize_t write(int fd, const void *buf, size_t count); פעולה: מנסה לכתוב עד count בתים מתוך buf להתקן המקושר ל-fd פרמטרים: fd – ה-descriptor המקושר להתקן אליו מבקשים לכתוב buf – מצביע לחוצץ בו מאוחסנים הנתונים שייכתבו להתקן count – מספר הבתים המבוקש לכתיבה ערך מוחזר: בהצלחה – מספר הבתים שנכתב בפועל להתקן מתוך buf. אם write() נקראה עם count = 0, יוחזר 0 ללא כתיבה בכישלון – (-1) קלט/פלט של תהליכים ב-Linux
10
מערכות הפעלה - תרגול 710 (c) ארז חדד 2003 קריאות מערכת בסיסיות (6) הערות ל-read() : ייתכן שייקראו פחות מ-count בתים (למשל, אם נותרו פחות מ-count בתים בקובץ ממנו קוראים), וייתכן אף שלא ייקראו בתים כלל (למשל, אם בקריאה מקובץ, מחוון הקובץ הגיע לסוף הקובץ (EOF)) מחוון הקובץ מקודם בכמות הבתים שנקראו בפועל מההתקן, כך שבפעולת הגישה הבאה לקובץ (קריאה, כתיבה וכד') ניגש לנתונים שאחרי הנתונים שנקראו בפעולה הנוכחית פעולת הקריאה תחסום את התהליך הקורא (תוריד אותו להמתנה) עד שיהיו נתונים זמינים לקריאה בהתקן הערות ל-write() : כתלות בהתקן, ייתכן שייכתבו בין 0 ל-count בתים (למשל, אם יש מספיק מקום פנוי) בדומה ל-read(), מחוון הקובץ מקודם בכמות הבתים שנכתבו בפועל, והגישה הבאה לקובץ תהיה לנתונים שאחרי אלו שנכתבו גם פעולת write() יכולה לחסום את התהליך בפעולה על התקנים מסוימים – למשל, עד שייתפנה מקום לכתיבה קלט/פלט של תהליכים ב-Linux
11
מערכות הפעלה - תרגול 711 (c) ארז חדד 2003 שיתוף ק/פ בין חוטים ותהליכים (1) חוטים: טבלת ה-descriptors גלובלית לתהליך, כלומר משותפת לכל החוטים שבו מתארי התהליכים של כל החוטים מצביעים על אותו PDT תהליכים: פעולת fork() יוצרת עותק נוסף של טבלת ה- descriptors אצל תהליך הבן. תהליך הבן יכול לגשת לאותם התקנים אליהם ניגש האב ה-descriptors ששוכפלו הינם שותפים, כלומר מצביעים לאותו אובייקט ניהול, ובפרט, חולקים את אותו מחוון קובץ. קלט/פלט של תהליכים ב-Linux
12
מערכות הפעלה - תרגול 712 (c) ארז חדד 2003 שיתוף ק/פ בין חוטים ותהליכים (2) תהליכים (וחוטים) המשתמשים ב-descriptors שותפים או משותפים צריכים לתאם את פעולות הגישה להתקן על- מנת שלא לשבש זה את פעולת זה הבעיה נפוצה במיוחד ב"שושלות משפחתיות" של תהליכים: אבות ובנים, אחים, נכדים וכו' פעולת execve() ודומותיה אינן משנות את טבלת ה- descriptors של התהליך, למרות שהתהליך מאותחל מחדש קבצים והתקנים פתוחים אינם נסגרים שימושי ביותר להכוונת קלט ופלט של תוכניות אחרות קלט/פלט של תהליכים ב-Linux
13
מערכות הפעלה - תרגול 713 (c) ארז חדד 2003 תקשורת בין תהליכים ב-Linux (1) בדומה למערכות הפעלה מודרניות אחרות, Linux מציעה מגוון מנגנונים לתקשורת וסנכרון בין תהליכים IPC = Inter-Process Communication המנגנונים ש-Linux מציעה למטרת תקשורת בין תהליכים כוללים: pipes ו-FIFOs (named pipes): ערוצי תקשורת בין תהליכים באותה מכונה בסגנון יצרן-צרכן. signals: איתותים - הודעות אסינכרוניות הנשלחות בין תהליכים באותה מכונה (וגם ממערכת ההפעלה לתהליכים) על-מנת להודיע לתהליך המקבל על אירוע מסוים. sockets: מנגנון סטנדרטי המאפשר יצירת ערוץ תקשורת דו-כיווני (duplex) בין תהליכים היכולים להימצא גם במכונות שונות.
14
מערכות הפעלה - תרגול 714 (c) ארז חדד 2003 תקשורת בין תהליכים ב-Linux (2) System V IPC: שם כולל לקבוצה של מנגנוני תקשורת שהופיעו לראשונה בגרסאות UNIX של חברת AT&T ואומצו במהרה על-ידי מרבית סוגי ה-UNIX הקיימים כולל Linux. במסגרת קבוצה זו נכללים: סמפורים תורי הודעות (message queues) – מנגנון המאפשר להגדיר "תיבות דואר" וירטואליות הנגישות לכל התהליכים באותה מכונה. תהליכים יכולים לתקשר באמצעות הכנסה והוצאה של הודעות מאותה תיבת דואר זיכרון משותף – יצירת אזור זיכרון מיוחד המשותף למרחבי הזיכרון של מספר תהליכים. זו צורת התקשורת היעילה ביותר והמקובלת ביותר עבור יישומים הדורשים כמות גדולה של IPC במהלך תרגול זה אנו נסקור את מנגנוני ה-pipes וה-signals כדוגמאות למנגנוני IPC
15
מערכות הפעלה - תרגול 715 (c) ארז חדד 2003 pipes ב-Linux (1) pipes (צינורות) הם ערוצי תקשורת חד-כיווניים המאפשרים העברת נתונים לפי סדר FIFO (First-In-First-Out). pipes משמשים גם לסנכרון תהליכים, כפי שנראה בהמשך. המימוש של pipes ב-Linux הוא כאובייקטים של מערכת הקבצים, למרות שאינם צורכים שטח דיסק כלל ואינם מופיעים בהיררכיה של מערכת הקבצים הגישה ל-pipe היא באמצעות שני descriptors: אחד לקריאה ואחד לכתיבה.
16
מערכות הפעלה - תרגול 716 (c) ארז חדד 2003 pipes ב-Linux (2) היצירה של pipe היא באמצעות קריאת המערכת pipe(). ה-pipe הנוצר הינו פרטי לתהליך, כלומר אינו נגיש לתהליכים אחרים במערכת. הדרך היחידה לשתף pipe בין תהליכים שונים היא באמצעות קשרי משפחה תהליך אב יוצר pipe ואחריו יוצר תהליך בן באמצעות fork() – לאב ולבן יש גישה ל-pipe באמצעות ה-descriptors שלו, המצויים בשניהם לאחר סיום השימוש ב-pipe מצד כל התהליכים (סגירת כל ה- descriptors) מפונים משאבי ה-pipe באופן אוטומטי.
17
מערכות הפעלה - תרגול 717 (c) ארז חדד 2003 pipes ב-Linux (3) למי מהתהליכים הבאים יש גישה ל-pipe שיוצר תהליך A? לכל התהליכים פרט ל-B fork() Process A pipe() fork() Process B Process C Process D Process E
18
מערכות הפעלה - תרגול 718 (c) ארז חדד 2003 קריאת המערכת pipe() תחביר: #include int pipe(int filedes[2]); פעולה: יוצרת pipe חדש עם שני descriptors: אחד לקריאה מה- pipe ואחד לכתיבה אליו פרמטרים: filedes – מערך בן שני תאים. ב-filedes[0] יאוחסן ה- descriptor לקריאה מה-pipe שנוצר וב-filedes[1] יאוחסן ה- descriptor לכתיבה ערך מוחזר: 0 בהצלחה ו- (-1) בכישלון pipes ב-Linux
19
מערכות הפעלה - תרגול 719 (c) ארז חדד 2003 קריאה וכתיבה ל-pipe (1) פעולות קריאה וכתיבה מתבצעות באמצעות read() ו-write() על ה-descriptors של ה-pipe. ניתן להסתכל על pipe כמו על תור FIFO עם מצביע קריאה יחיד (להוצאת נתונים) ומצביע כתיבה יחיד (להכנסת נתונים) כל קריאה (מכל תהליך שהוא) מה-pipe מקדמת את מצביע הקריאה. באופן דומה, כל כתיבה מקדמת את מצביע הכתיבה בדרך-כלל, תהליך מבצע רק אחת מהפעולות (קריאה או כתיבה) ולכן נהוג לסגור את ה-descriptor השני שאינו בשימוש. ה-descriptors של הקריאה בכל התהליכים הם שותפים, ולכן יש לתאם בין הקוראים. כנ"ל לגבי ה-descriptors של הכתיבה. pipes ב-Linux
20
מערכות הפעלה - תרגול 720 (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
21
מערכות הפעלה - תרגול 721 (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
22
מערכות הפעלה - תרגול 722 (c) ארז חדד 2003 הכוונת קלט ופלט (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…); execve(“/bin/ls”, …); {
23
מערכות הפעלה - תרגול 723 (c) ארז חדד 2003 הכוונת קלט ופלט (3) קריאת המערכת dup() - שימושית במיוחד לפעולות הכוונת קלט ופלט #include int dup(int oldfd); פעולה: מעתיקה את ה-descriptor oldfd ל-descriptor אחר פנוי (סגור) בטבלה. ה-descriptor החדש הינו ה-descriptor הסגור בעל הערך הנמוך ביותר בטבלה לאחר פעולה מוצלחת, oldfd וה-descriptor החדש הם שותפים פרמטרים: oldfd – ה-descriptor המיועד להעתקה – חייב להיות פתוח לפני ההעתקה. ערך מוחזר:בהצלחה, מוחזר הערך של ה-descriptor החדש,בכישלון מוחזר (-1). דוגמה מפורטת לשימוש ב-dup() וב-pipe() להכוונת קלט ופלט נמצאת בקבצים המצורפים לתרגול: master.c, father.c, son.c - נא לנסות אותה ולהבינה
24
מערכות הפעלה - תרגול 724 (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]);
25
מערכות הפעלה - תרגול 725 (c) ארז חדד 2003 FIFOs ב-Linux (1) FIFO הוא למעשה pipe. ההבדל העיקרי בינו לבין pipe הוא של- FIFO יש "שם" גלובלי שדרכו יכולים כל התהליכים במכונה לגשת אליו – pipe "ציבורי". השימוש העיקרי של FIFO (או של כל אובייקט תקשורת בעל "שם") הוא כאשר תהליכים רוצים לתקשר דרך ערוץ קבוע מראש מבלי שיהיה ביניהם קשרי משפחה. למשל, כאשר תהליכי לקוח צריכים לתקשר עם תהליך שרת FIFO נוצר באמצעות קריאת המערכת mkfifo(), שמעניקה לו את שמו. שם ה-FIFO הוא כשם קובץ במערכת הקבצים, למרות שאיננו קובץ כלל. למשל "/home/yossi/myfifo" ה-FIFO מופיע במערכת הקבצים בשם שנבחר
26
מערכות הפעלה - תרגול 726 (c) ארז חדד 2003 FIFOs ב-Linux (2) לאחר היווצרו, ניתן לגשת ל-FIFO באמצעות פקודת open() ולעבוד איתו כרגיל (קריאה וכתיבה) ניתן לבצע הן קריאה והן כתיבה ל-FIFO דרך אותו descriptor (ערוץ תקשורת דו-כיווני) תהליך שפותח את ה-FIFO לקריאה בלבד נחסם עד שתהליך נוסף יפתח את ה-FIFO לכתיבה, וההפך. פתיחת ה-FIFO לכתיבה וקריאה (O_RDWR) איננה חוסמת. עם זאת, יש לוודא שלכל קריאה יש כותב (אחרת תהליך יחיד ייחסם - קיפאון) כאובייקטים ציבוריים רבים, FIFO אינו מפונה אוטומטית לאחר שהמשתמש האחרון בו סוגר את הקובץ, ולכן יש לפנותו בצורה מפורשת באמצעות פקודות או קריאות מערכת למחיקת קבצים (למשל, פקודת rm או הקריאה unlink()).
27
מערכות הפעלה - תרגול 727 (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) בכישלון
28
מערכות הפעלה - תרגול 728 (c) ארז חדד 2003 signals ב-Linux (1) signal (אות/איתות) הינו הודעה על אירוע הנשלחת לתהליך באופן אסינכרוני ה-signal יכול להשלח לתהליך בכל נקודה בזמן ללא תלות במצב התהליך signals משמשים הן לתקשורת בין תהליכים והן להודעות ממערכת ההפעלה לתהליך על אירועים שונים. כל סוג signal מוגדר לפי שם מתאים וערך מספרי מתאים ומיועד להודעה על סוג מסוים של אירועים למשל: חלוקה ב-0 בקוד תהליך גורמת למערכת ההפעלה לשלוח signal מסוג SIGFPE (מספר 8 - Floating Point Exception) לתהליך תהליך אחד יכול לשלוח signal לתהליך אחר באמצעות קריאת מערכת כדוגמת kill() ישנם 31 signals מוגדרים ב-Linux (ערכים מספריים 1-31) כתאימות הסטורית ל-UNIX
29
מערכות הפעלה - תרגול 729 (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 אם ההמתנה היתה כתוצאה מקריאה חוסמת כלשהי, הקריאה תחזור עם תוצאת כשלון עקב הפרעה
30
מערכות הפעלה - תרגול 730 (c) ארז חדד 2003 טיפול ב-signals (1) תהליך יכול לטפל ב-signal מסוג מסוים בכמה אופנים שונים: terminate – סיום התהליך בתגובה ל-signal dump – בתגובה ל-signal יווצר קובץ dump בשם "core" בספריית העבודה הנוכחית והתהליך ייסתיים ignore – התעלמות - התגובה ל-signal תהיה המשך הביצוע הרגיל stop – עצירת תהליך במצב TASK_STOPPED (בד"כ בשליטת debugger) continue – המשך ביצוע תהליך שהיה במצב TASK_STOPPED (בד"כ בשליטת debugger) signal handler – הפעלת שגרת משתמש מיוחדת בתגובה ל- signal signals ב-Linux
31
מערכות הפעלה - תרגול 731 (c) ארז חדד 2003 טיפול ב-signals (2) מתכנת יכול לגרום לתהליך להגיב על קבלת signal בביצוע שגרה מוגדרת מראש הקרויה signal handler. שגרה זו מוגדרת בקוד התכנית השגרה מבוצעת ב-User Mode, בהקשר של התהליך שקיבל את ה-signal. הקשר הביצוע של התהליך נשמר לפני התחלת ביצוע השגרה ומשוחזר אחרי סיומה, אך הפעלת השגרה לא גורמת להחלפת הקשר התהליך במהלך ביצוע השגרה נחסם זמנית (masking) טיפול ב-signals מהסוג שגרם לביצוע השגרה, על-מנת למנוע בעיות של re-enterancy המתכנת יכול גם לחסום (block) את הטיפול ב-signals מסוגים מסוימים באמצעות קריאות מערכת כדוגמת sigprocmask(). את ה-signals מהסוג SIGKILL (אילוץ סיום תכנית – ברירת מחדל terminate) ו-SIGSTOP (עצירת תכנית בשליטת debugger – ברירת מחדל stop) לא ניתן לתפוס או לחסום. signals ב-Linux
32
מערכות הפעלה - תרגול 732 (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
33
מערכות הפעלה - תרגול 733 (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 handler (פונקציה קודמת / SIG_DFL / SIG_IGN), בכישלון, SIG_ERR signals ב-Linux
34
מערכות הפעלה - תרגול 734 (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
35
מערכות הפעלה - תרגול 735 (c) ארז חדד 2003 חוטים ו-signals קריאת המערכת clone(), כפי שהיא מופעלת מתוך Linux Threads, משתפת את הקישור הקיים ל-signal handlers המותקנים כל חוט באותו יישום יגיב על אותו signal באותו אופן קריאות ל-signal() מחוט אחד ישפיעו על תגובות כל החוטים האחרים של אותו יישום עם זאת, יש לזכור שב-Linux כל חוט הוא למעשה תהליך נפרד, וב-Linux Threads יש לכל חוט PID משלו, ולכן kill() תשפיע רק על החוט עם ה-PID המתאים כדי לעבוד עם signals בחוטים, מומלץ להשתמש ב-POSIX API קריאות כדוגמת pthread_kill() וכו' – נא לקרוא ב-man signals ב-Linux
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.