欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

信号的基本概念及产生

程序员文章站 2023-12-23 17:11:34
...

一、信号的基本概念
一些小细节:
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的计数速度远远大于外设的执行速度。

上一篇:

下一篇: