Linux系统编程—信号捕捉

系统 Linux
信号的捕捉,说白了就是抓到一个信号后,执行我们指定的函数,或者执行我们指定的动作。下面详细介绍两个信号捕捉操作参数:signal和sigaction。

前面我们学习了信号产生的几种方式,而对于信号的处理有如下几种方式:

  • 默认处理方式;
  • 忽略;
  • 捕捉。

信号的捕捉,说白了就是抓到一个信号后,执行我们指定的函数,或者执行我们指定的动作。下面详细介绍两个信号捕捉操作参数:signal和sigaction。

[[346857]]

##signal函数

函数原型:

  1. sighandler_t signal(int signum, sighandler_t handler); 

其中,sighandler定义是这样的:typedef void (*sighandler_t)(int);

函数作用:注册一个信号捕捉函数,也就是说,收到了某个信号,就执行它所注册的回调函数。

函数参数:

  • signum:信号编号,尽量用宏来写,而别用数字,这样更适合跨平台;
  • handler:注册的回调函数;

函数缺陷:

由于历史原因,该函数在不同版本的Unix和Linux系统中可能起到的效果不一样,所以跨平台性不佳,尽量避免使用它,取而代之使用通用性更好的sigaction函数。

  1.  #include <stdio.h> 
  2.  #include <signal.h> 
  3.  
  4.  void func() 
  5.  { 
  6.      printf("SIGQUIT catched!\n"); 
  7.  } 
  8.  
  9.  int main() 
  10.     signal(SIGQUIT, func); 
  11.     while(1); 

##sigaction函数

函数原型:

  1. int sigaction(int signum, const struct sigaction act, struct sigaction oldact); 

函数作用:与signal函数类似,用来注册一个信号捕捉函数;

返回值:

成功:0;失败:-1,并设置errno;

参数:

  • signum:信号编号,尽量用宏来写,而别用数字,这样更适合跨平台;
  • act:传入参数,新的信号捕捉方式;
  • oldact:传出参数,旧的信号捕捉方式

这里特别要注意参数中struct sigaction结构体,这也是这个函数的难点所在,下面详细说明:

struct sigaction结构体

原型:

  1. struct sigaction {  
  2. void (*sa_handler)(int); 
  3. ​ void (sa_sigaction)(int, siginfo_t , void *);  
  4. ​ sigset_t sa_mask;  
  5. ​ int sa_flags;  
  6. ​ void (*sa_restorer)(void);  
  7. }; 

这个结构体成员很多,又很多是回调函数的形式,令人望而生畏。但实际上,需要掌握的只有三个。

首先,sa_restorer和sa_sigaction这两个成员一个已经被弃用了,另一个很少使用,所以我们暂且不管它们,重点掌握剩下的三个。

(1) sa_handler:指定信号捕捉后的处理函数,即注册回调函数。该成员也可以赋值为SIG_IGN,表示忽略该信号,也可注册为SIG_DFL,表示执行信号的默认动作。

(2) sa_mask:临时阻塞信号集(或信号屏蔽字)先来看这样一个情景:

某个信号已经注册了回调函数,当内核传递这个信号过来时,会先经过一个阻塞信号集,先阻塞掉部分信号。再去执行对应的回调函数。如下图示:

假如说,这个回调函数回调执行的时间比较长,比如2秒,在这2秒里,又有其它的信号过来,那进程是暂停当前回调函数,去响应新的信号,还是不管新来的信号,先把当前回调函数处理完再说?

正确的做法是,在执行回调函数期间,使用sa_mask临时的去替代进程的阻塞信号集,保证回调函数安心的执行完毕,再解除替代。注意:这个过程仅仅发生在回调函数执行期间,是临时性的设置。

(3) sa_flags:通常设置为0,表示使用默认属性。

再来看另外一个场景:

比如进程对SIGQUIT注册了回调函数,当回调函数在执行期间,又来了SIGQUIT函数,这时,进程是响应还是不响应该信号?这就是sa_flags的一个作用,当其设置为0时,表示使用默认属性,也就是先不响应该信号,而是执行完回调函数再处理此信号。

另外,阻塞的常规信号不支持排队,也就是说,执行回调函数期间,再来千百个同个信号时,系统只记录一次。而后面的32个实时信号则支持排队。

  1.  #include <stdio.h> 
  2.  #include <signal.h> 
  3.  #include <unistd.h> 
  4.  
  5.  void func(int signal) 
  6.  { 
  7.      printf("SIGQUIT catched!\n"); 
  8.      sleep(2);   //用来模拟回调函数执行很长时间 
  9.      printf("func finished!\n"); 
  10.  
  11. int main() 
  12.     struct sigaction act; 
  13.     act.sa_handler = func
  14.     sigemptyset(&act.sa_mask);  //先清空临时阻塞信号集 
  15.     sigaddset(&act.sa_mask, SIGINT);    // 执行回调函数期间,屏蔽SIGINT 
  16.     act.sa_flags = 0
  17.  
  18.     sigaction(SIGQUIT, &act, NULL); //注册回调函数 
  19.  
  20.     while(1); 
  21.  
  22.     return 0; 

 

责任编辑:赵宁宁 来源: 今日头条
相关推荐

2020-09-25 07:34:40

Linux系统编程信号量

2020-10-08 10:10:51

Linux系统编程信号集

2020-10-10 07:18:14

Linux系统编程管道

2020-09-26 21:43:59

Linux系统编程条件变量

2020-10-05 22:01:02

Linux系统编程线程属性

2016-08-16 08:26:19

Linuxsignalsigaction

2020-09-26 23:09:00

Linux系统编程读写锁

2020-09-22 07:35:06

Linux线程进程

2020-09-28 06:49:50

Linux系统编程互斥量mutex

2020-10-05 22:05:10

Linux系统编程时序竞态

2020-10-09 07:13:11

Linux系统编程mmap

2017-02-28 18:26:09

Linuxinput子系统编程

2010-03-05 13:34:54

2019-03-15 09:30:09

Linux系统CPU

2009-07-03 11:57:18

系统编程安全linux

2009-10-23 16:35:44

linux Debia

2010-02-02 13:26:53

Linux内核

2009-02-18 17:31:48

2014-04-30 11:24:31

2021-05-16 18:02:52

系统编程JavaScript
点赞
收藏

51CTO技术栈公众号