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

 更新时间:2025年04月01日 10:36:15   作者:s_little_monster_  
从信号产生到信号保存,中间经历了很多,当操作系统准备对信号进行处理时,还需要判断时机是否 “合适”,在绝大多数情况下,只有在 “合适” 的时机才能处理信号,即调用信号的执行动作,本文将给大家介绍Linux进程信号的发送和保存方法,需要的朋友可以参考下

一、信号发送

1、信号动作

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

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、前置概念

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

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

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

2、阻塞信号

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

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

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

3、保存信号

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

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

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

4、信号递达

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

5、总结

一个信号,首先要经过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_SETMASK,则set表示要设置的新的信号掩码,若该参数为NULL,则不改变当前的信号掩码,仅获取当前信号掩码,此时oset不能为NULL

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

2、设置pending位图

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

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

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

setsigpending函数会将当前进程中处于未决状态(即已发送但由于被阻塞而尚未被处理)的信号集存储到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信号集清空
    sigaddset(&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进程信号发送和保存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • apache time_wait连接数太多问题解决方法

    apache time_wait连接数太多问题解决方法

    这篇文章主要介绍了apache time_wait连接数太多问题解决方法,本文使用调整内核参数来解决,需要的朋友可以参考下
    2014-11-11
  • apache的access.log和error.log减肥

    apache的access.log和error.log减肥

    我的服务器是用apache搭建的,里面的access.log和error.log这两个文件要经常上去看,和清理,如果时间忙,忘记看和清理了,过不了多久,这两个文件就膨胀的非常的大,打都打不开了。
    2009-09-09
  • linux在vim中查找和替换方式

    linux在vim中查找和替换方式

    Linux中使用Vim编辑器查找和替换文本的方法非常直观和强大,Vim支持多种查找和替换的命令,包括向前查找、向后查找、查找并替换文本、替换所有匹配项、确认每个替换和在多个文件中进行替换等
    2025-02-02
  • CentOS服务器apache绑定多个域名的方法

    CentOS服务器apache绑定多个域名的方法

    这篇文章主要为大家详细介绍了CentOS服务器apache绑定多个域名的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • windows10 更新Ubuntu20.04 LTS的方法步骤

    windows10 更新Ubuntu20.04 LTS的方法步骤

    这篇文章主要介绍了windows10 更新Ubuntu20.04 LTS的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • Linux VPS配置Web网站环境一键包(LNMP/LAMP/LNMPA)

    Linux VPS配置Web网站环境一键包(LNMP/LAMP/LNMPA)

    如果我们是资深Linux用户,可能不屑于网上免费Linux Web一键包、管理面板的安装,然后自己编译或者自由的一套环境安装配置环境。但是,对于大部分用户而言,麦子个人建议还是选择较为成熟的WEB一键包或者面板安装环境
    2017-02-02
  • cloudera manager 设置开机自启的方法

    cloudera manager 设置开机自启的方法

    下面小编就为大家带来一篇cloudera manager 设置开机自启的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Apache Pulsar集群搭建部署详细过程

    Apache Pulsar集群搭建部署详细过程

    这篇文章主要介绍了Apache Pulsar集群搭建过程,搭建Pulsar集群至少需要3个组件:ZooKeeper集群、BookKeeper集群和Broker集群,本文给大家介绍的非常详细,需要的朋友可以参考下
    2022-02-02
  • Xshell实现Windows上传文件到Linux主机的方法

    Xshell实现Windows上传文件到Linux主机的方法

    这篇文章主要介绍了Xshell实现Windows上传文件到Linux主机的方法,需要的朋友可以参考下
    2017-12-12
  • 详解ubuntu安装CMake的几种方式

    详解ubuntu安装CMake的几种方式

    这篇文章主要介绍了详解ubuntu安装CMake的几种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08

最新评论