信号的基本概念及产生

一、信号的基本概念
一些小细节:
1、Ctrl-C产生的信号只能发给前台进程。一个命令后面加上一个&可以放到后台运行,这样shell不必等待进程结束就可以接受新的命令,启动新的进程。
2、shell可以运行一个前台程序和任意多个后台进程,只有前台进程才能接到想Ctrl -C这样控制键产生的信号。
3、前台进程在运行过程中用户随时可能按下Ctrl-C而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到SIGINT信号而终止,,所以信号相对于进程的控制流程来说是异步的。
我们可以使用kill -l命令可以观察看系统定义的信号列表
这里写图片描述
我们可以看到信号的编号是1~64,我们通常把1~31的信号称为普通信号,把32~64的信号称为实时信号。
信号常见的处理方式
1、忽略此信号
2、执行该信号的默认处理动作
3、提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(Catch)一个信号。

二、信号产生的方式:

1、通过键盘按键产生
用户在终端下按下某些键时,终端驱动程序会发送信号给前台进程,例如Ctrl-C产生SININT信号,Ctrl-\产生SIGQUIT信号,Ctrl-Z产生SIGTSTP信号;
2、调用系统函数向进程发信号
硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号,例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程,再比如当前进程访问了非法的内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。
例如:我们在执行死循环时:
这里写图片描述

  • 3179是a.out进程的id,之所以出现这种现象:
  • 这里写图片描述
    是因为3179进程终止掉之前已经回到了shell提示符等待用户输入下一条命令,shell不希望segmentation fault信息和用户的输入交错在一起,所以等用户输入命令之后才显示。
  • 我们可以写成kill - SIGSEGV 3179或者写成kill -11 3279,其中11是信号的编号,我们一般知道这个段错误是非法内存访问产生的。而这个程序本身没有错,将它发SIGSEGV也能产生段错误。

kill命令是调用kill函数来实现的,kill函数可以给指定的进程发信号,raise函数可以给当前进程发送指定的信号(自己给自己发送信号)。

这里写图片描述
这里写图片描述

这两个函数都成功返回0,错误返回-1

kill函数的实现

 1 #include <stdio.h>
  2 #include <signal.h>
  3 
  4 int main(int argc,char *argv[])
  5 {
  6     if(argc !=3){                                                                                                                                         
  7         printf("usage: %s signo proc_id\n",argv[0]);
  8         return 1;
  9         }
 10 
 11     kill(atoi(argv[2]),atoi(argv[1]));//argv[1]为pid
 12 }
 13 
~                          

代码实现的结果:我们看到我们自己写的kill命令可以杀死进程。
这里写图片描述
abort函数使当前进程接收到信号而异常终止
这里写图片描述

像exit函数一样,abort函数总是会执行成功的,所以没有返回值。abort()函数会导致进程的异常终止除非SIGABRT信号被捕捉并且信号处理句柄没有返回。

下面我们来看一段代码:

 1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 int main()
  5 {
  6     int i=0;
  7     for(;i<100;i++){
  8         printf("%d\n",i);
  9         if(i==5)
 10 
 11             abort();
 12     }                                                                                                                                                     
 13         return 0;
 14     }
 15 
~                    

执行结果为
这里写图片描述
我们可以看到程序执行结束之后,遇到abort函数就异常终止了。abort()函数会导致进程的异常终止除非SIGABRT信号被捕捉并且信号处理句柄没有返回。
3、由软件条件产生的信号
SIGPIPE是一种由软件产生的信号,在“管道”中已经介绍过了,这次就认识alarm函数,和SIGALRM信号。
这里写图片描述

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号,该信号的默认处理动作是终止当前进程。
这个函数的给返回值时0或者是以前设定闹钟时间余下的秒数,打个比方来说,某人要小睡一觉,设定闹钟为30分钟之后响铃,20分钟后被人吵醒了一次,还想多睡一会,于是重新设定闹钟的值为15分钟之后响铃,以前设定的闹钟余下的时间还有10分钟,如果seconds值为0,表示取消以前设定的闹钟的值,函数返回值仍然是以前设定闹钟时间还余下的秒数(自己验证一下?)

 1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int main()
  5 {
  6     int count=14;
  7     alarm(1);
  8     for(;1;count++)
  9     {
 10         printf("count=%d\n",count);
 11     }
 12     return 0;
 13 }                                                                                                                                     

这里写图片描述


1 #include <stdio.h>                                                                                                                    
  2 #include <unistd.h>
  3 
  4 
  5 int count=0;
  6 void handler(int signo)
  7 {
  8     printf("get a signo: %d,count is: %d\n",signo,count);
  9     exit(1);
 10 }
 11 
 12 int main()
 13 {
 14     signal(14,handler);
 15     alarm(1);
 16     while(、1)
 17     {
 18         count++;
 19    // printf("haha: %d\n",count++);printf往显示器输出,是外设,访问外设是I/O计算功能受到拖累,当printf去掉后,发现执行计数的功能速度加强。
 20     }
 21     return 0;
 22 }

这里写图片描述
最后我们也验证了CPU的计数速度远远大于外设的执行速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值