信号(signal)
软中断信号(signal,又简称为信号)是Linux下用来在进程间传递消息的方式之一,也是进程间唯一的异步通信方式。从命名中可以看出信号的实质很像中断。进程间可以通过调用kill库函数发送软中断信号,Linux内核也可能给进程发送信号,用以告知该进程发生了某个异步事件。
注意,信号只用来告知进程发生了某个异步事件,并不用来传递数据。进程收到信号后会有三种处理方式:
- 忽略,不做任何处理
- 执行预先设置的处理函数(就像中断服务程序一样)
- 采用系统的默认操作,大部分是终止进程
信号的来源
-
用户
一般是键盘的输入会作为信号发送给进程,比如:
Ctrl + C
发送SIGINT信号给进程,默认动作为终止进程;Ctrl + \
发送SIGQUIT信号给进程,默认动作为终止进程并进行内核映像转储(core dump) -
内核
当进程执行出错时,内核给进程发送一个对应信号,例如:非法内存引用、浮点数溢出、执行非法指令
-
进程
C++的kill库函数用于进程间发送信号
信号的类型
信号名 | 信号值 | 默认处理动作 | 发出信号的原因 |
---|---|---|---|
SIGINT | 2 | 终止进程 | 键盘中断Ctrl+c |
SIGQUIT | 3 | 终止进程并进行内核映像转储 | 键盘的退出键被按下 |
SIGKILL | 9 | 终止进程,并且不能被捕获、忽略 | 采用kill -9 进程编号 强制杀死程序。 |
SIGSEGV | 11 | 终止进程并进行内核映像转储 | 无效的内存引用 |
SIGTERM | 15 | 终止进程 | 采用“kill 进程编号”或“killall 程序名”通知程序。 |
SIGCHLD | 20,17,18 | 忽略此信号 | 子进程结束信号 |
PS:
内核映像转储(core dump),内核映像转储是指将进程数据在内存的映像和进程在内核结构中的部分内容以一定格式转储到文件系统,并且进程退出执行,这样做的好处是为程序员 提供了方便,使得他们可以得到进程当时执行时的数据值,允许他们确定转储的原因,并且可以调试他们的程序。
信号的捕获处理
#include <signal.h>
sighandler_t signal(int signum, sighandler_t handler)
//signum 表示信号的编号
//handler 表示信号的处理方式,有三种:
1. SIG_IGN:忽略改信号,不作为
2. SIG_DFL:恢复该信号的默认处理方法
3. 自定义处理函数,注意函数参数为 (int signum)
信号的发送
int kill(pid_t pid, int sig)
//pid 目标进程号,有三种情况:
1. pid>0 将信号sig传给号为pid的进程
2. pid=0 将信号sig传给同进程组的所有进程(包括自己),常用于父进程给子进程发送信号
3. pid=-1 将信号广播到系统内所有进程,例如系统关机时向所有登录窗口广播关机信息
//sig 被发送的信号编号
信号的应用
屏蔽信号
通常为了程序不被干扰,程序开头通常会屏蔽所有信号,然后再用signal函数对关心的信号设置相应的处理方式。
for(int i=0; i<100; i++) signal(i, SIG_IGN);
搞点好玩的
改变信号 SIGINT
和 SIGTERM
的捕获处理,设计一个 ctrl+C
和 kill
都杀不掉的进程:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void func(int sig)
{
if (sig == SIGINT)
printf("\b\b杀不死,哈哈哈哈。\n");
else if (sig == SIGTERM)
printf("还是杀不死,哈哈哈哈。\n");
}
int main()
{
for (int ii = 0; ii < 100; ii++)
signal(ii, SIG_IGN); // 屏蔽全部的信号
signal(SIGINT, func);
signal(SIGTERM, func); // 设置SIGINT和SIGTERM的处理函数
while (1);
}
效果:
使用Ctrl+C
尝试关掉进程:
用 ps -ef | grep signal
找到其进程号,尝试用 kill 直接干掉:
可以看见,kill + 进程号
或 killall + 进程名
对它都无效,那改怎么杀死这个进程呢?如图,用 kill -9 进程号
:
因为kill -9 进程号
发送的信号是 SIGKILL,这个信号无法被捕获或忽略,能够快准狠杀掉进程
使系统休眠
这个是我尝试用 kill() 函数向系统所有进程发送 SIGKILL 信号后发现的,当然是在虚拟机上,我可不敢在主机上这样搞:
#include <stdio.h>
#include <signal.h>
int main()
{
kill(-1, SIGKILL);
}
效果是虚拟机进入休眠,输入密码后能再进入,原以为会直接关机呢