信号
对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。
信号的概述
每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIINT”、“SIGUP”等等。
信号定义在signal.h头文件中,信号名都定义为正整数。
具体的信号名称可以 使用kill -l来查看信号 的名字以及序号,
信号是 从1开始编号的,不存在0号信号。kill对于信号0有特殊的应用。
信号的处理:
忽略信号:大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程。
捕捉信号:需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
系统默认动作:对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。
其实对于常用的 kill 命令就是一个发送信号的工具,kill -9 PID来杀死进程。比如,我在后台运行了一个 top 工具,通过 ps命令可以查看他的 PID,通过 kill 9 来发送了一个终止进程的信号来结束了 top 进程。如果查看信号编号和名称,可以发现9对应的是“9-SIGKILL”,正是杀死该进程的信号。而以下的执行过程实际也就是执行了9号信号的默认动作——杀死进程。
kill -9 进程PID
kill -SIGKILL 进程PID
对于信号来说,最大的意义不是为了杀死信号,而是实现一些异步通讯的手段。
常用API
信号处理函数的注册
入门版:函数signal
高级版:函数sigaction
信号处理发送函数
1.入门版:kill
2.高级版:sigqueue
对于入门版的信号处理API的重点在于动作,但kill 函数发送的信号是无法携带数据。
对于高级版的信号处理API的重点是信号携带的信息
signal
函数原型:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数原型由两部分组成,一个是真实处理信号的函数,另一个是注册函数
注册函数:
sighandler_t signal(int signum, sighandler_t handler);
参数
signum :指出要设置处理方法的信号
handler :第二个参数handler是一个处理函数(一个函数地址),或者是
SIG_IGN:忽略参数signum所指的信号。
SIG_DFL:恢复参数signum所指信号的处理方法为默认值。
handler函数:
真实处理信号的函数:
typedef void (*sighandler_t)(int);
传中断函数的原型中,有一个参数是 int 类型,显然也是信号产生的类型,方便使用一个函数来处理多个信号。
捕捉信号 示例
键盘上敲ctrl c 的时候,会发出SIGINT的信号,被代码中的signal(SIGINT,handler)捕获了,这时候会进入handler进行函数处理,因为SIGINT对应的整数是2,会把2传到handler函数里面的signum参数里,进而在handler函数里面进行处理。
signalDemo1.c 捕获信息(使用kill里面的指令发送信号,供程序捕获信号)
//真实处理信号的函数
void handler(int signum)
{
printf("get signum=%d\n",signum);
switch(signum){
case 2:
printf("SIGINT\n");
break;
case 10:
printf("SIGUSR1\n");
break;
}
}
int main()
{
signal(SIGINT,handler);//信号处理函数的注册
signal(SIGUSR1,handler);//信号处理函数的注册
while(1);
return 0;
}
对于已注册的信号,使用 kill 发送都可以正常接收到,但是如果发送了未注册的信号,则会使得应用程序终止进程(结束程序 ,大部分信号默认都是使进程终止)
signalDemo1Ctrl.c (用程序来执行kill命令)
主要是将kill信号处理发送函数置于程序中,同时也可以使用system来实现kill
kill补充
函数原型
int kill(pid_t pid, int sig);
参数
pid: 指定的进程pid
sig:信号的编号
返回值:
成功返回0 ,出错返回-1
int main(int argc,char *argv[])
{
int signum=0;
int pid=0;
signum= atoi(argv[1]);//转为整型
pid= atoi(argv[2]);//转为整型
printf<