由于操作系统是进程的管理者,因此所有信号都必须经过操作系统发出。
文章目录
- 一、信号的概念
- 二、信号的常见处理方式
- 三、信号的产生
- 四、Core Dump
- 小结
- 五、信号的流程
- 六、信号集操作函数
- 七、信号的捕捉
- 八、可重入函数
- 九、volatile
- 十、SIGCHLD信号
- 附录—Linux常规信号一览表
一、信号的概念
就像打铃通知我们下课一样。
信号就是事件发生的通知,通知进程哪些事件要发生了。即便信号没发生,进程也知道如何处理这个信号,所以设置信号、捕捉信号等处理信号的动作由进程完成。
通过kill -l
指令可以查看信号的种类,其中前31个为普通信号,后31个为实时信号。普通信号和实时的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。
使用man 7 signal
或cman 7 signal
可以查看其用法和产生条件:
1.1.ctrl c发送前台信号
以下面的程序为例:
可以看到当这个程序运行起来以后,输入除了Ctrl c
之外的其他命令是没法运行的,这是因为这个程序是前台进程
,系统在一个终端中只允许有一个前台进程。因此,当这个程序在前台运行起来以后,bash
也是一个进程,此时其处在后台的,所以接收不到我们发送的其他命令。
但是Ctrl c
指令是通过硬件的输入方式中断进程,它的本质也是通过系统向进程发送信号。
所以如果把这个程序放在后台运行,Ctrl c
指令就无法终止整个程序。此时bash
处在前台,可以接收到我们输入的其他命令。
当进程被设置为后台进程时,我们在命令行输入的消息流会和后台进程的信息混合在一起,这是因为bash
进程是在前台的,我们可以输入信息,但是显示器只有一个,被两个进程同时使用,说明他是临界资源,而这个临界资源又没有被保护,因此它的数据会发生混乱。
此时输入fg 进程路径
就可以让进程从后台回到前台。
1.1.1.用signal系统调用接口验证ctrl c是信号
这个接口能够捕捉并重定向一个信号的默认处理动作,使信号不执行原来的动作,而是执行我们自定义的动作
#include <signal.h>
typedef void (\*sighandler\_t)(int);
sighandler\_t signal(int signum, sighandler\_t handler);
signum:对应信号的编号(普通信号编号1-31,实时信号编号34-64)
handler:回调函数(函数指针),传一个函数的地址。这个函数就是我们自定义的处理动作。
ctrl c
发送的是2号信号:
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void handler(int signo){
printf("catch a signal:%d\n",signo);
}
int main(){
while(1){
signal(2,handler);//收到2号信号就执行我们设定的动作
printf("I am running..!\n");
sleep(1);
}
}
由于我们修改了其默认处理动作,所以输入Ctrl c
是不会退出的,而是执行我们设定好的动作。
此时可以使用Ctrl \
来退出,这个指令是发送的是3号信号SIGQUIT
SIGSTOP和SIGKILL不可捕获
9号信号SIGKILL和19号信号 SIGSTOP是不能被signal函数捕捉并修改默认动作的。原因也很简单:如果所有信号都可以被捕捉,病毒可以将所有信号捕捉更改掉,系统就瘫痪了,因此需要这俩信号不能被捕捉,即系统始终拥有对进程的终止能力。
1.1.2.小结
- Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C 这种控制键产生的信号。
Ctrl c
产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。- 前台进程在运行过程中用户随时可能按下
Ctrl c
而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到SIGINT
信号而终止,所以信号相对于进程的控制流程来说是异步的。