zoukankan      html  css  js  c++  java
  • Linux系统编程—信号捕捉

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

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

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

    signal函数

    函数原型:

    sighandler_t signal(int signum, sighandler_t handler);

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

    函数作用:

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

    函数参数:

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

    handler:注册的回调函数;

    函数缺陷:

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

     #include <stdio.h>
     #include <signal.h>
     
     void func()
     {
         printf("SIGQUIT catched!
    ");
     }
     
     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!
    ");
         sleep(2);   //用来模拟回调函数执行很长时间
         printf("func finished!
    ");
    }
    
    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

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

  • 相关阅读:
    cmd中输入vue ui不起作用
    win10下如何让别人访问自己的数据库,开放3306端口
    maven出现报错:Failed to execute goal on project ***** Could not resolve dependencies for project com.**.**.**:jar:0.0.1-SNAPSHOT: Could not find artifact:jar:1.0-SNAPSHOT -> [Help 1]
    vue中改变字体大小,px不起作用
    vue安装教程
    Springboot快速入门
    【POI】Excel数据导入
    【MySQL】替换件需求
    【Git】Gitlab仓库访问拒绝,SSL校验影响
    【MySQL】java.sql.SQLException: Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_general_ci,IMPLICIT) for operation '='
  • 原文地址:https://www.cnblogs.com/yychuyu/p/13832170.html
Copyright © 2011-2022 走看看