1.信号分类
1.非实时信号(不可靠信号)
Linux信号机制基本上是从UNIX系统中继承过来的。早期UNIX系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,它的主要问题是:
(1)进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。
(2)因此导致, 早期UNIX下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。
(3)Linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。因此,Linux下的不可靠信号问题主要指的是信号可能丢失。
2.实时信号(可靠信号)
(1)随着时间的发展,实践证明,有必要对信号的原始机制加以改进和扩充。所以,后来出现的各种UNIX版本分别在这方面进行了研究,力图实现"可靠信号"。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号(SIGRTMIN ~ SIGRTMAX),并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。同时,信号的发送和安装也出现了新版本:信号发送函数sigqueue()及信号安装函数sigaction()。
(2)sigaction和signal函数都是调用内核服务do_signal函数;[内核服务函数,应用程序无法调用该函数]
(3)早期UNIX系统只定义了31种信号,而Linux 3.x支持64种信号,编号1-64(SIGRTMIN=34,SIGRTMAX=64),将来可能进一步增加,这需要得到内核的支持。前31种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL+C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于可靠信号。这保证了发送的多个实时信号都被接收。实时信号是POSIX标准的一部分,可用于应用进程。
(4)非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。
2.信号发送
1.kill
int kill(pid_t pid, int signo);
(1)kill既可以向自身发送信号,也可以向其他进程发送信号
(2) pid>0 将信号sig发给pid进程
pid=0 将信号sig发给同组进程
pid=-1 将信号sig发送给所有进程,调用者进程有权限发送的每一个进程(除了1号进程之外,还有它自身)
pid<-1 将信号sig发送给进程组是pid(绝对值)的每一个进程
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
void handler(int sig);
int main () {
signal(SIGINT, handler);
if(signal(SIGUSR1, handler) == SIG_ERR)
ERR_EXIT("signal error");
pid_t pid = fork();
if(-1 == pid)
ERR_EXIT("fork error");
if(0 == pid) {
pid = getpgrp();
//kill(-pid, SIGUSR1); //
killpg(getpgrp(), SIGUSR1);
//kill(getppid(), SIGUSR1);
exit(EXIT_SUCCESS);
}
int n = 15;
do {
n = sleep(n);
}while(n>0);
return 0;
}
void handler(int sig) {
printf("recv a sig=%d\n", sig);
}
执行后,父子进程均能收到SIGUSR1,信号,因为signal(SIGUSR1, handler)在fork之前。killpg向整个进程组发送sig信号。
注意:如果在fork之前安装信号,则子进程可以继承信号。
sleep遇上signal,子进程向父进程发送信号,sleep函数的几点说明
1)sleep函数作用,让进程睡眠。
2)能被信号打断,然后处理信号函数以后,就不再睡眠了。直接向下执行代码
3)sleep函数的返回值,是剩余的秒数
2.raise
int raise(int sig);
(1)给自己发送信号。raise(sig)等价于kill(getpid(), sig);
3.killpg
int killpg(int pgrp, int sig);
给进程组发送信号。killpg(pgrp, sig)等价于kill(-pgrp, sig);
killpg(getpgrp(), sig) = kill(-getpgrp(), sig);
4.sigqueue
int sigqueue(pid_t pid, int sig, const union sigval value);
(1)给进程发送信号,支持排队,可以附带信息。
5.pause
int pause(void);
(1)将进程置为可中断睡眠状态。然后它调用内核函数schedule(),使Linux进程调度器找到另一个进程来运行。
(2)pause使调用者进程挂起,直到一个信号被捕获
(3)信号处理结束后,才会执行pause
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
void handler(int sig);
int main () {
signal(SIGINT, handler);
for(;;) {
pause();
printf("pause return\n");
}
return 0;
}
void handler(int sig) {
printf("recv a sig=%d\n", sig);
sleep(1);
}