מערכות הפעלה תרגול 8 ק/פ ותקשורת תהליכים ב- Linux
תוכן התרגול קלט/פלט של תהליכים תקשורת וסנכרון בין תהליכים מבוא pipes הכוונת קלט ופלט FIFOs signals 2
קלט/פלט של תהליכים ב- Linux )1( ב- Linux פעולות הקלט/פלט של תהליך מול התקן מבוצעות באמצעות.descriptors descriptor הוא אינדקס של כניסה בטבלה מיוחדת, ששמה העממי.)Process Descriptor Table( PDT )file object) כל כניסה בטבלה מצביעה על אוביקט ניהול מטעם התהליך עבור ההתקן המקושר ל- descriptor. הטבלה מוצבעת ממתאר התהליך. Linux מפעיל את כל התקני קלט/פלט האפשריים )התקני חומרה, ערוצי תקשורת, קובצים וכ"ו( באותו אופן באמצעות ממשק תקשורת אחיד דרך ה-.descriptor 3
קלט/פלט של תהליכים ב- Linux )2( ערכי ה- descriptors הבאים מקושרים להתקנים הבאים כברירת מחדל: )stdin( 0 1 - מקושר לקלט הסטנדרטי, בדרך-כלל המקלדת פעולות הקלט המוכרות, כדוגמת scanf() ודומותיה, הן למעשה פעולות של קריאה מהתקן הקלט הסטנדרטי דרך stdin )stdout( במסוף 2 - מקושר לפלט הסטנדרטי, בדרך-כלל תצוגת טקסט פעולות הפלט המוכרות, כדוגמת printf() ודומותיה, הן למעשה פעולות של כתיבה להתקן הפלט הסטנדרטי דרך stdout )stderr( - מקושר לפלט השגיאות הסטנדרטי, בדרך-כלל גם הוא תצוגת טקסט במסוף ניתן לשנות את קישור ה- descriptors כפי שנראה בהמשך להתקנים באופן דינמי, 4
)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-) 5
)2( קלט/פלט של תהליכים ב- Linux קריאות מערכת בסיסיות פרמטרים: path מסלול להתקן )או קובץ( לפתיחה. לדוגמה: "file1" לציון הקובץ file1 בספריית העבודה הנוכחית "/dev/ttys0" לציון התקן תקשורת טורית המחובר למחשב flags תכונות לאפיון פתיחת הקובץ. חייב להכיל אחת מהאפשרויות הבאות: O_RDONLY הקובץ נפתח לקריאה בלבד O_WRONLY הקובץ נפתח לכתיבה בלבד O _RDWR הקובץ נפתח לקריאה ולכתיבה כמו כן, ניתן לצרף תכונות אופציונליות שימושיות נוספות באמצעות ) ( OR עם הדגל המתאים, למשל: O_CREAT צור את הקובץ אם אינו קיים O_APPEND שרשר מידע בסוף קובץ קיים mode פרמטר אופציונלי המגדיר את הרשאות הקובץ, במקרה שפתיחת הקובץ גורמת ליצירת קובץ חדש. 6
)3( קלט/פלט של תהליכים ב- Linux קריאות מערכת בסיסיות סגירת גישה להתקן #include <unistd.h> int close(int fd); close() פעולה: סגירת ה- descriptor לא ניתן לגשת להתקן דרך fd פרמטרים:.fd fd ה descriptor- המיועד לסגירה ערך מוחזר: הצלחה 0. לאחר הסגירה (-1) כישלון 7
)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() נקראה עם = 0,count יוחזר 0 ללא קריאה, בכישלון (-1) 8
)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() נקראה עם = 0,count יוחזר 0 ללא כתיבה בכישלון (-1) 9
)6( קלט/פלט של תהליכים ב- Linux קריאות מערכת בסיסיות הערות ל-() read : ייתכן שייקראו פחות מ- count בתים )למשל, אם נותרו פחות מ- count בתים בקובץ ממנו קוראים(, וייתכן אף שלא ייקראו בתים כלל )למשל, אם בקריאה מקובץ, מחוון הקובץ הגיע לסוף הקובץ ))EOF( מחוון הקובץ מקודם בכמות הבתים שנקראו בפועל מההתקן, כך שבפעולת הגישה הבאה לקובץ )קריאה, כתיבה וכד'( ניגש לנתונים שאחרי הנתונים שנקראו בפעולה הנוכחית פעולת הקריאה תחסום את התהליך הקורא )תוריד אותו להמתנה( עד שיהיו נתונים זמינים לקריאה בהתקן הערות ל-() write כתלות בהתקן, ייתכן שייכתבו בין 0 ל- count בתים )למשל, אם אין מספיק מקום פנוי( בדומה ל-() read, מחוון הקובץ מקודם בכמות הבתים שנכתבו בפועל, והגישה הבאה לקובץ תהיה לנתונים שאחרי אלו שנכתבו גם פעולת write() יכולה לחסום את התהליך בפעולה על התקנים מסוימים למשל, עד שיתפנה מקום לכתיבה : 10
)1( קלט/פלט של תהליכים ב- Linux שיתוף ק/פ בין חוטים ותהליכים חוטים: טבלת ה- descriptors גלובלית לתהליך, כלומר משותפת לכל החוטים שבו PDT מתארי התהליכים של כל החוטים מצביעים על אותו תהליכים: פעולת fork() יוצרת עותק נוסף של טבלת ה- descriptors אצל תהליך הבן. תהליך הבן יכול לגשת לאותם התקנים אליהם ניגש האב ה descriptors- ששוכפלו הינם שותפים, כלומר מצביעים לאותו אובייקט ניהול, ובפרט, חולקים את אותו מחוון קובץ. 11
)2( קלט/פלט של תהליכים ב- Linux שיתוף ק/פ בין חוטים ותהליכים תהליכים )וחוטים( המשתמשים ב- descriptors שותפים או משותפים צריכים לתאם את פעולות הגישה להתקן על- מנת שלא לשבש זה את פעולת זה הבעיה נפוצה במיוחד ב"שושלות משפחתיות" של תהליכים: אבות ובנים, אחים, נכדים וכו' פעולת execv() ודומותיה אינן משנות את טבלת ה- descriptors של התהליך, למרות שהתהליך מאותחל מחדש קבצים והתקנים פתוחים אינם נסגרים שימושי ביותר להכוונת קלט ופלט של תוכניות אחרות 12
תקשורת בין תהליכים ב- Linux )1( בדומה למערכות הפעלה מודרניות אחרות, Linux מציעה מגוון מנגנונים לתקשורת וסנכרון בין תהליכים Inter-Process Communication = IPC המנגנונים ש- Linux מציעה למטרת תקשורת בין תהליכים כוללים: :)named pipes( ו- FIFOs pipes באותה מכונה בסגנון יצרן-צרכן. ערוצי תקשורת בין תהליכים :signals איתותים - הודעות אסינכרוניות הנשלחות בין תהליכים באותה מכונה )וגם ממערכת ההפעלה לתהליכים( על-מנת להודיע לתהליך המקבל על אירוע מסוים. :sockets מנגנון סטנדרטי המאפשר יצירת ערוץ תקשורת דו-כיווני )duplex( בין תהליכים היכולים להימצא גם במכונות שונות. 13
תקשורת בין תהליכים ב- Linux )2( :System V IPC שם כולל לקבוצה של מנגנוני תקשורת שהופיעו לראשונה בגרסאות UNIX של חברת AT&T ואומצו במהרה על-ידי מרבית סוגי ה- UNIX הקיימים כולל.Linux במסגרת קבוצה זו נכללים: סמפורים תורי הודעות queues( )message מנגנון המאפשר להגדיר "תיבות דואר" וירטואליות הנגישות לכל התהליכים באותה מכונה. תהליכים יכולים לתקשר באמצעות הכנסה והוצאה של הודעות מאותה תיבת דואר זיכרון משותף יצירת אזור זיכרון מיוחד המשותף למרחבי הזיכרון של מספר תהליכים. זו צורת התקשורת היעילה ביותר והמקובלת ביותר עבור יישומים הדורשים כמות גדולה של IPC במהלך תרגול זה אנו נסקור את מנגנוני ה- pipes כדוגמאות למנגנוני IPC וה- signals 14
)1( ב- Linux pipes pipes )צינורות( הם ערוצי תקשורת חד-כיווניים המאפשרים העברת נתונים לפי סדר.)First-In-First-Out( FIFO pipes משמשים גם לסנכרון תהליכים, כפי שנראה בהמשך. המימוש של pipes ב- Linux הוא כאובייקטים של מערכת הקבצים, למרות שאינם צורכים שטח דיסק כלל ואינם מופיעים בהיררכיה של מערכת הקבצים הגישה ל- pipe היא באמצעות שני :descriptors ואחד לכתיבה. אחד לקריאה 15
)2( ב- Linux pipes היצירה של pipe היא באמצעות קריאת המערכת.pipe() ה- pipe הנוצר הינו פרטי לתהליך, כלומר אינו נגיש לתהליכים אחרים במערכת. הדרך היחידה לשתף pipe בין תהליכים שונים היא באמצעות קשרי משפחה fork() ואחריו יוצר תהליך בן באמצעות pipe תהליך אב יוצר לאב ולבן יש גישה ל- pipe באמצעות ה- descriptors שלו, המצויים בשניהם לאחר סיום השימוש ב- pipe מצד כל התהליכים )סגירת כל ה- )descriptors מפונים משאבי ה- pipe באופן אוטומטי. 16
)3( ב- Linux pipes למי מהתהליכים הבאים יש גישה ל- pipe שיוצר תהליך A? Process fork() A pipe() fork() Process B Process C fork() fork() Process D Process E התהליכים פרט ל- B לכל 17
pipe() ב- Linux pipes קריאת המערכת תחביר: פעולה: יוצרת pipe חדש עם שני pipe ואחד לכתיבה אליו פרמטרים: #include <unistd.h> int pipe(int filedes[2]); :descriptors אחד לקריאה מה- filedes מערך בן שני תאים. ב-[ filedes[0 יאוחסן ה- descriptor לקריאה מה- pipe שנוצר וב-[ filedes[1 יאוחסן ה- descriptor לכתיבה ערך מוחזר: 0 בהצלחה ו- (1-) בכישלון 18
)1( ב- Linux pipes קריאה וכתיבה ל- pipe פעולות קריאה וכתיבה מתבצעות באמצעות read() ו-() write על ה- descriptors של ה- pipe. ניתן להסתכל על pipe כמו על תור FIFO עם מצביע קריאה יחיד )להוצאת נתונים( ומצביע כתיבה יחיד )להכנסת נתונים( כל קריאה )מכל תהליך שהוא( מה- pipe מקדמת את מצביע הקריאה. באופן דומה, כל כתיבה מקדמת את מצביע הכתיבה בדרך-כלל, תהליך מבצע רק אחת מהפעולות )קריאה או כתיבה( ולכן נהוג לסגור את ה- descriptor השני שאינו בשימוש. ה- descriptors של הקריאה בכל התהליכים הם שותפים, ולכן יש לתאם בין הקוראים. כנ"ל לגבי ה- descriptors של הכתיבה. 19
)2( ב- Linux pipes קריאה וכתיבה ל- pipe קריאה מ- pipe תחזיר: את כמות הנתונים המבוקשת אם היא נמצאת ב- pipe פחות מהכמות המבוקשת אם זו הכמות הזמינה ב- pipe בזמן הקריאה )EOF( 0 כאשר כל ה- descriptors write נסגרו וה- pipe ריק תחסום את התהליך אם יש כותבים descriptors( )write ל- pipe וה- pipe ריק. כאשר תתבצע כתיבה, יוחזרו הנתונים שנכתבו עד לכמות המבוקשת כתיבה ל- pipe תבצע: אם אין קוראים descriptors( )read הכתיבה תיכשל )ואף ישלח סיגנל בשם Broken pipe לגבי סיגנלים נלמד בהמשך השיעור(. ה- pipe מוגבל בגודלו )כ- 4K (. אם יש מספיק מקום פנוי ב- pipe, תתבצע כתיבה של כל הכמות המבוקשת מיד. אם אין מספיק מקום פנוי ב- pipe, הכתיבה תחסום את התהליך עד שהקוראים האחרים יפנו מקום וניתן יהיה לכתוב את כל הכמות הדרושה. 20
ב- Linux תכנית דוגמה pipes pipe #include <stdio.h> #include <unistd.h> if (status == 0) { /* son process */ close(my_pipe[0]); 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); } } 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 21
הכוונת קלט ופלט )1( מה אחד השימושים הנפוצים ביותר ב- descriptors בכלל וב- pipes בפרט הינו הכוונת הקלט והפלט של תכניות Redirection( )Input/Output תוכנות ה- shell ב- Linux תומכות בהכוונת קלט ופלט באופן מובנה בפקודות שלהן: לדוגמה, הפקודה הבאה תגרום להדפסת רשימת הקבצים בספריה הנוכחית לתוך קובץ myfile במקום ה- stdout : )בערך...( ביצע ה- shell $ ls > myfile בתגובה לפקודה הקודמת? status = fork(); if (status == 0) { close(1); fd = open( myfile, O_WRONLY ); execv( /bin/ls, ); { 22
הכוונת קלט ופלט )2( קריאת המערכת dup() פעולה: מעתיקה את ה- descriptor )סגור( בטבלה. - שימושית במיוחד לפעולות הכוונת קלט ופלט #include <unistd.h> int dup(int oldfd); oldfd ה- descriptor החדש הינו ה- descriptor לאחר פעולה מוצלחת, oldfd ל- descriptor אחר פנוי הסגור בעל הערך הנמוך ביותר בטבלה וה- descriptor החדש הם שותפים פרמטרים: oldfd ה descriptor- המיועד להעתקה פתוח לפני ההעתקה. חייב להיות ערך מוחזר:בהצלחה, מוחזר הערך של ה- descriptor החדש,בכישלון מוחזר (1-). דוגמה מפורטת לשימוש ב-() dup וב-() pipe להכוונת קלט ופלט נמצאת בקבצים המצורפים לתרגול: master.c, father.c, son.c נסו להבין ולהריץ אותה! 23
הכוונת קלט ופלט )3( כאשר רוצים שהקלט יבוא מתוך )או הפלט ישלח אל) תהליך אחר, מחליפים את התקן הקלט או הפלט ב- pipe בין התהליכים בתור התקן לדוגמה: אם נרצה שהפלט של ls יודפס בעמודים עם הפסקות: $ ls more מה יבצע ה- shell בתגובה לפקודה הנ"ל? )בערך..( 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, ); } 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]); 24
)1( ב- Linux FIFOs FIFO הוא למעשה.pipe ההבדל העיקרי בינו לבין pipe הוא של- FIFO יש "שם" גלובלי שדרכו יכולים כל התהליכים במכונה לגשת אליו pipe "ציבורי". השימוש העיקרי של FIFO )או של כל אובייקט תקשורת בעל "שם"( הוא כאשר תהליכים רוצים לתקשר דרך ערוץ קבוע מראש מבלי שיהיה ביניהם קשרי משפחה. למשל, כאשר תהליכי לקוח צריכים לתקשר עם תהליך שרת FIFO נוצר באמצעות קריאת המערכת,mkfifo() שמעניקה לו את שמו. שם ה- FIFO הוא כשם קובץ במערכת הקבצים, למרות שאיננו קובץ כלל. למשל "/home/yossi/myfifo" ה- FIFO מופיע במערכת הקבצים בשם שנבחר 25
)2( ב- Linux FIFOs לאחר היווצרו, ניתן לגשת ל- FIFO באמצעות פקודת ולעבוד איתו כרגיל )קריאה וכתיבה( open() ניתן לבצע הן קריאה והן כתיבה ל- FIFO דרך אותו descriptor )ערוץ תקשורת דו-כיווני( תהליך שפותח את ה- FIFO לקריאה בלבד נחסם עד שתהליך נוסף יפתח את ה- FIFO לכתיבה, וההפך. פתיחת ה- FIFO לכתיבה וקריאה )O_RDWR( איננה חוסמת. עם זאת, יש לוודא שלכל קריאה יש כותב )אחרת תהליך יחיד ייחסם - קיפאון( כאובייקטים ציבוריים רבים, FIFO אינו מפונה אוטומטית לאחר שהמשתמש האחרון בו סוגר את הקובץ, ולכן יש לפנותו בצורה מפורשת באמצעות פקודות או קריאות מערכת למחיקת קבצים )למשל, פקודת rm או הקריאה.)unlink() 26
)3( ב- Linux FIFOs קריאת המערכת mkfifo() #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); פעולה: יוצרת FIFO המופיע במערכת הקבצים במסלול pathname והרשאות הגישה שלו הן mode פרמטרים: pathname שם ה- FIFO וגם המסלול לקובץ במערכת הקבצים mode הרשאות הגישה ל- FIFO שנוצר. ניתן להכניס ערך )777 0777 אוקטאלי( כדי לקבל הרשאות ברירת מחדל. ערך מוחזר: 0 בהצלחה, (1-) בכישלון 27
)1( ב- Linux signals signal )אות/איתות( הינו הודעה על אירוע הנשלחת לתהליך באופן אסינכרוני 28 ה- signal יכול להשלח לתהליך בכל נקודה בזמן ללא תלות במצב התהליך signals משמשים הן לתקשורת בין תהליכים והן להודעות ממערכת ההפעלה לתהליך על אירועים שונים. כל סוג signal מוגדר לפי שם מתאים וערך מספרי מתאים ומיועד להודעה על סוג מסוים של אירועים למשל: חלוקה ב- 0 בקוד תהליך גורמת למערכת ההפעלה לשלוח signal מסוג SIGFPE )מספר - 8 Exception )Floating Point לתהליך תהליך אחד יכול לשלוח signal לתהליך אחר באמצעות קריאת מערכת כדוגמת kill() ישנם signals 31 מוגדרים ב- Linux כתאימות הסטורית ל- UNIX )ערכים מספריים 1-31(
)2( ב- Linux signals signal שנשלח לתהליך כלשהו אבל עדיין לא טופל נקרא signal )אות ממתין(. pending כל ה- signals pending של אותו תהליך נשמרים ע"י מערכת ההפעלה עד לטיפול בהם ואח"כ נמחקים יכול להיות לכל היותר pending signal מספרי( לתהליך נתון. אחד מאותו סוג )אותו ערך אם יתקבלו signals נוספים מאותו ערך לפני שהראשון טופל, הם יבוטלו ע"י מערכת ההפעלה בדיקה וטיפול ב- signals pending מבוצעים בכל פעם שהתהליך עובר מ- Mode Kernel ל- Mode User במהלך הריצה שלו. תהליך ממתין במצב TASK_INTERRUPTIBLE יוחזר למצב ריצה )TASK_RUNNING( אם יש לו pending signals אם ההמתנה היתה כתוצאה מקריאה חוסמת כלשהי, הקריאה תחזור עם תוצאת כשלון עקב הפרעה 29
)1( ב- Linux signals טיפול ב- signals תהליך יכול לטפל ב- signal מסוג מסוים בכמה אופנים שונים: סיום התהליך בתגובה ל- signal terminate "core" בשם dump קובץ יווצר בתגובה ל- signal dump בספריית העבודה הנוכחית והתהליך ייסתיים ignore התעלמות - התגובה ל- signal תהיה המשך הביצוע הרגיל stop עצירת תהליך במצב TASK_STOPPED )בד"כ בשליטת )debugger TASK_STOPPED המשך ביצוע תהליך שהיה במצב continue )בד"כ בשליטת )debugger signal handler הפעלת שגרת משתמש מיוחדת בתגובה ל- signal 30
)2( ב- Linux signals טיפול ב- signals מתכנת יכול לגרום לתהליך להגיב על קבלת signal מוגדרת מראש הקרויה.signal handler בביצוע שגרה שגרה זו מוגדרת בקוד התכנית השגרה מבוצעת ב- Mode,User בהקשר של התהליך שקיבל את ה- signal. הקשר הביצוע של התהליך נשמר לפני התחלת ביצוע השגרה ומשוחזר אחרי סיומה, אך הפעלת השגרה לא גורמת להחלפת הקשר התהליך במהלך ביצוע השגרה נחסם זמנית )masking( טיפול ב- signals מהסוג שגרם לביצוע השגרה, על-מנת למנוע בעיות של re-enterancy המתכנת יכול גם לחסום )block( את הטיפול ב- signals מסוגים מסוימים באמצעות קריאות מערכת כדוגמת.sigprocmask() את ה- signals מהסוג SIGKILL )אילוץ סיום תכנית ברירת מחדל )terminate ו- SIGSTOP )עצירת תכנית בשליטת debugger ברירת מחדל )stop לא ניתן לתפוס או לחסום. 31
)1( ב- Linux signals קריאות מערכת בסיסיות שליחת #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); kill() signal לתהליך פעולה: ה- signal sig נשלח לתהליך המזוהה ע"י pid הפעולה תיכשל אם אין תהליך עם מזהה pid אם sig==0 אז הפעולה רק בודקת שהתהליך pid קיים מבלי לשלוח signal שימושי לבדיקת תקפות pid פרמטרים: התהליך אליו מיועד ה- signal pid סוג ה- signal sig ערך מוחזר: 0 בהצלחה, 1- בכישלון תכנית המערכת kill מורכבת למעשה מקריאה לפונקציה kill() 32
)2( ב- Linux signals קריאות מערכת בסיסיות שינוי הטיפול ב- signal signal() #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); פעולה: התקנת handler לטיפול ב- signal שמספרו signum )כאשר מתקבל signal מתאים, תופעל פונקצית ה- handler עם מספר ה- signal כפרמטר( פרמטרים: )17( ו- SIGSTOP )9( פרט ל- SIGKILL signal יכול להיות כל מספר signum SIG_DFL יכול להיות מצביע לפונקצית משתמש מתאימה או הערך handler שפירושו טיפול ב- signal לפי ברירת המחדל הקבועה במערכת, או הערך SIG_IGN שפירושו התעלמות. ערך מוחזר: בהצלחה, ערכו הקודם של ה- handler signal קודמת / SIG_DFL,)SIG_IGN / בכישלון, SIG_ERR )פונקציה 33
ב- Linux signals תכנית דוגמה #include <stdio.h> #include <signal.h> void catcher1(int signum) { printf("\nhi"); kill(getpid(), 22); } בניית התכנית: $ gcc g o signal1 signal1.c הרצת התכנית: $./signal1 & [1] 3189 Look & Listen void catch22(int signum) { printf("\nbye\n"); exit(0); } main() { signal(sigterm, catcher1); signal(22, catch22); printf("\nlook & Listen\n"); while(1); } $ 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 לתהליך שיצרנו 34
ב- Linux signals חוטים ו- signals קריאת המערכת,clone() כפי שהיא מופעלת מתוך Linux signal משתפת את הקישור הקיים ל- handlers,threads המותקנים כל חוט באותו יישום יגיב על אותו signal באותו אופן קריאות ל-() signal מחוט אחד ישפיעו על תגובות כל החוטים האחרים של אותו יישום עם זאת, יש לזכור שב- Linux כל חוט הוא למעשה תהליך נפרד, וב- Threads Linux יש לכל חוט PID משלו, ולכן kill() תשפיע רק על החוט עם ה- PID המתאים כדי לעבוד עם signals בחוטים, מומלץ להשתמש ב- API POSIX קריאות כדוגמת pthread_kill() וכו' נא לקרוא ב- man 35