Linux下的信号signal的编写

对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。

信号的概述

1、信号的名字和编号:
每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO ”、“SIGCHLD”等等。
信号定义在signal.h头文件中,信号名都定义为正整数。
具体的信号名称可以使用kill -l来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用。
信号的名称
信号的处理:
信号的处理有三种方法,分别是:忽略、捕捉和默认动作
在这里插入图片描述2、忽略信号,大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景
捕捉信号,需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
系统默认动作,对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。
具体的信号默认动作可以使用man 7 signal来查看系统的具体定义。在此,我就不详细展开了,需要查看的,可以自行查看。也可以参考 《UNIX 环境高级编程(第三部)》的 P251——P256中间对于每个信号有详细的说明。
了解了信号的概述,那么,信号是如何来使用呢?

其实对于常用的 kill 命令就是一个发送信号的工具,kill 9 PID来杀死进程。比如,我在后台运行了一个 top 工具,通过 ps 命令可以查看他的 PID,通过 kill 9 来发送了一个终止进程的信号来结束了 top 进程。如果查看信号编号和名称,可以发现9对应的是 9) SIGKILL,正是杀死该进程的信号。而以下的执行过程实际也就是执行了9号信号的默认动作——杀死进程。
kill 杀死进程

处理信号函数的注册

1、入门版:signal
2、高级版:sigaction

信号处理发送函数

信号发送函数也不止一个,同样分为入门版和高级版
1.入门版:kill
2.高级版:sigqueue

信号注册函数——入门版

在正式开始了解这两个函数之前,可以先来思考一下,处理中断都需要处理什么问题。
按照我们之前思路来看,可以发送的信号类型是多种多样的,每种信号的处理可能不一定相同,那么,我们肯定需要知道到底发生了什么信号。
另外,虽然我们知道了系统发出来的是哪种信号,但是还有一点也很重要,就是系统产生了一个信号,是由谁来响应?
如果系统通过 ctrl+c 产生了一个 SIGINT(中断信号),显然不是所有程序同时结束,那么,信号一定需要有一个接收者。对于处理信号的程序来说,接收者就是自己。

开始的时候,先来看看入门版本的信号注册函数,他的函数原型如下:
signal 的函数原型在这里插入图片描述
在 这个头文件中。
signal(参数1,参数2);
第一个参数signum:指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。  
第二个参数handler:描述了与信号关联的动作,它可以取以下三种值:

1)SIG_IGN

这个符号表示忽略该信号。
例如:

#include <stdio.h>
#include <signal.h>
int main(int argc, char *argv[]) {
    signal(SIGINT, SIG_IGN);
    while(1);
    return 0;
}

SIGINT信号代表由InterruptKey产生,通常是CTRL +C 或者是DELETE 。执行上述代码时,按下CTRL + C程序没有反应。这就对了,如果我们想结束该程序可以按下CTRL +\来结束,当我们按下CTRL +\组合键时,产生了SIGQUIT信号,此信号并没有被忽略。

(2)SIG_DFL

这个符号表示恢复对信号的系统默认处理。不写此处理函数默认也是执行系统默认操作。
例如

#include <stdio.h>
#include <signal.h>
int main(int argc, char *argv[]) {
    signal(SIGINT, SIG_DFL);
    while(1);
    return 0;
}

这时就可以按下CTRL +C 来终止该进程了。把signal(SIGINT, SIG_DFL);这句去掉,效果是一样的

(3)sighandler_t类型的函数指针   
上面提到了sighandler_t类型声明:

 typedef void (*sighandler_t)(int); sighandler_t signal(int signum,
> sighandler_t handler);

此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为sig的信号时,就执行handler 所指定的函数。(int)signum是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制权返回进程被中断的那一点继续执行。
例如

#include <stdio.h>
#include <signal.h>
typedef void (*signal_handler)(int);

void signal_handler_fun(int signum) {
    printf("catch signal %d\n", signum);
}

int main(int argc, char *argv[]) {
    signal(SIGINT, signal_hander_fun);
    while(1);
    return 0;
}

执行时,当我们按下CTRL +C键时,会执行我们定义的信号处理函数。
catch signal 2
catch signal 2
catch signal 2
catch signal 2
=退出
每当我们按下CTRL +C键时会打印该信号的number.可以看出该信号的num为2。要想退出可以按下CTRL +\ 打印结果为最后一行。

4. 函数说明

signal()会依参数signum 指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。

当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断。

5. 返回值

返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值