信号分类(不可靠信号/可靠信号)
linux信号机制基本上是从unix系统中继承过来的。早期unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,它的主要问题是:
进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。
早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。
linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。因此,linux下的不可靠信号问题主要指的是信号可能丢失。
随着时间的发展,实践证明,有必要对信号的原始机制加以改进和扩充。所以,后来出现的各种unix版本分别在这方面进行了研究,力图实现"可靠信号"。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。同时,信号的发送和安装也出现了新版本:信号发送函数sigqueue()及信号安装函数sigaction()。
实时信号(都是可靠信号)和非实时信号(不可靠信号)
早期Unix系统只定义了32种信号,Rethat7.2支持64种信号,编号0-63(SIGRTMIN=31,SIGRTMAX=63),将来可能进一步增加,这需要得到内核的支持。前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL+C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。实时信号是POSIX标准的一部分,可用于应用进程。非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。
信号发送
kill函数
#include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig);
pid>0将信号sig发给pid进程
pid=0将信号sig发给同组进程
pid=-1将信号sig发送给所有进程,调用者进程有权限发送的每一个进程(除了1号进程之外,还有它自身)
pid<-1将信号sig发送给进程组是pid(绝对值)的每一个进程
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<signal.h> void myhandle(int num) { if (num == SIGINT) { printf("recv signal SIGINT \n"); } else if (num == SIGUSR1) { printf("recv signal SIGUSR1 \n"); } } int main(int argv,char* argc[]) { pid_t pid; if (signal(SIGINT, myhandle) == SIG_ERR) { perror("func signal err\n"); return 0; } if (signal(SIGUSR1, myhandle) == SIG_ERR) { perror("func signal err\n"); return 0; } pid = fork(); if (pid == 0) { pid=getppid(); kill(pid, SIGUSR1); exit(0); } int n = 3; do { printf("父进程开始睡眠\n"); n = sleep(n); printf("父进程开始唤醒\n"); } while (n > 0); return 0; }
raise函数
#include <signal.h> int raise(int sig);给自己发送信号。raise(sig)等价于kill(getpid(), sig);
进程休眠
pause函数
#include <unistd.h> int pause(void);
将进程置为可中断睡眠状态。然后它调用内核函数schedule(),使linux进程调度器找到另一个进程来运行。pause使调用者进程挂起,直到一个信号被捕获。
alarm函数
#include <unistd.h> unsigned int alarm(unsigned int seconds);
alarm函数,设置一个闹钟延迟发送信号,告诉linux内核n秒中以后,发送SIGALRM信号。
#include<stdio.h> #include<stdlib.h> #include<signal.h> #include<unistd.h> void func(int num) { printf("the num is %d\n",num); if(num==SIGALRM) { printf("SIGALRM signal is called\n"); } } int main(int argv,char * argc[]) { int i; if(signal(SIGALRM,func)==SIG_ERR) { printf("error!\n"); } alarm(1); for(;;) { pause(); } return 0; }
sleep函数
#include <unistd.h> unsigned int sleep(unsigned int seconds);
让进程睡眠。能被信号打断,然后处理信号函数以后,就不再睡眠了,直接向下执行代码。sleep函数的返回值,是剩余的秒数。
可重入函数与不可重入函数
为了增强程序的稳定性,在信号处理函数中应使用可重入函数。所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。因为进程在收到信号后,就将跳转到信号处理函数去接着执行。如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,可能会出现不可预料的后果。不可重入函数在信号处理函数中被视为不安全函数。