信号的概念
信号是进程间通信机制中唯一的异步机制
来看看在Linux中都有哪些信号
kill -l 命令可以查看Linux中的信号列表
我们可以看到每个信号都有一个编号和一个宏定义名称,这些宏定义可以在头文件signal.h中找到。而且可以发现的是没有32、33号信号。1-31号信号叫做普通信号,34-64号信号叫做实时信号。
在这里对这些信号就不做详细的解释了,可以查看man手册 man 7 signal
信号的产生
先从我们的生活中说起,我们生活中也有很多信号,“铃声”、“红绿灯”,Linux中的信号也是一样的,是为了提供一个机制在需要的时候告诉某个进程该怎样做。是一种规定,便于系统操作。
信号的发送者有很多,比如终端驱动程序,进程,系统等。而接收者大多是一个进程。
那怎样就算是给某进程发信号呢?事实上,给进程发信号就是修改目标进程PCB结构体中的关于信号的字段。由于进程是否接收到信号本身是一个原子问题。它要么收到,要么没收到。所以PCB中就用位图来表示进程是否收到信号,只需要修改一个比特位(操作系统完成):收到信号就置1,没收到就为0
信号产生条件:
- 通过终端按键产生信号
- 硬件异常产生的信号
- 调用系统函数向进程发送信号
- 由软件条件产生信号
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在解释信号产生条件之前,我们先来谈谈遇到信号我们该如何处理呢?
我们一共有三种解决方式:
- 执行该信号的默认处理动作
- 忽略该信号
- 执行自定义动作
对于大部分的信号来说,默认处理动作,都是终止进程,当然还有继续、暂停等;
忽略该信号就是继续执行自己的操作,但需要注意的是有两个特殊信号SIGKILL信号和 SIGSTOP信号,这两个信号是不能被忽略的;
自定义动作,就是捕捉该信号,用户本身提供一个信号处理函数,内核在处理这个信号时,切换到用户态去执行这个函数,这就是捕捉一个信号。
来看看信号捕捉函数的原型:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//signum:需要捕捉的信号
//handler:该信号需要进行的动作
我们可以发现,sighandler_t是一个函数指针,函数类型是void*,参数是int。接下来在说明信号产生时,我们再来做验证
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
接下来解释一下这四种情况:
1.通过终端按键产生信号
当我们想让一个正在运行的程序终止时,我们经常会按下组合键 Ctrl+C、Ctrl+\ ,实际上Ctrl+C是给该进程发送了SIGINT信号,而Ctrl+\是给该进程发送SIGQUIT
SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump
我们来验证一下这两个信号:
写个死循环,分别利用Ctrl+C和Ctrl+\终止程序
#include <stdio.h>
#include <stdlib.h>
int main()
{
while(1)
{
printf("hello signal\n");
sleep(1);
}
return 0;
}
Ctrl+C
Ctrl+\ ,我们把代码修改一下,捕捉SIGINT信号,然后再利用Ctrl+\来终止
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
void handler(int signo)
{
printf("%d ,you can't kill me...\n",signo);
}
int main()
{
signal(SIGINT,handler);
while(1)
{
printf("hello signal\n");
sleep(1);
}
return 0;
}
由结果可以发现,我们按下的Ctrl+C确实是2号信号SIGINT,利用同样的方法也可以验证Ctrl+\
2. 硬件异常产生的信号
由硬件异常产生的信号是由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为发送了SIGSEGV信号(11号信号)给进程,再例如当前进程出现了除零错误,CPU的运算单元会产生异常,内核将这个异常解释为发送了SIGFPE信号(8号信号)给进程。
来验证一下:在刚刚的代码中加入产生异常的代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
void handler(int signo)
{
printf("recv %d signal.\n",signo);
exit(1);
}
int main()
{
signal(SIGSEGV,handler);
while(1)
{
printf("hello signal\n");
sleep(1);
int *p=(int*)10;
*p=20;//访问非法内存
}
return