在生活的角度,我们随时随地不可能收到信号,比如我们在过马路的时候,看到了绿灯就过去,红灯就停止;在上学的时候,听到了下课铃声就高兴飞起。其本质就是我们知道这个信号是干嘛的,我们可以识别信号并处理信号,在进程角度也是如此。
信号产生前:信号还没有产生的时候,对应普通人来讲,我们是有足够的知识知道这个信号来了我们该怎么做,进程也是如此(进程内部一定能够识别信号,程序员已经在编写设计进程的时候已经内置了处理方案)(信号属于进程内部特有的特征)。
信号产生中:信号已经来了,但是进程不一定要立即处理(时间窗口),因为有可能正在做优先级更高的事情,这个时候进程需要用某种方式来记录这个信号。
准备处理信号:处理信号的三种方式:默认行为、自定义行为、忽略行为。
用买快递来形容这三种行为。
信号产生的过程与进程是属于异步关系,不会相互干扰。
之前我经常用Ctrl+C
来结束进程,在我们按Ctrl+C的时候,键盘是产生了硬件中断,被OS获取,解释为信号,发生给目标前台进程,进程收到这个信号,进而引起进程退出。
#include
#include
int main()
{
while(1){
printf("1\n");
slepp(1);
}
return 0;
}
进程接收到系统获取Ctrl+C解释的信号的处理方式是属于默认行为。
注意:
Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C 这种控制键产生的信号。
前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。
信号是进程之间事件异步通知的一种方式,属于软中断。
kill -l
查看系统定义的进程。
共有62个信号,其中白色的是普通信号,黑色的是实时信号。
这些信号都是宏表示的。
使用
通过vim /usr/include/asm-generic/signal.h
可以看到
(sigaction函数稍后详细介绍),可选的处理动作有以下三种:
我们通过kill -l命令知道普通信号是31个,一字节32比特位,通过这些我们可以判断记录信号的方式是通过位图。而这个位图是在PCB中的。
0000 0000 0000 0000 0000 0000 0000 0000 0000
比特位的位置:信号的编号
比特位的内容:是否收到信号
进程收到信号本质是进程位图被修改了,且只要OS才有资格来修改信号位图。
我们有很多个组合键入Crtl+C,这些是通过终端按键来给进程发信号的。
其中Ctrl+C是SIGINT信号。SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,现在我们来验证一下。
首先解释什么是Core Dump。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部 保存到磁盘上,文件名通常是core,这叫做Core Dump。进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。默认是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文件。 首先用ulimit命令改变Shell进程的Resource Limit,允许core文件最大为1024K: $ ulimit -c1024
我们通过ulimit -a
查看core的大小。
通过ulimit -c 1024
修改core大小
编写一段除0错误的代码,在运行之后会产生一个core.xxx文件,xxx是这个进程的id。
#include
int main()
{
int a=1/0;
return 0;
}
通过kill
命令对进程发信号
比如发生2号信号
kill -2 进程的PID
kill -SIGQUIT 进程的PID
信号是可以自定义捕捉的,使用到的函数是signal.
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数:signum:捕捉的是几号信号
sighandler:捕捉到的信号的处理方式
返回值:signal()返回信号处理程序的上一个值。
如果发生错误,则设置errno以指示原因。
代码实现:
#include
#include
#include
void sighand(int sig)
{
printf("catah a sig:%d \n",sig);
}
int main()
{
int signo=1;
for(signo=1;signo<=31;signo++){
signal(signo,sighand);
}
while(1){
sleep(1);
}
return 0;
}
在我们之前的博客中有提到status,其中第8个比特位我们没有说,其实第8个比特位是来表示有没有core。
我用代码验证一下:
#include
#include
#include
#include
#include
#include
int main()
{
if(fork()==0){
int a=1/0;
exit(1);
}
int status=0;
waitpid(-1,&status,0);
printf("code:%d - core:%d - sig:%d \n",
(status>>8)&0xFF,(status>>7)&1,status&0x7f);
return 0;
}