• 【c++】信号处理--signal、raise、sigaction的介绍


    1.信号概述

    信号 signal 可以理解为由操作系统传给程序(进程)的事件,用来通知程序发生了什么事件。signal.h 是处理信号的C++ 库,其定义了如下类型的信号:

    编号信号备注
    1SIGHUP本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联.
    2SIGINT程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出
    3SIGQUIT和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号.
    4SIGILL执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号.
    5SIGTRAP由断点指令或其它trap指令产生. 由debugger使用.
    6SIGABRT程序自己发现错误并调用abort时产生.
    6SIGIOTSIGABRT别名,在PDP-11上由iot指令产生, 在其它机器上和SIGABRT一样.
    7SIGBUS非法地址, 包括内存地址对齐(alignment)出错. eg: 访问一个四个字长的整数, 但其地址不是4的倍数.
    8SIGFPE在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误.
    9SIGKILL用来立即结束程序的运行. 本信号不能被阻塞, 处理和忽略.
    10SIGUSR1留给用户使用
    11SIGSEGV试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
    12SIGUSR2留给用户使用
    13SIGPIPEBroken pipe
    14SIGALRM时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
    15SIGTERM程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理. 通常用来要求程序自己正常退出. shell命令kill缺省产生这个信号.
    17SIGCHLD子进程结束时, 父进程会收到这个信号.
    18SIGCONT让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
    19SIGSTOP停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
    20SIGTSTP停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号
    21SIGTTIN当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.
    22SIGTTOU类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.
    23SIGURG有”紧急”数据或out-of-band数据到达socket时产生.
    24SIGXCPU超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变
    25SIGXFSZ超过文件大小资源限制.
    26SIGVTALRM虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
    27SIGPROF类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.
    28SIGWINCH窗口大小改变时发出.
    29SIGIO文件描述符准备就绪, 可以开始进行输入/输出操作.
    30SIGPWRPower failure

    在ubuntu在可以运行kill -l 查看系统支持的信号类型。

    2. 使用signal函数处理信号

    当程序捕捉到操作系统提供的信号时,可以调用signal对相应信号进行处理,其函数原型为:

    signal(registered signal, signal handler)
    
    • 1

    这个函数接收两个参数:第一个参数是要处理的信号类型;第二个参数描述了与信号关联的动作;

    动作可以分为三类:
        I. 默认处理:对信号进行该信号的系统默认处理,第二参数为 SIG_DFL。
        II. 忽略信号:忽略该信号,第二参数为 SIG_IGN。
        III. Function handler:指定处理函数,由该函数来处理,第二参数为 函数指针 。

    例子1.:使用 signal() 函数捕获 SIGINT 信号,并指定处理函数输出相关信息

    #include 
    #include 
    #include 
     
    using namespace std;
     
    void signalHandler( int signum )
    {
        cout << "Interrupt signal (" << signum << ") received.\n";
     
        // 清理并关闭
        // 终止程序  
     
       exit(signum);  
     
    }
     
    int main ()
    {
        // 注册信号 SIGINT 和信号处理程序
        signal(SIGINT, signalHandler);  
     
        while(1){
           cout << "Going to sleep...." << endl;
           sleep(1);
        }
     
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    当上面的代码被编译和执行时,它会产生下列结果:

    Going to sleep....
    Going to sleep....
    Going to sleep....
    
    • 1
    • 2
    • 3

    按 Ctrl+C 来中断程序,程序捕获信号,程序打印如下内容并退出:

    Going to sleep....
    Going to sleep....
    Going to sleep....
    Interrupt signal (2) received.
    
    • 1
    • 2
    • 3
    • 4

    3. 使用raise函数生成信号

    函数原型:

    int raise (signal sig); //sig 是要发送的信号的编号
    
    • 1

    其中:sig 是要发送的信号的编号

    例子:

    #include 
    #include 
    #include 
     
    using namespace std;
     
    void signalHandler( int signum )
    {
        cout << "Interrupt signal (" << signum << ") received.\n";
     
        // 清理并关闭
        // 终止程序 
     
       exit(signum);  
     
    }
     
    int main ()
    {
        int i = 0;
        // 注册信号 SIGINT 和信号处理程序
        signal(SIGINT, signalHandler);  
     
        while(++i){
           cout << "Going to sleep...." << endl;
           if( i == 3 ){
              raise( SIGINT);
           }
           sleep(1);
        }
     
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    运行结果:

    Going to sleep....
    Going to sleep....
    Going to sleep....
    Interrupt signal (2) received.
    
    • 1
    • 2
    • 3
    • 4

    4.sigaction函数

    signal 函数的使用方法简单,但并不属于 POSIX 标准,在各类 UNIX 平台上的实现不尽相同,因此其用途受到了一定的限制。而 POSIX 标准定义的信号处理接口是 sigaction 函数,其接口头文件及原型如下:

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

    ◆ signum:要操作的信号。
    ◆ act:要设置的对信号的新处理方式。
    ◆ oldact:原来对信号的处理方式。
    ◆ 返回值:0 表示成功,-1 表示有错误发生。

    结构体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);
     };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • sa_handler:是一个函数指针,其含义与 signal 函数中的信号处理函数类似
    • sa_sigaction: 是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息
    • sa_mask: 用来指定在信号处理函数执行期间需要被屏蔽的信号,特别是当某个信号被处理时,它自身会被自动放入进程的信号掩码,因此在信号处理函数执行期间这个信号不会再度发生。
    • sa_flags: 用来设置信号处理的其他相关操作,它可以是以下值的“按位或”组合:
      ◆ SA_RESTART:使被信号打断的系统调用自动重新发起。
      ◆ SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
      ◆ SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
      ◆ SA_NODEFER:一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。 SA_NODEFER标记使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
      ◆ SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
      ◆ SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。
    void show_handler(int sig)
    {
        printf("I got signal %d\n", sig);
        int i;
        for(i = 0; i < 5; i++) 
       {
            printf("i = %d\n", i);
            sleep(1);
        }
    }
     
    int main(void)
    {
        int i = 0;
        struct sigaction act, oldact;
        act.sa_handler = show_handler;
        sigaddset(&act.sa_mask, SIGQUIT);         //见注(1)
        act.sa_flags = SA_RESETHAND | SA_NODEFER; //见注(2)
        //act.sa_flags = 0;                      //见注(3)
     
        sigaction(SIGINT, &act, &oldact);
        while(1) 
       {
            sleep(1);
            printf("sleeping %d\n", i);
            i++;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    (1)如果在信号SIGINT(Ctrl + c)的信号处理函数show_handler执行过程中,本进程收到信号SIGQUIT(Crt+),将阻塞该信号,直到show_handler执行结束才会处理信号SIGQUIT。
    (2) SA_RESETHAND:信号处理之后重新设置为默认的处理方式;SA_NODEFER使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
    (2)如果不需要重置该给定信号的处理函数为缺省值;并且不需要阻塞该给定信号(无须设置sa_flags标志),那么必须将sa_flags清零,否则运行将会产生段错误。但是sa_flags清零后可能会造成信号丢失!

    参考:

  • 相关阅读:
    zookeeper应用之leader选举
    linux常见命令-文件目录类
    antd form+upload把上传后的文件删除后,表单校验失效了
    【程序的编译(预处理操作)+链接】
    m序列生成器
    目标检测YOLO系列从入门到精通技术详解100篇-【目标检测】目标视觉检测
    BM25:信息检索的核心算法解析
    计算机毕业设计(附源码)python应急互助信息管理系统
    AAPT: error: resource android:attr/lStar not found
    vue3+vite+pinia+axios+mock+ElementPlus:登录,动态路由,存储,网络(mock) js非ts纯前端
  • 原文地址:https://blog.csdn.net/qq_44876051/article/details/126303548