כיצד להשתמש במפעילי אותות בשפת C?

How Use Signal Handlers C Language



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

אוֹת

אות הוא אירוע שנוצר כדי להודיע ​​לתהליך או שרשור שהגיע מצב חשוב כלשהו. כאשר תהליך או חוט קיבל אות, התהליך או השרשור יעצור את מה שהוא עושה וינקוט פעולה כלשהי. האות עשוי להיות שימושי לתקשורת בין תהליכים.







אותות סטנדרטיים

האותות מוגדרים בקובץ הכותרת signal.h כקבוע מאקרו. שם האות התחיל ב- SIG ואחריו תיאור קצר של האות. אז לכל אות יש ערך מספרי ייחודי. התוכנית שלך תמיד צריכה להשתמש בשם האותות, ולא במספר האותות. הסיבה היא שמספר האות יכול להשתנות בהתאם למערכת אך משמעות השמות תהיה סטנדרטית.



המאקרו NSIG הוא המספר הכולל של האות המוגדר. הערך של NSIG הוא אחד מהמספר הכולל של האות שהוגדר (כל מספרי האות מוקצים ברצף).



להלן האותות הסטנדרטיים:





שם האות תיאור
הרשמה נתק את התהליך. האות SIGHUP משמש לדיווח על ניתוק הטרמינל של המשתמש, אולי בגלל שחיבור מרוחק אבד או נתקע.
סימן קטע את התהליך. כאשר המשתמש מקליד את תו ה- INTR (בדרך כלל Ctrl + C) האות SIGINT נשלח.
SIGQUIT צא מהתהליך. כאשר המשתמש מקליד את תו ה- QUIT (בדרך כלל Ctrl + ) האות SIGQUIT נשלח.
חותם הוראה לא חוקית. כאשר נעשה ניסיון לבצע זבל או הוראה מיוחסת, האות SIGILL נוצר. כמו כן, ניתן להפיק SIGILL כאשר הערימה עולה על גדותיה, או כאשר המערכת מתקשה להפעיל מטפל אותות.
סיגראפ מלכודת עקבות. הוראת נקודת שבירה והוראת מלכודת אחרת תייצר את האות SIGTRAP. המאגר משתמש באות זה.
SIGABRT לְהַפִּיל. האות SIGABRT נוצר כאשר נקראת פונקציית abort (). אות זה מציין שגיאה שמזהה התוכנית עצמה ומדווחת על ידי קריאת הפונקציה abort ().
SIGFPE חריגה בנקודה צפה. כאשר אירעה שגיאה אריתמטית קטלנית האות SIGFPE נוצר.
SIGUSR1 ו- SIGUSR2 ניתן להשתמש באותות SIGUSR1 ו- SIGUSR2 כרצונך. כדאי לכתוב עבורם מטפל אותות בתוכנית המקבלת את האות לתקשורת פשוטה בין-תהליכים.

פעולת ברירת מחדל של אותות

לכל אות יש פעולת ברירת מחדל, אחת מהפעולות הבאות:

טווח: התהליך יסתיים.
הליבה: התהליך יסתיים ויפיק קובץ dump של הליבה.
הצתה: התהליך יתעלם מהאות.
תפסיק: התהליך ייפסק.
חֶשְׁבּוֹן: התהליך ימשיך וייעצר.



ניתן לשנות את פעולת ברירת המחדל באמצעות פונקציית המטפל. לא ניתן לשנות את פעולת ברירת המחדל של אות כלשהי. SIGKILL ו SIGABRT לא ניתן לשנות או להתעלם מפעולת ברירת המחדל של האות.

טיפול באותות

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

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

אנו יכולים להתמודד עם האות באמצעות אוֹת אוֹ חתימה פוּנקצִיָה. כאן אנו רואים כיצד הפשוט ביותר אוֹת() הפונקציה משמשת לטיפול באותות.

intאוֹת() (intסִימָן, בָּטֵל (*פוּנקצִיָה)(int))

ה אוֹת() יתקשר ל פוּנקצִיָה פונקציה אם התהליך מקבל אות סִימָן . ה אוֹת() מחזיר מצביע לתפקוד פוּנקצִיָה אם הוא מוצלח או שהוא מחזיר שגיאה ל- errno ו- -1 אחרת.

ה פוּנקצִיָה מצביע יכול להיות בעל שלושה ערכים:

  1. SIG_DFL : זהו מצביע על פונקציית ברירת המחדל של המערכת SIG_DFL () , הכריז ב ח קובץ הכותרת. הוא משמש לביצוע פעולות ברירת המחדל של האות.
  2. SIG_IGN : זה מצביע על פונקציית התעלמות המערכת SIG_IGN () , הכריז ב ח קובץ הכותרת.
  3. מצביע פונקציית מטפל המוגדר על ידי המשתמש : סוג פונקציית המטפל שהוגדר על ידי המשתמש הוא void (*) (int) , פירושו סוג ההחזרה הוא בטל וטענה אחת מסוג int.

דוגמה בסיסית של מטפל באותות

#לִכלוֹל
#לִכלוֹל
#לִכלוֹל
בָּטֵלמטפל sig_handler(intסִימָן){

// סוג ההחזרה של פונקציית המטפל צריך להיות בטל
printf (' nפונקציית מטפל בתוך n');
}

intרָאשִׁי(){
אוֹת(סימן,מטפל sig_handler); // רשום מטפל אותות
ל(intאני=1;;אני++){ //לולאה אינסופית
printf ('%d: בפונקציה העיקרית בפנים n',אני);
לִישׁוֹן(1); // עיכוב לשנייה אחת
}
לַחֲזוֹר 0;
}

בצילום המסך של הפלט של דוגמא 1.c, אנו יכולים לראות שבפונקציה העיקרית לולאה אינסופית מבצעת. כאשר המשתמש הקליד Ctrl+C, הפעלת הפונקציה הראשית נעצרת ופונקציית המטפל של האות מופעלת. לאחר השלמת פונקציית המטפל, פעולת הפונקציה הראשית התחדשה. כאשר סוג המשתמש הקליד Ctrl+, התהליך מופסק.

התעלם מדוגמאות אותות

#לִכלוֹל
#לִכלוֹל
#לִכלוֹל
intרָאשִׁי(){
אוֹת(סימן,SIG_IGN); // רשום מטפל אותות להתעלמות מהאות

ל(intאני=1;;אני++){ //לולאה אינסופית
printf ('%d: בפונקציה העיקרית בפנים n',אני);
לִישׁוֹן(1); // עיכוב לשנייה אחת
}
לַחֲזוֹר 0;
}

כאן פונקציית המטפל היא הרשמה ל- SIG_IGN () פונקציה להתעלמות מפעולת האות. לכן, כאשר המשתמש הקליד Ctrl+C, סימן האות יוצר אך מתעלמים מהפעולה.

רישום מחדש של מטפל איתותים מחדש

#לִכלוֹל
#לִכלוֹל
#לִכלוֹל

בָּטֵלמטפל sig_handler(intסִימָן){
printf (' nפונקציית מטפל בתוך n');
אוֹת(סימן,SIG_DFL); // רשום מחדש מטפל אותות לפעולת ברירת המחדל
}

intרָאשִׁי(){
אוֹת(סימן,מטפל sig_handler); // רשום מטפל אותות
ל(intאני=1;;אני++){ //לולאה אינסופית
printf ('%d: בפונקציה העיקרית בפנים n',אני);
לִישׁוֹן(1); // עיכוב לשנייה אחת
}
לַחֲזוֹר 0;
}

בצילום המסך של הפלט של דוגמה 3.c, אנו יכולים לראות שכאשר המשתמש הקליד בפעם הראשונה Ctrl+C, הפונקציה מטפלת הופעלה. בפונקציית המטפל, מטפל האותות נרשם מחדש אל SIG_DFL לפעולת ברירת המחדל של האות. כאשר המשתמש הקליד Ctrl+C בפעם השנייה, התהליך מסתיים, וזו פעולת ברירת המחדל של סימן אוֹת.

שליחת אותות:

תהליך יכול גם לשלוח במפורש אותות לעצמו או לתהליך אחר. ניתן להשתמש בפונקציה raise () ו- kill () לשליחת אותות. שתי הפונקציות מוכרזות בקובץ הכותרת signal.h.

int הַעֲלָאָה (intסִימָן)

פונקציית ההעלאה () המשמשת לשליחת אות סִימָן לתהליך הקריאה (עצמו). הוא מחזיר אפס אם הוא מצליח וערך ללא אפס אם הוא נכשל.

intלַהֲרוֹג(pid_t pid, intסִימָן)

פונקציית ההרג המשמשת לשליחת אות סִימָן לתהליך או לקבוצת תהליכים שצוינו על ידי pid .

דוגמת מטפל אותות SIGUSR1

#לִכלוֹל
#לִכלוֹל

בָּטֵלמטפל sig_handler(intסִימָן){
printf ('פונקציית מטפל בתוך n');
}

intרָאשִׁי(){
אוֹת(SIGUSR1,מטפל sig_handler); // רשום מטפל אותות
printf ('בפונקציה העיקרית בפנים n');
הַעֲלָאָה (SIGUSR1);
printf ('בפונקציה העיקרית בפנים n');
לַחֲזוֹר 0;
}

כאן התהליך שולח לעצמו אות SIGUSR1 באמצעות פונקציית העלאה ().

העלה עם תוכנית דוגמת Kill

#לִכלוֹל
#לִכלוֹל
#לִכלוֹל
בָּטֵלמטפל sig_handler(intסִימָן){
printf ('פונקציית מטפל בתוך n');
}

intרָאשִׁי(){
pid_t pid;
אוֹת(SIGUSR1,מטפל sig_handler); // רשום מטפל אותות
printf ('בפונקציה העיקרית בפנים n');
pid=חולה(); // מזהה תהליך של עצמו
לַהֲרוֹג(pid,SIGUSR1); // שלח את SIGUSR1 לעצמו
printf ('בפונקציה העיקרית בפנים n');
לַחֲזוֹר 0;
}

הנה, התהליך נשלח SIGUSR1 לאות לעצמו באמצעות לַהֲרוֹג() פוּנקצִיָה. getpid () משמש כדי לקבל את מזהה התהליך של עצמו.

בדוגמה הבאה נראה כיצד תהליכי הורים וילדים מתקשרים (תקשורת בין תהליכים) באמצעות לַהֲרוֹג() ופונקציית האות.

תקשורת בין ילדים להורים עם אותות

#לִכלוֹל
#לִכלוֹל
#לִכלוֹל
#לִכלוֹל
בָּטֵלsig_handler_parent(intסִימָן){
printf ('הורה: קיבל אות תגובה מילד n');
}

בָּטֵלsig_handler_child(intסִימָן){
printf ('ילד: קיבל אות מההורה n');
לִישׁוֹן(1);
לַהֲרוֹג(להתבאס(),SIGUSR1);
}

intרָאשִׁי(){
pid_t pid;
אם((pid=מזלג())<0){
printf ('מזלג נכשל n');
יְצִיאָה (1);
}
/ * תהליך ילדים */
אַחֵר אם(pid==0){
אוֹת(SIGUSR1,sig_handler_child); // רשום מטפל אותות
printf ('ילד: מחכה לאות n');
הַפסָקָה();
}
/ * תהליך הורים */
אַחֵר{
אוֹת(SIGUSR1,sig_handler_parent); // רשום מטפל אותות
לִישׁוֹן(1);
printf ('הורה: שולח אות לילד n');
לַהֲרוֹג(pid,SIGUSR1);
printf ('הורה: מחכה לתגובה n');
הַפסָקָה();
}
לַחֲזוֹר 0;
}

פה, מזלג() פונקציה יוצרת תהליך ילדים וחוזרת אפס לתהליך הילד ותעודת זהות לתהליך הילד. לכן, pid נבדק כדי להחליט על תהליך ההורה והילד. בתהליך האב, הוא ישן למשך שנייה אחת, כך שתהליך הילד יכול לרשום את פונקציית מטפל האותות ולחכות לאות מההורה. לאחר תהליך הורה שני שני שלח SIGUSR1 תהליך האות לילד והמתן לאות התגובה מהילד. בתהליך הילד, תחילה הוא מחכה לאות מההורה וכאשר מתקבל האות, מופעלת פונקציית המטפל. מתפקוד המטפל, תהליך הילד שולח אחר SIGUSR1 איתות להורה. פה להתבאס () הפונקציה משמשת לקבלת מזהה תהליך האב.

סיכום

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