能够用来处理进程间的异步事件——即进程间能够经过系统调用来发送信号,只是告知某进程发生了什么事,使得被告知的进程去作对应的事件(信号处理),要注意的是,发送信号的过程并不会传送任何数据。经过kill -l
能够看到信号的名字和序号。html
能够经过这个案例来讲明:linux
在终端运行top
来查看系统运行的一些相关信息,能够看到终端的数据一直是变化的,同事经过ps -aux|grep top
来查看如今系统是否正在运行该指令,能够获得运行该指令的进程号,而后用kill -9 进程号
将该进程杀掉,咱们此时经过ps -aux|grep top
来发现此时top
的运行相关信息已经没有了。这个过程就是一个进程给另外一个进程发送了SIGKILL
的信号。(注意:kill
,就是送出一个特定的信号给某个进程,而该进程根据信号作出相应的动做(sigqueue
也是),-9
能够经过kill -l
看出是SIGKILL
)ios
gqx@gqx-Lenovo-Product:~$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 ......
通常信号的处理能够分为三种:c++
忽略信号shell
大多数信号可使用这个方式来处理,可是有两种信号不能被忽略(分别是 SIGKILL
和SIGSTOP
)。由于他们向内核和超级用户提供了进程终止和中止的可靠方法,若是忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不但愿看到的场景异步
捕捉信号函数
须要告诉内核,用户但愿如何处理某一种信号,说白了就是写一个信号处理函数,而后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。spa
系统的默认动做操作系统
对于每一个信号来讲,系统都对应由默认的处理动做,当发生了该信号,系统会自动执行。不过,对系统来讲,大部分的处理方式都比较粗暴,就是直接杀死该进程。设计
1. signal函数
/* Type of a signal handler. */ typedef void (*__sighandler_t) (int); /* Set the handler for the signal SIG to HANDLER, returning the old handler, or SIG_ERR on error. By default `signal' has the BSD semantic. */ extern __sighandler_t signal (int __sig, __sighandler_t __handler);
第一个参数表示信号量类型(对应的kill -l
里的数据),第二个参数则表示该进程被告知该信号后的处理函数。参考案例以下:
#include <iostream> #include <list> #include <unistd.h> #include <sys/wait.h> using namespace std; void timeout(int sig){ if(sig == SIGALRM){ puts("Time out!"); } alarm(2); } void keycontrol(int sig){ if(sig == SIGINT){ puts("CTRL + C pressed"); } } int main(){ int i; signal(SIGALRM, timeout); //到达经过了alarm函数设置的时间,调用函数timeout signal(SIGINT, keycontrol); //键盘键入Ctrl+后,调用keycontrol函数 alarm(2); for(i = 0; i < 6; i++){ puts("wait..."); sleep(100); } return 0; }
这段代码要注意的是,在signal
中注册信号函数后,调用信号函数的则是操做系统,但进程处于睡眠状态的时间为100s,而alarm函数等待的时间是2秒,即2秒后会产生SIGALRM
信号,此时将唤醒处于休眠状态的进程,并且进程一旦被唤醒,则不会再进入休眠状态,因此上述程序运行时间比较短。
2. sigaction
该函数已经彻底取代了上述signal
函数。
struct sigaction { void (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动做 void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,可以接受额外数据和sigqueue配合使用 sigset_t sa_mask;//阻塞关键字的信号集,能够再调用捕捉函数以前,把信号添加到信号阻塞字,信号捕捉函数返回以前恢复为原先的值。 int sa_flags;//影响信号的行为SA_SIGINFO表示可以接受数据 }; /* Get and/or set the action for signal SIG. */ extern int sigaction (int __sig, const struct sigaction *__restrict __act, struct sigaction *__restrict __oact) __THROW;
第一个参数表示信号量类型;第二个参数信号处理函数;第三个参数:经过此参数获取以前注册的信号处理函数指针,若不须要,则传递0;
程序示例以下,改程序用来消除由父进程产生的两个子进程会致使僵尸进程的产生的状况,当子进程的生命周期结束后,回收子进程的内存信息,而不用等到父进程结束才去回收销毁子进程:
#include <iostream> #include <list> #include <unistd.h> #include <sys/wait.h> using namespace std; void read_childproc(int sig){ int status; pid_t id = waitpid(-1, &status, WNOHANG); //消灭子进程结束后产生的僵尸进程 if(WIFEXITED(status)){ printf("Remove proc id: %d \n", id); printf("Child send: %d \n", WEXITSTATUS(status)); } } int main(){ pid_t pid; struct sigaction act; act.sa_handler = read_childproc; sigemptyset(&act.sa_mask); //将sa_mask全部位初始化为0(初始化sa_mask中传入的信号集,清空其中全部信号) act.sa_flags = 0; sigaction(SIGCHLD, &act, 0); //SIGCHLD 子进程结束信号 pid = fork(); if(pid == 0){ puts("Hi, I am a child process!"); sleep(6); return 12; }else{ printf("Child proc id: %d \n", pid); pid = fork(); if(pid == 0){ puts("Hi, I am a child process!"); sleep(13); exit(24); }else{ int i; printf("Child proc id: %d \n", pid); for(i = 0; i < 4; i++){ puts("wait..."); sleep(5); } } } return 0; }