编号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); } } }