Linux系统编程—信号捕捉

前面咱们学习了信号产生的几种方式,而对于信号的处理有以下几种方式:程序员

  1. 默认处理方式;
  2. 忽略;
  3. 捕捉。

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

signal函数

函数原型:架构

sighandler_t signal(int signum, sighandler_t handler);函数

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

函数做用:网站

注册一个信号捕捉函数,也就是说,收到了某个信号,就执行它所注册的回调函数。人工智能

函数参数:spa

signum:信号编号,尽可能用宏来写,而别用数字,这样更适合跨平台;rest

handler:注册的回调函数;code

函数缺陷:

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

#include <stdio.h>
 #include <signal.h>
 
 void func()
 {
     printf("SIGQUIT catched!\n");
 }
 
 int main()
{
    signal(SIGQUIT, func);
    while(1);
}

sigaction函数

函数原型:

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

函数做用:

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

返回值:

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

参数:

signum:信号编号,尽可能用宏来写,而别用数字,这样更适合跨平台;

act:传入参数,新的信号捕捉方式;

oldact:传出参数,旧的信号捕捉方式

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

struct sigaction结构体

原型:

struct sigaction {

​ void (*sa_handler)(int);

​ void (sa_sigaction)(int, siginfo_t , void *);

​ sigset_t sa_mask;

​ int sa_flags;

​ void (*sa_restorer)(void);

};

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

首先,sa_restorer和sa_sigaction这两个成员一个已经被弃用了,另外一个不多使用,因此咱们暂且无论它们,重点掌握剩下的三个。

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

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

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

img

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

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

sa_flags:一般设置为0,表示使用默认属性。

再来看另一个场景:

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

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

#include <stdio.h>
 #include <signal.h>
 #include <unistd.h>
 
 void func(int signal)
 {
     printf("SIGQUIT catched!\n");
     sleep(2);   //用来模拟回调函数执行很长时间
     printf("func finished!\n");
}

int main()
{
    struct sigaction act;
    act.sa_handler = func;
    sigemptyset(&act.sa_mask);  //先清空临时阻塞信号集
    sigaddset(&act.sa_mask, SIGINT);    // 执行回调函数期间,屏蔽SIGINT
    act.sa_flags = 0;

    sigaction(SIGQUIT, &act, NULL); //注册回调函数

    while(1);

    return 0;
}

更多精彩内容,请关注公众号良许Linux,公众内回复1024可免费得到5T技术资料,包括:Linux,C/C++,Python,树莓派,嵌入式,Java,人工智能,等等。公众号内回复进群,邀请您进高手如云技术交流群。

img


最后,最近不少小伙伴找我要Linux学习路线图,因而我根据本身的经验,利用业余时间熬夜肝了一个月,整理了一份电子书。不管你是面试仍是自我提高,相信都会对你有帮助!

免费送给你们,只求你们金指给我点个赞!

电子书 | Linux开发学习路线图

也但愿有小伙伴能加入我,把这份电子书作得更完美!

有收获?但愿老铁们来个三连击,给更多的人看到这篇文章

推荐阅读: