信号的基本概念及产生信号

本节讲述重点:
1.信号的基本概念:
2.一些常用的特殊信号的深入了解(用代码呈现)
3.了解核心转储Core Dump
4.信号的产生方式:(4种)(mykill代码)

1.信号的基本概念:
首先使用kill -l命令查看系统定义的信号列表:
这里写图片描述
由于没有32、33信号,所以信号共有62个。其中编号34以上为实时信号,我们先来讨论34以下的信号。
我们来看看signal(7),可以看到这些信号的产生条件及默认的处理动作。
这里写图片描述

那么现在问题来了,这些信号都是什么作用?是谁发给谁的呢?发送的方式又是什么呢?

答案很简单,想想我们在日常生活中也有很多信号,比如常见的红绿灯信号。所以linux中的信号也是类似的。它无非是想提供一个机制在需要的时候告诉某个进程该怎样做。是一种规定,便于系统操作。就像我们都知道”红灯停,绿灯行“一样。

信号的发送者有很多,比如终端驱动程序,进程,操作系统等。而接收者大多是一个进程。

发送的方式:即是修改目标进程PCB中对应的信号位图。只需要修改一个比特位(操作系统完成):收到信号就置1。

产生信号的方式概览:

  • 用户在终端按某些键时,终端驱动程序会发送信号给前台进程。
  • 硬件异常产生信号,这些条件又硬件检测到并通知内核,然后内核向当前进程发送适当的信号。
  • 一个进程调用Kill(2)函数可以发送信号给另一个进程,
  • 软件条件产生。

信号处理的方式:

  • 忽略此信号
  • 执行该信号的默认处理动作(大多数信号的默认处理动作都为终止进程)
  • 提供一个信号处理函数,要求内核在处理该信号时切换到用用户态执行此处理函数,这种方式为捕捉信号。

在此介绍一个信号捕捉函数:
函数原型为:

typedef void(*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);


在此声明:9号信号不能进行捕捉。
如下代码为捕捉2号信号。

#include<stdio.h>
  2 #include<signal.h>
  3 int handler(int signo)
  4 {
  5         printf("I am a sig: %d\n",signo);
  6 
  7 }
  8 int main()
  9 {
 10         signal(2,handler);
 13         while(1);
 14         return 0;
 15 }

这里写图片描述
2.一些常用的特殊信号的深入了解

  1. 2号信号SIGINT(ctrl-c)。
    此信号的默认动作为终止进程。但是ctrl-c只能发送至前台。下面用代码来呈现:
    先写一个死循环,并用进程命令查看。观察输入Ctrl -C前后系统中的进程变。
  1 #include<stdio.h>
  2 #include<signal.h>
  8 int main()
  9 {
 13         while(1);
 14         return 0;
 15 }

这里写图片描述
上面说到ctrl-c只能发给前台,那么什么是前台呢,什么又是后台呢?

以上边程序为例,我们在运行程序时在命令后边加一个”&“符号,及在后台进行,即为后台进程再用命令查看系统中的进程,就会变成这样。
这里写图片描述
会发现毫无反应,即可说明ctrl -c 只能给前台发信号。

对于前台进程与后台进程,我们需要知道以下几点:

1.用命令查看时,发现后台进程STAT状态栏是R,所以有+表示前台进程,无+表示后台进程。
可以看到./sig为后台进程。
这里写图片描述

2.Ctrl-C产生的信号只能发给前台进程。因为后台进程使Shell(Shell俗称壳(用来区别于核),是指“提供使用者使用界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。)不必等待进程结束就可以接受新的命令,启动新的进程。而前台进程运行时占用shell,它运行的时候SHELL不能接受其他命令。
这里写图片描述
3.前台进程在运行过程中用户随时可能按下Ctrl-C而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到SIGINT信号而终止,所以信号相对于进程的控制流程来说是异步的。

2.3号信号SIGQUIT

这里写图片描述
- Ctrl \产生SIGQUIT信号,该信号的默认处理动作为终止进程并且Core Dump。
这里写图片描述
上面我们说过了2号信号,发现2号信号与3号信号的区别是:3号信号的默认动作是终止进程并且Core Dump.
下面来重点讲讲Core Dump.

3.Core Dump:
Core Dump的概念:当一个进程要异常终止时,可以选择把金成德用户空间内数据全部保存到磁盘上,文件名通常为Core,这叫做Core Dump。也称为核心转储。

进程异常终止通常是因为有 Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做 Post-mortem Debug(事后调试)。
首先用ulimit命令查看core文件:
这里写图片描述
那么我们要允许Core文件最大为1024K:则命令为ulimit -c 1024

这里写图片描述

编写一个野指针的代码:运行后会出现Core Dump

  #include<stdio.h>
  2 #include<signal.h>
  8 int main()
  9 {
 11         int *p;
 12         *p=0;
 13         while(1);
 14         return 0;
 15 }
~                         

这里写图片描述
下面来用gdb进行调试。
这里写图片描述

4.信号的产生方式:(4种)(mykill代码)

(1).通过终端按键产生信号:上边的集中Ctrl-c与ctrl \均属于此种方式,不做详细解释。*
(2).硬件异常产生信号:

  • 硬件异常产生信号是由硬件检测到并通知内核,然后内核向当前进程发送的信号。

例如当前进程执行了除以0的指令,CPU的运算单元会产生异常。内核将这个异常解释为SIGFPE信号发送给进程。

再⽐如当前进程访问了非法内存地址,MMU会产⽣生异常。内核将这个异常解释为SIGSEGV信号(11号)发送给进程:

如下代码:

  1 #include<stdio.h>
  2 #include<signal.h>
  8 int main()
  9 {
 11         int *p;
 12         *p=0;
 13         while(1);
 14         return 0;
 15 }    

运行之后会形成核心转储:
这里写图片描述

(3).调用系统函数像进程发信号

  1. kill命令:可以给任意进程发任意信号。
    举例如下:给一个死循环程序,然后用kill命令给它发SIGSEGV(11)号信号。
  1 #include<stdio.h>
  2 #include<signal.h>
  8 int main()
  9 {
 13         while(1);
 14         return 0;
 15 } 

这里写图片描述
因为11号信号是以往遇到段错误都是由非法内存访问产生的。而此程序没有错误,给它发11号信号产生段错误。

下面来介绍系统函数:
1.kill函数:
kill命令就是调用kill函数实现的,kill函数可以给一个指定的进程发送指定的信号。

函数原型:
#include<signal.h>
int kill(pid_t pid,int signo);

返回值:成功返回0,错误返回-1.

#include<stdio.h>
  2 #include<signal.h>
  3 #include<sys/types.h>
  4 
  5 int main(int argc,char*argv[])
  6 {
 16         if(argc!=3){
 17         printf("Usage:%s signo pid\n",argv[0]);
 18         return 1;
 19         }
 20         int signo=atoi(argv[1]);//字符串转成int
 21         int pid=atoi(argv[2]);
 22 
 23         kill(pid,signo);
 24         return 0;
 25 }

运行结果为:
这里写图片描述
2.raise函数:自己给自己发信号
函数原型:

#include<signal.h>
int raise(int signo);

返回值:成功返回0,错误返回-1.

 #include<stdio.h>
  2 #include<signal.h>
  3 #include<sys/types.h>
  4 
  5 int main(int argc,char*argv[])
  6 {
  7         int count=0;
  8         while(1){
  9         printf("hello %d\n",count++);
 10     if(count>=1000){
 11         raise(2);//当到1000时自己给自己发2号信号,终止进程
 13                 }
 14         }
 15 }

这里写图片描述

3.abort函数:使当前进程收到信号而异常中止。
函数原型:

#include<signal.h>
void abort(void);
像exit函数一样,函数总是会成功的,所以没有返回值。
 #include<stdio.h>
  2 #include<signal.h>
  3 #include<sys/types.h>
  4 
  5 int main(int argc,char*argv[])
  6 {
  7         int count=0;
  8         while(1){
  9         printf("hello %d\n",count++);
 10     if(count>=1000){
 11        abort();
 13                 }
 14         }
 15 }

这里写图片描述

4.由软件条件产生信号:
主要介绍alarm函数和SIGALRM信号。
alarm函数:

  • 函数原型:unsigned int alarm(unsigned int seconds)在头文件unistd.h中。
  • 作用: 调用alarm函数可以设定⼀个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终⽌当前进程。这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。

下面程序的作用是1秒钟之内不停地数数,1秒钟到了就被SIGALRM信号终止。

 #include<stdio.h>
  2 #include<signal.h>
  3 #include<sys/types.h>
  4 
  5 int main(int argc,char*argv[])
  6 {
  7         int count=0;
  8         alarm(1);
  9         while(1){
 16                 count++;
 17                 printf("count=%d\n",count);
 18         }

这里写图片描述

猜你喜欢

转载自blog.csdn.net/xiaodu655/article/details/80231452