編號html
事件linux
名稱c++
默認處理動做程序員
忽略算法
終止shell
終止併產生core文件微信
暫停網絡
繼續併發
下面是英語:異步
Term Default action is to terminate the process. Ign Default action is to ignore the signal. Core Default action is to terminate the process and dump core (see core(5)). Stop Default action is to stop the process. Cont Default action is to continue the process if it is currently stopped.
下面是從man 7 signal裏考出來的linux常規信號一覽表,value裏有3列的,看中間的數字,中間的數字表明linux
Signal Value Action Comment ────────────────────────────────────────────────────────────────────── SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process SIGINT 2 Term Interrupt from keyboard SIGQUIT 3 Core Quit from keyboard SIGILL 4 Core Illegal Instruction SIGABRT 6 Core Abort signal from abort(3) SIGFPE 8 Core Floating-point exception SIGKILL 9 Term Kill signal SIGSEGV 11 Core Invalid memory reference SIGPIPE 13 Term Broken pipe: write to pipe with no readers; see pipe(7) SIGALRM 14 Term Timer signal from alarm(2) SIGTERM 15 Term Termination signal SIGUSR1 30,10,16 Term User-defined signal 1 SIGUSR2 31,12,17 Term User-defined signal 2 SIGCHLD 20,17,18 Ign Child stopped or terminated SIGCONT 19,18,25 Cont Continue if stopped SIGSTOP 17,19,23 Stop Stop process SIGTSTP 18,20,24 Stop Stop typed at terminal SIGTTIN 21,21,26 Stop Terminal input for background process SIGTTOU 22,22,27 Stop Terminal output for background process
中文解釋:
1) SIGHUP: 當用戶退出shell時,由該shell啓動的全部進程將收到這個信號,默認動做爲終止進程 2) SIGINT:當用戶按下了<Ctrl+C>組合鍵時,用戶終端向正在運行中的由該終端啓動的程序發出此信號。默認動 做爲終止進程。 3) SIGQUIT:當用戶按下<ctrl+\>組合鍵時產生該信號,用戶終端向正在運行中的由該終端啓動的程序發出些信 號。默認動做爲終止進程。 4) SIGILL:CPU檢測到某進程執行了非法指令。默認動做爲終止進程併產生core文件 5) SIGTRAP:該信號由斷點指令或其餘 trap指令產生。默認動做爲終止里程 併產生core文件。 6) SIGABRT: 調用abort函數時產生該信號。默認動做爲終止進程併產生core文件。 7) SIGBUS:非法訪問內存地址,包括內存對齊出錯,默認動做爲終止進程併產生core文件。 8) SIGFPE:在發生致命的運算錯誤時發出。不只包括浮點運算錯誤,還包括溢出及除數爲0等全部的算法錯誤。默認動做爲終止進程併產生core文件。 9) SIGKILL:無條件終止進程。本信號不能被忽略,處理和阻塞。默認動做爲終止進程。它向系統管理員提供了能夠殺死任何進程的方法。 10) SIGUSE1:用戶定義 的信號。即程序員能夠在程序中定義並使用該信號。默認動做爲終止進程。 11) SIGSEGV:指示進程進行了無效內存訪問。默認動做爲終止進程併產生core文件。 12) SIGUSR2:另一個用戶自定義信號,程序員能夠在程序中定義並使用該信號。默認動做爲終止進程。 13) SIGPIPE:Broken pipe向一個沒有讀端的管道寫數據。默認動做爲終止進程。 14) SIGALRM: 定時器超時,超時的時間 由系統調用alarm設置。默認動做爲終止進程。 15) SIGTERM:程序結束信號,與SIGKILL不一樣的是,該信號能夠被阻塞和終止。一般用來要示程序正常退出。執行shell命令Kill時,缺省產生這個信號。默認動做爲終止進程。 16) SIGSTKFLT:Linux早期版本出現的信號,現仍保留向後兼容。默認動做爲終止進程。 17) SIGCHLD:子進程結束時,父進程會收到這個信號。默認動做爲忽略這個信號。 18) SIGCONT:若是進程已中止,則使其繼續運行。默認動做爲繼續/忽略。 19) SIGSTOP:中止進程的執行。信號不能被忽略,處理和阻塞。默認動做爲暫停進程。 20) SIGTSTP:中止終端交互進程的運行。按下<ctrl+z>組合鍵時發出這個信號。默認動做爲暫停進程。 21) SIGTTIN:後臺進程讀終端控制檯。默認動做爲暫停進程。 22) SIGTTOU: 該信號相似於SIGTTIN,在後臺進程要向終端輸出數據時發生。默認動做爲暫停進程。 23) SIGURG:套接字上有緊急數據時,向當前正在運行的進程發出些信號,報告有緊急數據到達。如網絡帶外數據到達,默認動做爲忽略該信號。 24) SIGXCPU:進程執行時間超過了分配給該進程的CPU時間 ,系統產生該信號併發送給該進程。默認動做爲終止進程。 25) SIGXFSZ:超過文件的最大長度設置。默認動做爲終止進程。 26) SIGVTALRM:虛擬時鐘超時時產生該信號。相似於SIGALRM,可是該信號只計算該進程佔用CPU的使用時間。默認動做爲終止進程。 27) SGIPROF:相似於SIGVTALRM,它不公包括該進程佔用CPU時間還包括執行系統調用時間。默認動做爲終止進程。 28) SIGWINCH:窗口變化大小時發出。默認動做爲忽略該信號。 29) SIGIO:此信號向進程指示發出了一個異步IO事件。默認動做爲忽略。 30) SIGPWR:關機。默認動做爲終止進程。 31) SIGSYS:無效的系統調用。默認動做爲終止進程併產生core文件。 34) SIGRTMIN ~ (64) SIGRTMAX:LINUX的實時信號,它們沒有固定的含義(能夠由用戶自定義)。全部的實時信號的默認動做都爲終止進程。
可使用kill –l命令查看當前系統可以使用的信號有哪些。
kill函數
#include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig);
例子:在子進程中調用kill函數,殺死父進程
#include <sys/types.h> #include <signal.h> #include <unistd.h> #include <stdio.h> int main(){ int proIdx = 0; for(proIdx = 0; proIdx < 5; ++proIdx){ pid_t pid = fork(); if(pid == 0){ break; } } //在第三個子進程中殺死父進程 if(proIdx == 2){ printf("kill parent process after 5s\n"); sleep(5); int ret = kill(getppid(), SIGKILL); while(1){ sleep(1); } } //parent process if(proIdx == 5){ while(1){ printf("father\n"); sleep(1); } } }
例子:在父進程中調用kill函數,殺死子進程
#include <sys/types.h> #include <signal.h> #include <unistd.h> #include <stdio.h> int main(){ pid_t pid3, pid; int proIdx = 0; for(proIdx = 0; proIdx < 5; ++proIdx){ pid = fork(); if(pid == 0){ break; } //parent process if(proIdx == 2){ pid3 = pid; } } if(proIdx < 5){ while(1){ printf("pid=%d\n", getpid()); sleep(3); } } //在父進程中殺死第三個子進程 if(proIdx == 5){ sleep(5); int ret = kill(pid3, SIGKILL); while(1){ sleep(1); } } }
raise函數:給調用raise函數的進程發送信號,實際調用的是kill(getpid(), sig)函數
#include <signal.h> int raise(int sig);
例子:
#include <signal.h> #include <sys/types.h> #include <stdio.h> #include <unistd.h> int main(){ printf("will kill myself\n"); sleep(2); raise(SIGKILL); return 0; }
abort函數:給調用abort函數的進程發送信號,並生成core文件(生成core文件的前提是:ulimit -c unlimited)
#include <stdlib.h> void abort(void);
#include <signal.h> #include <sys/types.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(){ printf("will kill myself\n"); sleep(2); abort(); return 0; }
alarm函數:在指定參數的秒數後,給調用alarm函數的進程發送SIGALRM信號。SIGALRM信號的默認處理的Term,因此就會終止當前的進程。
Signal Value Action SIGALRM 14 Term
#include <unistd.h> unsigned int alarm(unsigned int seconds);
例子:
#include <stdio.h> #include <unistd.h> int main() { int ret = 0; ret = alarm(2); printf("ret:%d\n", ret); sleep(1); ret = alarm(5); printf("ret:%d\n", ret); while(1){ printf("aaaaaaaaaa\n"); sleep(1); } }
setitimer函數:功能強大的alarm函數,能夠設置週期性的鬧鐘。
#include <sys/time.h> int getitimer(int which, struct itimerval *curr_value); int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
which:
Signal Value Action Comment SIGALRM 14 Term Timer signal from alarm(2) SIGVTALRM 26,26,28 Term Virtual alarm clock (4.2BSD) SIGPROF 27,27,29 Term Profiling timer expired
能夠看出來,這3個信號的默認處理都是Term,因此就會終止當前的進程。
new_value和old_value的結構體
struct itimerval { struct timeval it_interval; /* Interval for periodic timer */ 週期定時用 struct timeval it_value; /* Time until next expiration */ 單次定時用 }; struct timeval { time_t tv_sec; /* seconds */ 秒 suseconds_t tv_usec; /* microseconds */ 微妙 };
返回值:成功0,失敗-1。
例子:週期性的鬧鐘,第一次鬧鐘在5秒後,而後每3秒鬧鐘響一次。鬧鐘響後調用函數cap,並把信號SIGALRM傳給函數。
#include <stdio.h> #include <unistd.h> #include <sys/time.h> #include <signal.h> void cap(int num){ printf("cap:%d\n", num); } int main(){ signal(SIGALRM, cap);//當本進程收到SIGALRM信號時,調用函數cap,並把信號SIGALRM對應的數字傳給cap struct itimerval it = {{3,0}, {5,0}};//第一次鬧鐘在5秒後,而後每3秒鬧鐘響一次。 setitimer(ITIMER_REAL, &it, NULL); while(1){ printf("aaaaa\n"); sleep(1); } }
#include <signal.h> //把對應的標識位所有設置成0 int sigemptyset(sigset_t *set); //把對應的標識位所有設置成1 int sigfillset(sigset_t *set); //添加某個信號到信號集 int sigaddset(sigset_t *set, int signum); //從信號集刪除某個信號…… int sigdelset(sigset_t *set, int signum); //判斷某個信號是否在信號集裏 int sigismember(const sigset_t *set, int signum);
#include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
#include <signal.h> int sigpending(sigset_t *set);
#include <stdio.h> #include <unistd.h> #include <signal.h> int main(){ sigset_t setu; //清空信號集合setu sigemptyset(&setu); //把信號SIGINT加入信號集合setu sigaddset(&setu, SIGINT); //把信號SIGQUIT加入信號集合setu sigaddset(&setu, SIGQUIT); //把集合裏面的信號設置爲阻塞。 sigprocmask(SIG_BLOCK, &setu, NULL); while(1){ sigset_t set1; sigpending(&set1); for(int i = 1; i < 31; ++i){ //判斷信號i是否在信號集合set1裏面。 if(sigismember(&set1, i) == 1){ printf("1"); } else { printf("0"); } } printf("\n"); sleep(1); } }
#include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);
#include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
signum:要捕捉的信號
act:捕捉到信號後,執行的動做定義在結構體裏。
struct sigaction { void (*sa_handler)(int);//當sa_flags爲0時,調用的函數指針 void (*sa_sigaction)(int, siginfo_t *, void *);//當sa_flags爲SA_SIGINFO時,調用的函數指針,這個函數能夠傳進去不少參數,可是信號自己的目的就是簡單並攜帶少許信息,因此通常不使用這個參數 sigset_t sa_mask;//在執行函數期間,指定的臨時的阻塞信號集合。 int sa_flags;//通常爲0,根據這個flag來決定調用哪一個函數。SA_RESTART的做用是,好比某些阻塞函數(read)在阻塞的時候,忽然來了個信號,執行完信號捕捉函數後,會繼續執行阻塞的函數(read),不設置sa_flags爲SA_RESTART的話,信號捕捉函數信息後,不繼續執行阻塞的函數(read)。 void (*sa_restorer)(void); };
oldact:原來對應這個信號的動做。目的是,方便還原回去。
#include <signal.h> #include <stdio.h> #include <sys/time.h> #include <unistd.h> void catch(int num){ printf("catch %d signal\n", num); } int main() { struct sigaction act; act.sa_flags = 0; act.sa_handler = catch; sigemptyset(&act.sa_mask); sigaction(SIGALRM, &act, NULL); struct itimerval it = {{3, 0}, {5, 0}}; setitimer(ITIMER_REAL, &it, NULL); while(1){ printf("kill me plz\n"); sleep(1); } }
#include <signal.h> #include <stdio.h> #include <sys/time.h> #include <unistd.h> void catch(int num){ printf("begin catch %d signal\n", num); sleep(5); printf("end catch %d signal\n", num); } int main(){ struct sigaction act; act.sa_flags = 0; act.sa_handler = catch; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGQUIT); sigaction(SIGINT, &act, NULL); while(1){ printf("kill me\n"); sleep(1); } }
原理:當子進程被暫停或者終止的時候,會給父進程發送信號SIGCHLD,信號SIGCHLD的默認處理爲Ign,也就是什麼都不作。因此咱們能夠經過捕捉信號SIGCHLD,在信號捕捉函數裏調用wait或者waitpid函數來回收子進程。
Signal Value Action Comment ─────────────────────────────────────────────────────────── SIGCHLD 20,17,18 Ign Child stopped or terminated
1,錯誤的粗略版,不可以回收所有的子進程。理由是,信號SIGCHLD是不支持排隊的,因此當waitpid函數執行的同時,又有一個子進程結束了,也就是又來了一個SIGCHLD信號,因此這個信號就被忽略了。
重點看最後一行的sleep函數。若是把最後一行的sleep函數的註釋打開,就可以所有回收子進程了。理由是睡了1秒,在waitpid函數執行的同時,沒有別的SIGCHLD信號進來,因此就都回收了。
#include <signal.h> #include <stdio.h> #include <sys/time.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> void catch(int num){ pid_t pid = waitpid(-1, NULL, WNOHANG); printf("%d is ok\n", pid); } int main(){ int i = 0; for(; i < 10; ++i){ pid_t p = fork(); if(p == 0)break; } if(i == 10){ //parent process struct sigaction act, old; act.sa_flags = 0; act.sa_handler = catch; sigemptyset(&act.sa_mask); sigaction(SIGCHLD, &act, NULL); while(1){ sleep(1); } } else { //child process printf("child %d %d\n", i, getpid()); //sleep(1); } }
2,改進版,即便註釋掉最後一行的sleep函數,也可以回收所有的子進程。理由是虛幻調用waitpid函數了。在一次信號捕捉函數裏,儘可能多的回收子進程,可是也不必定是絕對的穩定,極端時候也會有回收不全的狀況。每次執行的時候就會發現,打印的【begin】和【end】不必定是幾回。也就是說在一次捕捉函數裏,調用了屢次waitpid函數。並且還有一個致命的弱點,若是父進程的信號捕捉函數sigaction尚未調用,子進程就所有結束了的話,因此的子進程都不可以被回收了。
#include <signal.h> #include <stdio.h> #include <sys/time.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> void catch(int num){ printf("begin\n"); pid_t pid; while((pid = waitpid(-1, NULL, WNOHANG)) > 0){ printf("%d is ok\n", pid); } printf("end\n"); } int main(){ int i = 0; for(; i < 10; ++i){ pid_t p = fork(); if(p == 0)break; } if(i == 10){ //parent process struct sigaction act, old; act.sa_flags = 0; act.sa_handler = catch; sigemptyset(&act.sa_mask); sigaction(SIGCHLD, &act, NULL); while(1){ sleep(1); } } else { //child process printf("child %d %d\n", i, getpid()); } }
3,最終版。解決改進版的問題。
原理:在父進程裏阻塞信號SIGCHLD,這樣一來,即便子進程在父進程調用sigaction以前就結束了,子進程發送過來的信號SIGCHLD是被阻塞的狀態,也就是信號處於未決狀態。在調用sigaction後的下一行代碼,把信號SIGCHLD從阻塞狀態恢復到原來的非阻塞狀態後,這個時點全部被阻塞的子進程的信號SIGCHLD,就一次(【begin】和【end】只被打印一次)都被捕捉函數處理掉了。
#include <signal.h> #include <stdio.h> #include <sys/time.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> void catch(int num){ printf("begin\n"); pid_t pid; while((pid = waitpid(-1, NULL, WNOHANG)) > 0){ printf("%d is ok\n", pid); } printf("end\n"); } int main(){ sigset_t st, oldset; sigemptyset(&st); sigaddset(&st, SIGCHLD); //把信號SIGCHLD設置爲阻塞,並取得當前的信號集合oldset sigprocmask(SIG_BLOCK, &st, &oldset); int i = 0; for(; i < 10; ++i){ pid_t p = fork(); if(p == 0)break; } if(i == 10){ //parent process sleep(2); struct sigaction act, old; act.sa_flags = 0; act.sa_handler = catch; sigemptyset(&act.sa_mask); sigaction(SIGCHLD, &act, NULL); //用信號集合oldset,恢復到以前的狀態。 sigprocmask(SIG_SETMASK, &oldset, NULL); while(1){ sleep(1); } } else { //child process printf("child %d %d\n", i, getpid()); //sleep(1); } }
#include <signal.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> void fcat(int num){ sleep(1); printf("aa"); } void fcat2(int num){ sleep(1); printf("aa1"); } int main(){ pid_t pid = fork(); if(pid == 0){ sigset_t st, oldset; sigemptyset(&st); sigaddset(&st, SIGUSR2); if((sigprocmask(SIG_BLOCK, &st, &oldset)) == -1){ perror("sigprocmask"); } struct sigaction act; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = fcat2; sigaction(SIGUSR2, &act, NULL); sigprocmask(SIG_SETMASK, &oldset, NULL); int cnt = 1; while(1){ printf("child:%d\n", getpid()); kill(getppid(), SIGUSR1); sleep(1); } } else{ sigset_t st, oldset; sigemptyset(&st); sigaddset(&st, SIGUSR1); sigprocmask(SIG_BLOCK, &st, &oldset); struct sigaction act; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = fcat; sigaction(SIGUSR1, &act, NULL); sigprocmask(SIG_SETMASK, &oldset, NULL); int cnt = 0; while(1){ printf("parent:%d\n", getpid()); kill(pid, SIGUSR2); sleep(1); } } }