开发者

Linux进程信号的发送和保存方法

目录
  • 一、信号发送
    • 1、信号动作
    • 2、信号发送的本质
      • 普通信号
      • 实时信号
    • 3、core dump
    • 二、信号的保存
      • 1、前置概念
        • 2、阻塞信号
          • 3、保存信号
            • 4、信号递达
              • 5、总结
              • 三、信号集操作函数
                • 1、设置block位图
                  • 2、设置pending位图
                    • 3、设置handler行为
                    • 四、验证信号保存行为

                      一、信号发送

                      1、信号动作

                      通过指令man -7 signal查看信号的手册,然后往下翻翻可以看到普通信号发出后对应的操作,以及它们的信号编号,和详细描述信息

                      Linux进程信号的发送和保存方法

                      2、信号发送的本质

                      普通信号

                      信号发送的本质实际上是写信号,把信号写到进程PCB结构体对应的位图上去,在进程的PCB中有这么一个位图(是pending位图,下面会说)正好对应着我们从1 ~ 31的普通信号编号,收到哪个信号就将哪一位对应的比特位置为1,表示收到信号,然后PCB再做对应的工作

                      值得注意的是,如果连续发普通信号,那么进程只会处理最后一次的信号,每次写都是覆盖写的

                      实时信号

                      我们前面说过信号分为31个普通信号和31个实时信号,实时信号的作用类似于我们嵌入式RTOS实时运转场景,要保持实时性,实时信号发送的本质类似于普通信号,不过此时我们保存信号的载体不再是一个位图,而是一个结构体,它们被组织在信号队列当中,谁先发送谁就先入队,队列遵循先入先出的规则,所以先发送也代表着先被处理

                      值得注意的是,如果连续发实时信号,那么进程会将队列中的信号一个个全部处理

                      3、core dump

                      当程序在运行过程中发生崩溃(如段错误、除零错误等),Core dump 会记录下程序崩溃瞬间的内存状态,包括寄存器的值、调用栈信息、全局变量和局部变量的值等,开发人员可以使用调试工具编程(如 GDB)加载 Core dump 文件,通过分析这些信息,准确地找到程序崩溃的位置和原因

                      我们可以通过ulimit -c 10240将core文件的大小限制修改为10240字节,出现错误的时候core文件可能瞬间会被打满的,所以我们云服务器上一般默认core文件的大小限制为0,我们要是用的话再修改它的大小限制即可

                      形成的文件叫做core.pid,pid就是出错进程的pid,假设test进程出现错误,12314是它的pid,我们可以通过在gdb模式下输入gdb test core.12314打印错误信息和原因

                      二、信号的保存

                      1、前置概念

                      实际执行信号的处理动作称为信号递达

                      信号从产生到递达之间的状态,称为信号未决

                      被阻塞的信号产生是将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作

                      Linux进程信号的发送和保存方法

                      2、阻塞信号

                      信号被阻塞就是将信号阻塞在信号未决状态,具体实现阻塞功能的,是一个位图block

                      block是一个位图,共31位,对应1 ~ 31号信号,当对应比特位为1时,表示该编号信号被阻塞,为0则表示不阻塞

                      如果信号被阻塞则进入阻塞态,若没有被阻塞那么信号进入未决状态

                      3、保存信号

                      未决状态的作用就是保存信号,它保存信号编程的方式也是通过位图pending

                      pending也是一个位图,与block一致,对应的下标和信号编号也是一一对应,当对应比特位为1时,表示该编号信号处于未决状态,为0则信号递达

                      实际上blockpending都属于保存信号,只不过因为有两个位图,我们分开来说罢了

                      4、信号递达

                      信号递达后的信号,会执行相对应的行为,有SIG_DFL:默认处理动作,SIG_IGN:忽略,和自定义处理sighandler,这在前面提到过

                      Linux进程信号的发送和保存方法

                      5、总结

                      Linux进程信号的发送和保存方法

                      一个信号,首先要经过block,block为0来到pending,pending为0来到handler执行动作,其中,9号和19号新号还是特例,它们是不能被阻塞和保存的,这两个信号一旦发出就是直接handler,其实也不用handler了,它们对应的不可能为忽略和信号捕捉后的自定义函数,只能为默认动作终止和暂停

                      三、信号集操作函数

                      信号集操作函数顾名思义就是操作信号集的函数,sigset_t被称作信号集,是操作系统提供的数据类型,用于描述位图,下面就是信号集操作函数

                      #include <signal.h>
                      
                      int sigemptyset(sigset_t *set); 
                      // 将位图全部设置为 0
                      
                      int sigfillset(sigset_t *set); 
                      // 将位图全部都设置为 1
                      
                      int sigaddset (sigset_t *set, int signo); 
                      // 将位图中的某一位设置为 1
                      
                      int sigdelset(sigset_t *set, int signo); 
                      // 将位图中的某一位设置为 0
                      
                      int sigismember(const sigset_t *set, int signo); 
                      // 判断一个信号是否在信号集中,不在返回0,在返回1,出错返回-1
                      

                      1、设置block位图

                      sigprocmask是一个在信号处理中非常重要的系统调用,主要用于检查、修改进程的信号掩码(阻塞信号集)

                      #include <signal.h>
                      int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 
                      

                      返回值:若成功则为0,若出错则为-1

                      how:指定对信号掩码的操作方式

                      how 取值含义示例说明
                      SIG_BLOCK将 set 所指向的信号集中的信号添加到当前的信号掩码中,即阻塞 set 中的信号若当前信号掩码已阻塞 SIGINT,使用 SIG_BLOCK 并传入包含 SIGTERM 的信号集,SIGTERM 也会被阻塞
                      SIG_UNBLOCK从当前的信号掩码中移除 set 所指向的信号集中的信号,即解除对 set 中信号的阻塞若当前信号掩码阻塞了 SIGINT 和 SIGTERM,使用 SIG_UNBLOCK 并传入包含 SIGINT 的信号集,SIGINT 信号的阻塞状态将被解除
                      SIG_SETMASK将当前的信号掩码设置为 set 所指向的信号集,覆盖原来的信号掩码若原信号掩码阻塞 SIGINT,使用 SIG_SETMASK 并传入包含 SIGTERM 的信号集,信号掩码将只阻塞 SIGTERM

                      set:指向一个sigset_t类型的信号集,该信号集包含了要操作的信号,如果how的值为SIG_BLOCKSIG_UNBLOCK,则set表示要添加或移除的信号集;如果how的值为SIG_SEjsTMASK,则set表示要设置的新的信号掩码,若该参数为NULL,则不改变当前的信号掩码,仅获取当前信号掩码,此时oset不能为NULL

                      oset:指向一个sigset_t类型的信号集,用于存储调用sigprocmask之前的信号掩码,如果不需要保存旧的信号掩码,可以将该参数设置为NULL

                      2、设置pending位图

                      sigpending是一个用于获取进程当前未决信号集的系统调用

                      #include <signal.h>
                      int sigpending(sigset_t *set);
                      

                      返回值:成功返回0,失败返回-1

                      setsigpending函数会将当前进程中处于未决状态(即已发送但由于被阻python塞而尚未被处理)的信号集存储到set所指向的sigset_t对象中

                      3、设置handler行为

                      三种情况,默认,忽略和自定义,自定义那当然是signal函数,前面有,不再赘述

                      四、验证信号保存行为

                      #include <IOStream>
                      #include <signal.h>
                      #include <unistd.h>
                      
                      using namespace std;
                      //打印出位图
                      void PrintPending(const sigset_t &pset)
                      {
                          for(int i = 31; i >= 1; i--)
                          {
                              cout << sigismember(&pset, i);
                          }
                          cout << endl;
                      }
                      
                      void handler(int signum)
                      {
                          cout << "catch a signum: " << signum << endl;
                      }
                      
                      int main()
                      {
                      	//自定义捕捉2号信号
                          signal(2, handler);
                      
                          sigset_t bset, oset; 
                          sigemptyset(&bset); // bset信号集清空
                          sigemptyset(&oset); // oset信号集清空
                          sigphpaddset(&bset, 2); // 将bset的第2位设为1,也就是给bset中添加上2号信号
                      
                          // 调用系统调用,将数据设置进内核,设置block,此时2号信号被阻塞
                          sigprocmask(SIG_SETMASK, &bset, &oset);
                      
                          // 重复打印当前进程的 pending 信号集,期间向进程发送 2号信号
                          // 因为 2号信号被阻塞了,所以 2号信号会一直被保存在 pending 中
                          sigset_t pset; 
                          sigemptyset(&pset);// pset信号集清空
                          int cnt = 0;
                          //在15秒内,未接受信号前,都是一直打印0
                          while(true)
                          {
                               int n = sigpending(&pset);
                               if(n < 0) continue;
                               //打印位图
                               PrintPending(pset);
                               sleep(1);
                               cnt++;
                               if(cnt == 15)
                               {
                                  cout << "unblock 2 signo" << endl;
                                  // 打印15次位图后解除阻塞
                                  sigdelset(&bset, 2);//将bset的第2位设置为0,也就是给bset去除2号新号,不阻塞2号
                                  //设置当前信号屏蔽字为oset指向的值,也就是0
                                  sigprocmask(SIG_SETMASK, &oset, nullptr);
                               }
                          }
                      
                          return 0;
                      }
                      
                      

                      查看一下效果,在3秒后,我按下ctrl+c,然后我们的捕捉信号函数没有工作,说明信号被阻塞了,然后15秒后我们自动放开阻塞,瞬间打印出handler函数定义要打印的信息,再按ctrl+c就正常进行handler行为了

                      Linux进程信号的发送和保存方法

                      到此这篇关于linux进程信号的发送和保存方法的文章就介绍到这了,更多相关Linux进程信号发送和保存内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

                      0

                      上一篇:

                      下一篇:

                      精彩评论

                      暂无评论...
                      验证码 换一张
                      取 消

                      最新运维

                      运维排行榜