UNIX再学习 -- 信号处理
生活随笔
收集整理的這篇文章主要介紹了
UNIX再学习 -- 信号处理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、鬧鐘和睡眠
1、函數 alarm
#include <unistd.h> unsigned int alarm(unsigned int seconds); 返回值:返回 0 或先前所設鬧鐘的剩余秒數(1)函數功能
使用 alarm 函數可以設置一個定時器(鬧鐘時間),在將來的某個時刻該定時器會超時。當定時器超時時,產生 SIGALRM 信號。如果忽略或不捕捉此信號,則其默認動作是終止調用該 alarm 函數的進程。(2)參數解析
參數 seconds 的值是產生信號 SIGALRM 需要經過的時鐘秒數。當這個時刻到達時,信號由內核產生,由于進程調度的延遲,所以進程得到控制從而能夠處理該信號還需要一個時間間隔。(3)函數解析
每個進程只能有一個鬧鐘時間。如果在調用 alarm 時,之前已為該進程注冊的鬧鐘時間還沒有超時,則該鬧鐘時間的余值作為本次 alarm 函數調用的值返回。以前注冊的鬧鐘時間則被新值代替。 如果有以前注冊的尚未超過的鬧鐘時間,而且本次調用的 seconds 值是 0,則取消以前的鬧鐘時間,其余留值仍作為 alarm 函數的返回值。 雖然 SIGALRM 的默認動作是終止進程,但是大多數使用鬧鐘的進程捕捉此信號。如果此時進程要終止,則在終止之前它可以執行所需的清理操作。如果我們想捕捉 SIGALRM 信號,則必須在調用 alarm 之前安裝該信號的處理程序。如果我們先調用 alarm,然后在我們能夠安裝 SIGALRM 處理程序之前已接到該信號,那么進程將終止。(4)示例說明
//示例一 //alarm函數的使用 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/types.h>void fa(int signo) {printf("捕獲到了信號%d\n",signo);//設置1秒后發送信號alarm(1); }int main(void) {//設置SIGALRM信號進行自定義處理signal(SIGALRM,fa);//設置5秒后發送SIGALRM信號int res = alarm(5);printf("res = %d\n",res);//0sleep(2);//重新修改為10秒后發送SIGALRM信號res = alarm(10);printf("res = %d\n",res); //3/*sleep(3);//重新設置為0秒后,取消之前的鬧鐘res = alarm(0);printf("res = %d\n",res); //7*/while(1);return 0; } 輸出結果: res = 0 res = 3 捕獲到了信號 14 捕獲到了信號 14 捕獲到了信號 14 捕獲到了信號 14 捕獲到了信號 14 捕獲到了信號 14 捕獲到了信號 14 捕獲到了信號 14 //示例二 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <time.h>void sigalrm (int signum) {time_t t = time (NULL);struct tm *it = localtime (&t);printf ("\n%02d:%02d:%02d 按回車鍵退出\n", it->tm_hour, it->tm_min, it->tm_sec);alarm (1); }int main (void) {if (signal (SIGALRM, sigalrm) == SIG_ERR)perror ("signal"), exit (1);sigalrm (SIGALRM);getchar ();return 0; } 輸出結果: 14:07:37 按回車鍵退出14:07:38 按回車鍵退出14:07:39 按回車鍵退出14:07:40 按回車鍵退出(5)示例解析
示例一,alarm 設置 5 秒結束后,發送 SIGALRM 信號,如果鬧鐘時間沒有結束,又設了鬧鐘,則返回剩余鬧鐘秒數。alarm 參數為 0,則取消鬧鐘。 示例二,sigalrm (SIGARLM);函數運行,執行到 alarm (1); 產生 SIGALRM 信號,觸發 signal。然后就開始循環。2、函數 pause
#include <unistd.h> int pause(void); 成功阻塞,失敗返回 -1(1)函數功能
pause 函數使調用進程掛起直至捕捉到一個信號。(無限睡眠)(2)函數解析
該函數使調用 進程/線程 進入無時限的睡眠狀態,直到有信號終止了調用進程或被其捕獲。 如果有信號被調用進程捕獲,在信號處理函數返回以后,pause 函數才會返回,且返回值為 -1,同時置 errno 為 EINTR,表示阻塞的系統調用被信號中斷。 pause 函數要么不返回,要么返回失敗,不會返回成功。(3)示例說明
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <errno.h>void sigint (int signo) {printf ("\n中斷符號被發送\n"); }int main (void) {if (signal (SIGINT, sigint) == SIG_ERR)perror ("signal"), exit (1);printf ("按 ctrl+c 繼續\n");if (pause () != -1 && errno != EINTR)perror ("pause"), exit (1);printf ("進程繼續\n");return 0; } 輸出結果: 按 ctrl+c 繼續 ^C 中斷符號被發送 進程繼續(4)示例解析
當有信號被當前進程捕獲,在信號處理函數返回以后,pause函數返回,且返回值為-1,同時置errno為EINTR,表示阻塞的系統調用被信號中斷。3、函數 sleep
#include <unistd.h>unsigned int sleep(unsigned int seconds); 返回 0 或剩余秒數(1)函數功能
有限睡眠(2)參數解析
參數 seconds 以秒為單位的睡眠時限。?(3)函數解析
該函數使調用進程/線程睡眠 seconds 秒,除非有信號終止了調用進程或被其捕獲。 如果有信號被調用進程捕獲,在信號處理函數返回以后,sleep 函數才會返回,且返回值為剩余的秒數,否則該函數將返回 0,表示睡眠充足。如果由于捕獲到某個信號 sleep 提早返回時,返回值是未休眠完的秒數(所要求的時間減去實際休眠時間)。(4)示例說明
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <errno.h>void sigint (int signo) {printf ("\n中斷符號被發送\n"); }int main (void) {if (signal (SIGINT, sigint) == SIG_ERR)perror ("signal"), exit (1);printf ("按 ctrl+c 繼續\n");int res = sleep (60);if (res)printf ("進程被提前%d秒叫醒\n", res);printf ("進程繼續\n");return 0; } 輸出結果: 按 ctrl+c 繼續 ^C 中斷符號被發送 進程被提前57秒叫醒 進程繼續(5)sleep 函數簡單實現
#include "apue.h"static void sig_alrm(int signo) {/* nothing to do, just returning wakes up sigsuspend() */ }unsigned int my_sleep(unsigned int seconds) {struct sigaction newact, oldact;sigset_t newmask, oldmask, suspmask;unsigned int unslept;/* set our handler, save previous information */newact.sa_handler = sig_alrm;sigemptyset(&newact.sa_mask);newact.sa_flags = 0;sigaction(SIGALRM, &newact, &oldact);/* block SIGALRM and save current signal mask */sigemptyset(&newmask);sigaddset(&newmask, SIGALRM);sigprocmask(SIG_BLOCK, &newmask, &oldmask);alarm(seconds);suspmask = oldmask;/* make sure SIGALRM isn't blocked */sigdelset(&suspmask, SIGALRM);/* wait for any signal to be caught */sigsuspend(&suspmask);/* some signal has been caught, SIGALRM is now blocked */unslept = alarm(0);/* reset previous action */sigaction(SIGALRM, &oldact, NULL);/* reset signal mask, which unblocks SIGALRM */sigprocmask(SIG_SETMASK, &oldmask, NULL);return(unslept); }int main (void) {my_sleep (10);printf ("hello world!\n");return 0; } 輸出結果: hello world!4、函數 nanosleep
#include <time.h> int nanosleep(const struct timespec *req, struct timespec *rem); 返回值:若休眠到要求的時間,返回 0,若出錯,返回 -1struct timespec {time_t ?tv_sec; ? ? ? ? /* seconds */long ? ?tv_nsec; ? ? ? ?/* nanoseconds */ };(1)函數功能
nanosleep 函數與 sleep 函數類似,但是提供了納秒級的精度。(2)參數解析
這個函數掛起調用進程,直到要求的時間已經超時或者某個信號中斷了該函數。 req 參數用秒和納秒指定了需要休眠的時間長度。如果某個信號中斷了休眠間隔,進程并沒有終止,rem 參數指向的 timespec 結構就會被設置為未休眠完的時間長度。如果對未休眠完的時間并不感興趣,可以把該參數置為 NULL。如果系統并不支持納秒這一精度,要求的時間就會取整。因為,nanosleep 函數并不涉及產生任何信號,所以不需要擔心與其函數的交互。 參看:C語言再學習 -- 時間函數
(3)示例說明
#include <stdio.h> #include <time.h> #include <stdlib.h> #include <sys/time.h> int main (void) {struct timespec req, rem;rem.tv_sec = 0; rem.tv_nsec = 0; req.tv_sec = 0; req.tv_nsec = 1000000; int res = 0;struct timeval start,end; gettimeofday( &start, NULL ); /*測試起始時間*/ if (res = (nanosleep (&req, &rem)) == -1)perror ("nanosleep"), exit (1);gettimeofday( &end, NULL ); /*測試終止時間*/ int timeuse = (end.tv_usec - start.tv_usec); printf("運行時間為:%d us\n",timeuse); return 0; } 輸出結果: 運行時間為:1173 us5、sleep、usleep 和 nanosleep 區別
參看:linux下nanosleep() & sleep()的區別 參看:關于短延遲 sleep usleep nanosleep select 時間單位還有:秒(s)、毫秒(ms)、微秒 (μs)、納秒(ns)、皮秒(ps)、飛秒(fs)、阿秒、渺秒 ? ?? usleep 和 sleep 區別在于精度,ulseep 為 微妙級,sleep 為秒級。 sleep() 和 nanosleep() 都是使進程睡眠一段時間后被喚醒,但是二者的實現完全不同。Linux 中并沒有提供系統調用 sleep(),sleep() 是在庫函數中實現的,它是通過調用 alarm() 來設定報警時間,調用sigsuspend() 將進程掛起在信號 SIGALARM 上。
nanosleep() 則是 Linux中 的系統調用,它是使用定時器來實現的,該調用使調用進程睡眠,并往定時器隊列上加入一個 timer_list 型定時器,time_list 結構里包括喚醒時間以及喚醒后執行的函數,通過 nanosleep() 加入的定時器的執行函數僅僅完成喚醒當前進程的功能。系統通過一定的機制定時檢查這些隊列(比如通過系統調用陷入核心后,從核心返回用戶態前,要檢查當前進程的時間片是否已經耗盡,如果是則調用 schedule() 函數重新調度,該函數中就會檢查定時器隊列,另外慢中斷返回前也會做此檢查),如果定時時間已超過,則執行定時器指定的函數喚醒調用進程。當然,由于系統時間片可能丟失,所以 nanosleep() 精度也不是很高。
二、信號集
1、什么是信號集
顧名思義,多個信號組成的信號集合謂之信號集。 系統內核用 sigset_t 類型表示信號集。sigset_t 類型是一個結構體,但該結構體中只有一個成員,是一個包含 32 個元素的整數數組。 在?/usr/include/i386-linux-gnu/bits/sigset.h 中有如下類型定義: /* A `sigset_t' has a bit for each signal. */# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) typedef struct{unsigned long int __val[_SIGSET_NWORDS];} __sigset_t; 可以把 sigset_t 類型看成一個由 1024 個二進制位組成的大整數。其中的每一位對應一個信號,其實目前遠沒有那么多信號。某位為 1 就表示信號集中有此信號,反之為 0 就是無此信號。當需要同時操作多個信號時,常以 sigset_t 作為函數的參數或返回值的類型。2、信號集函數
#include <signal.h> int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signum); int sigdelset(sigset_t *set, int signum); 這 4 個函數返回值:若成功,返回 0,;若出錯,返回 -1 int sigismember(const sigset_t *set, int signum); 返回值:若真,返回 1;若假,返回 0(1)函數 sigemptyset
清空信號集,即將信號集的全部信號位清 0. 例如: sigset_t sigset; if (sigemptyset (&sigset) == -1)perror ("sigemptyset"), exit (1);(2)函數 sigfillset
填滿信號集,即將信號集的全部信號位置 1. 例如: sigset_t sigset; if (sigfillset (&sigset) == -1)perror ("sigfillset"), exit (1);(3)函數 sigaddset
加入信號,即將信號集中與指定信號編號對應的信號位置 1. 例如: if (sigaddset (&sigset, SIGINT))perror ("sigaddset"), exit (1);(4)函數 sigdelset
刪除信號,即將信號集中與指定信號編號對應的信號位清 0. 例如: if (sigdelest (&sigset, SIGINT))perror ("sigdelset"), exit (1);(5)函數 sigismember
判斷信號集中是否有某信號,即檢查信號集中與指定信號編號對應的信號位是否為 1. 例如: if (sigismember (&sigset, SIGINT) == 1)printf ("信號集中有 SIGINT 信號\n");(6)示例說明
//信號集的操作 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h>int main(void) {sigset_t set;printf("set = %lu\n",set);printf("sizeof(sigset_t) = %d\n",sizeof(sigset_t));//清空信號集sigemptyset(&set);printf("set = %lu\n",set);//0//增加信號到信號集中sigaddset(&set,2);// 0000 0010 => 1*2 = 2printf("set = %lu\n",set);//2sigaddset(&set,3);// 0000 0110 => 4+2 = 6printf("set = %lu\n",set);//6sigaddset(&set,7);// 0100 0110 => 64+4+2 = 70printf("set = %lu\n",set);//70//從信號集中刪除信號3sigdelset(&set,3);// 0100 0010 => 64 + 2 = 66printf("set = %lu\n",set);//66//判斷信號是否存在if(sigismember(&set,2)){printf("信號2存在\n");}if(sigismember(&set,3)){printf("信號3存在\n");}if(sigismember(&set,7)){printf("信號7存在\n");}//填滿信號集sigfillset(&set);printf("set = %lu\n",set);//很大的數return 0; } 輸出結果: set = -1078591106 sizeof(sigset_t) = 128 set = 0 set = 2 set = 6 set = 70 set = 66 信號2存在 信號7存在 set = 21474836473、信號集函數實現 (了解)
#include <signal.h> #include <errno.h>/** <signal.h> usually defines NSIG to include signal number 0.*/ #define SIGBAD(signo) ((signo) <= 0 || (signo) >= NSIG)int sigaddset(sigset_t *set, int signo) {if (SIGBAD(signo)) {errno = EINVAL;return(-1);}*set |= 1 << (signo - 1); /* turn bit on */return(0); }int sigdelset(sigset_t *set, int signo) {if (SIGBAD(signo)) {errno = EINVAL;return(-1);}*set &= ~(1 << (signo - 1)); /* turn bit off */return(0); }int sigismember(const sigset_t *set, int signo) {if (SIGBAD(signo)) {errno = EINVAL;return(-1);}return((*set & (1 << (signo - 1))) != 0); }三、信號屏蔽
1、遞送、未決與掩碼
當信號產生時,系統內核會在其所維護的進程表中,為特定的進程設置一個與該信號相對應的標志位,這個過程就叫做遞送。 信號從產生到完成遞送之間存在一定的時間間隔,處于這段時間間隔中的信號狀態稱為未決。 每個進程都有一個信號掩碼,它實際上是一個信號集,位于該信號集中的信號一旦產生,并不會被遞送給相應的進程,而是會被阻塞在未決狀態。 在信號處理函數執行期間,這個正在被處理的信號總是處于信號掩碼中,如果又有該信號產生,則會被阻塞,直到上一個針對該信號的處理過程結束以后才會被遞送。 當進程正在執行類似更新數據庫這樣的敏感任務時,可能不希望被某些信號中斷。這時可以通過信號掩碼暫時屏蔽而非忽略掉這些信號,使其一旦產生即被阻塞于未決狀態,待特定任務完成后,再回過頭來處理這些信號。2、設置掩碼與檢測未決
(1)設置調用進程的信號掩碼
#include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 返回值:若成功,返回 0;若出錯,返回 -11》》參數解析
參數 how 為修改信號掩碼的方式,可取以下值 ? ? SIG_BLOCK ? ? ? ?將 sigset 中的信號加入當前信號掩碼 ? ? SIG_UNBLOCK ?從當前信號掩碼中刪除 sigset 中的信號 ? ? SIG_SETMASK ? 把 sigset 設置成當前信號掩碼 參數 sigset 為信號集,取 NULL 則忽略此參數 參數 oldset 為輸出原信號掩碼,取 NULL 則忽略此參數2》》示例說明
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h>int main() {sigset_t sigset;sigemptyset (&sigset);sigaddset (&sigset, SIGINT);sigaddset (&sigset, SIGQUIT);sigset_t oldset;if (sigprocmask (SIG_SETMASK, &sigset, &oldset) == -1){perror ("sigprocmask");exit (EXIT_FAILURE);}if (sigpending (&sigset) == -1){perror ("sigpending");exit (EXIT_FAILURE);}if (sigismember (&sigset, SIGINT) == 1)printf ("SIGINT信號未決\n");while (1)pause ();return 0; } 輸出結果: 按 ctrl+c 和 ctrl+\ 失效(2)獲取調用進程的未決信號集
#include <signal.h> int sigpending(sigset_t *set); 返回值:成功返回 0,失敗返回 -11》》函數解析
sigpending 函數返回一信號集,對于調用進程而言,其中的各信號是阻塞不能遞送的,因而也一定是當前未決的。該信號集通過 set 參數返回。2》》示例說明
#include "apue.h"static void sig_quit(int);int main(void) {sigset_t newmask, oldmask, pendmask;if (signal(SIGQUIT, sig_quit) == SIG_ERR)err_sys("can't catch SIGQUIT");/** Block SIGQUIT and save current signal mask.*/sigemptyset(&newmask);sigaddset(&newmask, SIGQUIT);if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)err_sys("SIG_BLOCK error");sleep(5); /* SIGQUIT here will remain pending */if (sigpending(&pendmask) < 0)err_sys("sigpending error");if (sigismember(&pendmask, SIGQUIT))printf("\nSIGQUIT pending\n");/** Restore signal mask which unblocks SIGQUIT.*/if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)err_sys("SIG_SETMASK error");printf("SIGQUIT unblocked\n");sleep(5); /* SIGQUIT here will terminate with core file */exit(0); }static void sig_quit(int signo) {printf("caught SIGQUIT\n");if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)err_sys("can't reset SIGQUIT"); } 輸出結果: ^\^\^\ SIGQUIT pending caught SIGQUIT SIGQUIT unblocked ^\^\退出 (核心已轉儲)3》》示例解析
進程阻塞 SIGQUIT 信號,保存了當前信號屏蔽字(以便以后恢復),然后休眠 5 秒。在此期間所產生的退出信號 SIGQUIT 都被阻塞,不遞送至該進程,直到該信號不再被阻塞。在 5 秒休眠結束后,檢查該信號是否是未決的,然后將 SIGQUIT 設置為不再阻塞。(3)可靠和不可靠信號的屏蔽
對于可靠信號,通過 sigprocmask 函數設置信號掩碼以后,每種被屏蔽信號中的每個信號都會被阻塞,并按先后順序排隊,一旦解除屏蔽,這些信號會被依次遞送。對于不可靠信號,通過 sigprocmask 函數設置信號掩碼以后,每種被屏蔽信號中只有第一個會被阻塞,并在解除屏蔽后被遞送,其余的則全部丟失。
四、信號處理與發送
1、信號處理函數 sigaction
#include <signal.h> int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact); 返回值:成功返回 0,失敗返回 -1(1)函數功能
sigaction 函數的功能是檢查或修改(或檢查并修改)與指定信號相關聯的處理動作。此函數取代了 UNIX 早期版本使用的 signal 函數。可以理解為,是 signal 函數的增強版。(2)參數解析
signum:信號編號act:信號行為oldact:輸出原信號行為,可置 NULL當 signum 信號被遞送時,按 act 所描述的行為響應。若 oldact 非 NULL,則通過該參數輸出原來的響應行為。sigaction 函數通過信號行為結構類型 sigaction 來描述對一個信號的響應行為。struct sigaction { 1 void (*sa_handler)(int); =>函數指針,用于設置信號的處理方式,與signal函數中第二個參數相同,SIG_IGW,SIG_DFL函數名 2 void (*sa_sigaction)(int, siginfo_t */*結構體指針*/, void *);=>函數指針,作為第二種處理信號的方式 是否使用該處理方式,依賴與sa_flags的值 3 sigset_t sa_mask;=>用于設置在信號處理函數的執行期間,需要屏蔽的信號=>自動屏蔽與正在處理的信號相同的信號 4 int sa_flags;=>處理的標志 =>SA_SIGINFO 表示采用第二個函數指針處理信號=>SA_NODEFER 表示解除對相同信號的屏蔽=>SA_RESETHAND 表示自定義處理信號后恢復默認處理方式 5 void (*sa_restorer)(void); => 保留成員,暫時不是用,目前置NULL };其中第二個函數指針的第二個參數類型如下:
struct siginfo_t { pid_t si_pid; /* Sending process ID */ //發送信號進程的ID sigval_t si_value; /* Signal value */ //發送信號時的附加數據 };
(3)示例說明
示例一:增減信號掩碼
//使用sigaction函數處理信號 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/types.h>void fa(int signo) {printf("捕獲到了信號%d\n",signo);sleep(5);printf("信號處理完畢\n"); }int main(void) {//定義結構體變量并且進行初始化struct sigaction action = {};//指定函數指針的初始值action.sa_handler = fa;//清空信號集,然后放入信號3sigemptyset(&action.sa_mask);sigaddset(&action.sa_mask,3);//設置處理信號的標志//解除對相同信號的屏蔽,信號2//action.sa_flags = SA_NODEFER;//自定義處理后恢復默認處理方式action.sa_flags = SA_RESETHAND;//使用sigaction對信號2自定義處理sigaction(2,&action,NULL);while(1);return 0; } 輸出結果: ^C捕獲到了信號2 ^\^\^\^\^\^\^\信號處理完畢 退出 (核心已轉儲)示例二:一次性信號處理
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h>void oldsigint(int signum) {printf ("\n%d進程:收到%d信號\n", getpid (), signum); }int main() {struct sigaction sigact = {};sigact.sa_handler = oldsigint;sigaddset (&sigact.sa_mask, SIGQUIT);sigact.sa_flags = SA_NODEFER | SA_RESETHAND;if (sigaction (SIGINT, &sigact, NULL) == -1){perror ("sigaction");exit (EXIT_FAILURE);}pause();return 0; } 輸出結果: ^C 2512進程:收到2信號 ^C示例三:提供更多信息的信號處理函數
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h>void newsigint (int signum, siginfo_t* siginf, void* reserved) {printf ("%d進程給我發了一個SIGINT信號\n", siginf->si_pid); }int main() {struct sigaction sigact = {};sigact.sa_sigaction = newsigint;sigaddset (&sigact.sa_mask, SIGQUIT);sigact.sa_flags = SA_NODEFER | SA_SIGINFO;if (sigaction (SIGINT, &sigact, NULL) == -1){perror ("sigaction");exit (EXIT_FAILURE);}pause();return 0; } 輸出結果: ^C0進程給我發了一個SIGINT信號(4)用 sigaction 實現 signal 函數
#include <stdio.h> #include <signal.h> #include "apue.h" /* Reliable version of signal(), using POSIX sigaction(). */ Sigfunc * signal_my(int signo, Sigfunc *func) {struct sigaction act, oact;act.sa_handler = func;sigemptyset(&act.sa_mask);act.sa_flags = 0;if (signo == SIGALRM) { #ifdef SA_INTERRUPTact.sa_flags |= SA_INTERRUPT; #endif} else {act.sa_flags |= SA_RESTART;}if (sigaction(signo, &act, &oact) < 0)return(SIG_ERR);return(oact.sa_handler); }void fa (int signo) {printf ("捕獲到了信號%d\n", signo); }int main (void) {signal_my (2, fa); sleep (5);return 0; } 輸出結果: ^C捕獲到了信號2 另一個版本,可阻止被中斷的系統調用重啟動。#include "apue.h"Sigfunc * signal_intr(int signo, Sigfunc *func) {struct sigaction act, oact;act.sa_handler = func;sigemptyset(&act.sa_mask);act.sa_flags = 0; #ifdef SA_INTERRUPTact.sa_flags |= SA_INTERRUPT; #endifif (sigaction(signo, &act, &oact) < 0)return(SIG_ERR);return(oact.sa_handler); }2、信號發送函數 sigqueue
#include <signal.h> int sigqueue(pid_t pid, int sig, const union sigval value); 返回值:成功返回 0;失敗返回 -1(1)函數功能
表示向指定進程發送指定的信號和附加數據(2)參數解析
pid:接收信號進程的 PID sig:信號編號 value:附加數據 注意,該參數的類型 sigval 是一個聯合: union sigval {int sival_int;//整數void *sival_ptr;//地址 };(3)示例說明
//sigqueue函數和sigaction函數的使用 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <signal.h>void fa(int signo,siginfo_t* info,void* p) {printf("進程%d發送來了信號%d,附加數據是:%d\n",info->si_pid,signo,info->si_value); }int main(void) {//定義結構體變量進行初始化struct sigaction action = {};//給第二個函數指針進行初始化action.sa_sigaction = fa;//給處理標志進行賦值//表采用結構中第二個函數指針處理action.sa_flags = SA_SIGINFO;//使用sigaction對信號40自定義處理sigaction(40,&action,NULL);//創建子進程給父進程發信號和數據pid_t pid = fork();if(-1 == pid){perror("fork"),exit(-1);}if(0 == pid) //子進程{int i = 0;for(i = 0; i < 100; i++){//定義聯合進行初始化union sigval v;v.sival_int = i;//發送信號和附加數據sigqueue(getppid(),40,v);}sleep(1);exit(100);//終止子進程}//父進程等待處理信號和附加數據while(1);return 0; }輸出結果: 進程2721發送來了信號40,附加數據是:0 進程2721發送來了信號40,附加數據是:1 進程2721發送來了信號40,附加數據是:2 。。。。 進程2721發送來了信號40,附加數據是:97 進程2721發送來了信號40,附加數據是:98 進程2721發送來了信號40,附加數據是:99五、函數 sigsetjmp 和 siglongjmp
暫時不講六、 函數 sigsuspend
暫時不講七、未講部分
中斷的系統調用 ?未講 可靠信號術語和語義 ?未講 函數 system ?未講 函數 clock_nanosleep ?未講 作業控制信號 ?未講 信號名和編號 ?未講 信號這一章,內容有點雜,好多東西沒有用過,所以不太熟悉。總結
以上是生活随笔為你收集整理的UNIX再学习 -- 信号处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JMS学习八(ActiveMQ消息持久化
- 下一篇: 机器学习笔记(十八)——HMM的参数估计