欢迎光临
我们一直在努力

【Linux 系统篇(三十七)】信号(三):信号是如何保存的?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

在这里插入图片描述

大家好,欢迎来到 huangjin007_ 的博客
⭐
个人主页:huangjin007_
🔥
文章收录专栏:Linux 内功修炼手册(系统篇)

总会有一些坚持能从冰封的土地里培育出十万朵怒放的蔷薇


Linux 系统篇(三十七) —— 信号是如何保存的?

文章目录

  • Linux 系统篇(三十七) —— 信号是如何保存的?
    • 一、信号产生之后,去哪儿了?
    • 二、信号相关的四个核心概念
      • 2.1 信号递达(Delivery)
      • 2.2 信号未决(Pending)
      • 2.3 信号阻塞(Block)
      • 2.4 阻塞和忽略有什么区别?
    • 三、内核如何保存信号?
      • 3.1 三种信息分别记录什么?
      • 3.2 Block:阻塞信号集
      • 3.3 Pending:未决信号集
      • 3.4 Handler:信号处理动作
    • 四、三种信息如何配合工作?
    • 五、sigset_t:信号集
      • 5.1 sigset_t 是什么?
    • 六、信号集操作函数
      • 6.1 sigemptyset:清空信号集
      • 6.2 sigfillset:填满信号集
      • 6.3 sigaddset:向信号集中添加信号
      • 6.4 sigdelset:从信号集中删除信号
      • 6.5 sigismember:判断信号是否在信号集中
      • 6.6 函数总结
    • 七、sigprocmask:修改进程的阻塞信号集
      • 7.1 参数介绍
      • 7.2 参数二:set
      • 7.3 参数三:oldset
      • 7.4 返回值
    • 八、sigpending:读取未决信号集
    • 九、动手实验:阻塞 SIGINT,观察 Pending 的变化
      • 9.1 实验目标
      • 9.2 完整代码
      • 9.3 实验现象
      • 9.4 为什么 Ctrl+\\ 仍然可以终止程序?
    • 十、常规信号为什么会丢失?
      • 10.1 实时信号可以排队
    • 十一、Core Dump:进程异常终止时留下的调试信息
      • 11.1 为什么没有生成 core 文件?
      • 11.2 使用 GDB 调试 core 文件
      • 11.3 waitpid 如何判断是否产生 Core?
    • 十二、本篇总结
      • 12.1 三个核心信息
      • 12.2 常用函数
      • 12.3 信号产生到递达的完整流程
      • 12.4 面试易错点
    • 结语:

一、信号产生之后,去哪儿了?

  在上一篇文章中,我们已经知道了信号可以通过多种方式产生:

  • 按下 Ctrl+C,产生 SIGINT 信号;
  • 使用 kill 命令,向指定进程发送信号;
  • 调用 alarm(),在指定时间后产生 SIGALRM 信号;
  • 发生除零、非法内存访问等硬件异常,也可能产生对应信号。

  但是,这里有一个问题:

  信号产生之后,操作系统会立即处理它吗?

  答案是:不一定。

  我们可以想象这样一个场景:

  你正在开会,手机突然收到一条消息。可是你现在正在发言,不能立刻查看手机,于是先把消息放在那里,等有空了再处理。

  Linux 信号也有类似的机制。

  信号产生之后,不一定能够立即递达。如果暂时不能处理,内核就会先把它记录下来,等到合适的时机再处理。

  那么,内核究竟是怎么记录这些信号的呢?

  在正式学习之前,我们先认识几个非常重要的概念。


二、信号相关的四个核心概念

2.1 信号递达(Delivery)

  实际执行信号处理动作的过程,称为信号递达(Delivery)。

比如:

signal(SIGINT, handler);

void handler(int signo)
{
cout << "收到 SIGINT 信号" << endl;
}

  当 SIGINT 信号最终被处理,执行对应的处理动作时,就发生了信号递达。

  这里的处理动作有三种:

  • 执行默认动作 SIG_DFL;
  • 忽略信号 SIG_IGN;
  • 执行用户自定义的信号处理函数。
  •   这些处理动作在第一篇中已经介绍过,这里不再展开。

    2.2 信号未决(Pending)

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

    简单理解:

      信号已经产生了,但是还没有被处理,这个信号就处于未决状态。

    例如:

    #mermaid-svg-TMhcpkdB24F84V5E{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TMhcpkdB24F84V5E .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TMhcpkdB24F84V5E .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TMhcpkdB24F84V5E .error-icon{fill:#552222;}#mermaid-svg-TMhcpkdB24F84V5E .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TMhcpkdB24F84V5E .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TMhcpkdB24F84V5E .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TMhcpkdB24F84V5E .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TMhcpkdB24F84V5E .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TMhcpkdB24F84V5E .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TMhcpkdB24F84V5E .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TMhcpkdB24F84V5E .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TMhcpkdB24F84V5E .marker.cross{stroke:#333333;}#mermaid-svg-TMhcpkdB24F84V5E svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TMhcpkdB24F84V5E p{margin:0;}#mermaid-svg-TMhcpkdB24F84V5E .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-TMhcpkdB24F84V5E .cluster-label text{fill:#333;}#mermaid-svg-TMhcpkdB24F84V5E .cluster-label span{color:#333;}#mermaid-svg-TMhcpkdB24F84V5E .cluster-label span p{background-color:transparent;}#mermaid-svg-TMhcpkdB24F84V5E .label text,#mermaid-svg-TMhcpkdB24F84V5E span{fill:#333;color:#333;}#mermaid-svg-TMhcpkdB24F84V5E .node rect,#mermaid-svg-TMhcpkdB24F84V5E .node circle,#mermaid-svg-TMhcpkdB24F84V5E .node ellipse,#mermaid-svg-TMhcpkdB24F84V5E .node polygon,#mermaid-svg-TMhcpkdB24F84V5E .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TMhcpkdB24F84V5E .rough-node .label text,#mermaid-svg-TMhcpkdB24F84V5E .node .label text,#mermaid-svg-TMhcpkdB24F84V5E .image-shape .label,#mermaid-svg-TMhcpkdB24F84V5E .icon-shape .label{text-anchor:middle;}#mermaid-svg-TMhcpkdB24F84V5E .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TMhcpkdB24F84V5E .rough-node .label,#mermaid-svg-TMhcpkdB24F84V5E .node .label,#mermaid-svg-TMhcpkdB24F84V5E .image-shape .label,#mermaid-svg-TMhcpkdB24F84V5E .icon-shape .label{text-align:center;}#mermaid-svg-TMhcpkdB24F84V5E .node.clickable{cursor:pointer;}#mermaid-svg-TMhcpkdB24F84V5E .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TMhcpkdB24F84V5E .arrowheadPath{fill:#333333;}#mermaid-svg-TMhcpkdB24F84V5E .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TMhcpkdB24F84V5E .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TMhcpkdB24F84V5E .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TMhcpkdB24F84V5E .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TMhcpkdB24F84V5E .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TMhcpkdB24F84V5E .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TMhcpkdB24F84V5E .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TMhcpkdB24F84V5E .cluster text{fill:#333;}#mermaid-svg-TMhcpkdB24F84V5E .cluster span{color:#333;}#mermaid-svg-TMhcpkdB24F84V5E div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TMhcpkdB24F84V5E .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TMhcpkdB24F84V5E rect.text{fill:none;stroke-width:0;}#mermaid-svg-TMhcpkdB24F84V5E .icon-shape,#mermaid-svg-TMhcpkdB24F84V5E .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TMhcpkdB24F84V5E .icon-shape p,#mermaid-svg-TMhcpkdB24F84V5E .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TMhcpkdB24F84V5E .icon-shape .label rect,#mermaid-svg-TMhcpkdB24F84V5E .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TMhcpkdB24F84V5E .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TMhcpkdB24F84V5E .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TMhcpkdB24F84V5E :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-TMhcpkdB24F84V5E .startNode>*{fill:#F2F4F7!important;stroke:#9CA3AF!important;stroke-width:2px!important;color:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .startNode span{fill:#F2F4F7!important;stroke:#9CA3AF!important;stroke-width:2px!important;color:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .startNode tspan{fill:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .processNode>*{fill:#E0F2FE!important;stroke:#7DD3FC!important;stroke-width:1px!important;color:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .processNode span{fill:#E0F2FE!important;stroke:#7DD3FC!important;stroke-width:1px!important;color:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .processNode tspan{fill:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .pendingNode>*{fill:#FFE4E6!important;stroke:#FDA4AF!important;stroke-width:2px!important;color:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .pendingNode span{fill:#FFE4E6!important;stroke:#FDA4AF!important;stroke-width:2px!important;color:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .pendingNode tspan{fill:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .deliverNode>*{fill:#DCFCE7!important;stroke:#86EFAC!important;stroke-width:2px!important;color:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .deliverNode span{fill:#DCFCE7!important;stroke:#86EFAC!important;stroke-width:2px!important;color:#333!important;}#mermaid-svg-TMhcpkdB24F84V5E .deliverNode tspan{fill:#333!important;}

    SIGINT 信号产生

    信号暂时没有被处理

    信号处于未决状态 (Pending)

    最终执行信号处理动作

    信号递达 (Delivery)

    注意:

      未决描述的是一种状态,而不是一种处理动作。

    2.3 信号阻塞(Block)

      进程可以选择阻塞某个信号。

      阻塞可以理解为:

      进程主动告诉操作系统:

    这个信号先别处理,暂时放一放。

      例如,我们可以选择阻塞 SIGINT:

    sigset_t block_set;

    sigemptyset(&block_set);
    sigaddset(&block_set, SIGINT);

      不过,这里只是准备好了一个信号集,还没有真正阻塞信号。

      真正修改进程信号屏蔽字,需要使用后面要学习的 sigprocmask()。

      当一个信号被阻塞时,即使它产生了,也不会立即递达,而是保持未决状态。

      等到解除阻塞之后,内核才有机会递达这个信号。

    2.4 阻塞和忽略有什么区别?

      两者看起来都可能导致进程没有执行对应的信号处理函数,但它们发生的阶段完全不同。

    对比阻塞(Block)忽略(Ignore)
    发生阶段 递达之前 递达时
    信号是否递达 不会递达 已经递达
    信号状态 保持未决 不再等待处理
    能否改变处理动作 可以在递达前修改 需要重新设置处理动作

    举个例子:

      假设我们把 SIGINT 设置为忽略:

    signal(SIGINT, SIG_IGN);

      此时按下 Ctrl+C,信号递达后执行忽略动作,进程不会因为这个信号而终止。

      但是,如果我们阻塞了 SIGINT:

    sigprocmask(SIG_BLOCK, &block_set, nullptr);

      此时按下 Ctrl+C,信号会进入未决状态,而不是直接被忽略。


    三、内核如何保存信号?

      前面介绍了三个概念:

    • Block:信号是否被阻塞;
    • Pending:信号是否处于未决状态;
    • Handler:信号应该如何处理。

      为了方便理解,我们可以把它们看成内核为信号维护的三类信息。

    3.1 三种信息分别记录什么?

      假设一个进程收到了 SIGINT 信号。

      内核需要知道:

  • 这个信号是否被阻塞?
  • 这个信号是否已经产生但尚未递达?
  • 如果可以递达,应该执行什么处理动作?
  •   因此,可以用下面的概念图来理解:

    #mermaid-svg-Vp4bFYjdbNz7YuZy{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Vp4bFYjdbNz7YuZy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Vp4bFYjdbNz7YuZy .error-icon{fill:#552222;}#mermaid-svg-Vp4bFYjdbNz7YuZy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Vp4bFYjdbNz7YuZy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Vp4bFYjdbNz7YuZy .marker.cross{stroke:#333333;}#mermaid-svg-Vp4bFYjdbNz7YuZy svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Vp4bFYjdbNz7YuZy p{margin:0;}#mermaid-svg-Vp4bFYjdbNz7YuZy .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Vp4bFYjdbNz7YuZy .cluster-label text{fill:#333;}#mermaid-svg-Vp4bFYjdbNz7YuZy .cluster-label span{color:#333;}#mermaid-svg-Vp4bFYjdbNz7YuZy .cluster-label span p{background-color:transparent;}#mermaid-svg-Vp4bFYjdbNz7YuZy .label text,#mermaid-svg-Vp4bFYjdbNz7YuZy span{fill:#333;color:#333;}#mermaid-svg-Vp4bFYjdbNz7YuZy .node rect,#mermaid-svg-Vp4bFYjdbNz7YuZy .node circle,#mermaid-svg-Vp4bFYjdbNz7YuZy .node ellipse,#mermaid-svg-Vp4bFYjdbNz7YuZy .node polygon,#mermaid-svg-Vp4bFYjdbNz7YuZy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Vp4bFYjdbNz7YuZy .rough-node .label text,#mermaid-svg-Vp4bFYjdbNz7YuZy .node .label text,#mermaid-svg-Vp4bFYjdbNz7YuZy .image-shape .label,#mermaid-svg-Vp4bFYjdbNz7YuZy .icon-shape .label{text-anchor:middle;}#mermaid-svg-Vp4bFYjdbNz7YuZy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Vp4bFYjdbNz7YuZy .rough-node .label,#mermaid-svg-Vp4bFYjdbNz7YuZy .node .label,#mermaid-svg-Vp4bFYjdbNz7YuZy .image-shape .label,#mermaid-svg-Vp4bFYjdbNz7YuZy .icon-shape .label{text-align:center;}#mermaid-svg-Vp4bFYjdbNz7YuZy .node.clickable{cursor:pointer;}#mermaid-svg-Vp4bFYjdbNz7YuZy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Vp4bFYjdbNz7YuZy .arrowheadPath{fill:#333333;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Vp4bFYjdbNz7YuZy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Vp4bFYjdbNz7YuZy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Vp4bFYjdbNz7YuZy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Vp4bFYjdbNz7YuZy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Vp4bFYjdbNz7YuZy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Vp4bFYjdbNz7YuZy .cluster text{fill:#333;}#mermaid-svg-Vp4bFYjdbNz7YuZy .cluster span{color:#333;}#mermaid-svg-Vp4bFYjdbNz7YuZy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Vp4bFYjdbNz7YuZy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Vp4bFYjdbNz7YuZy rect.text{fill:none;stroke-width:0;}#mermaid-svg-Vp4bFYjdbNz7YuZy .icon-shape,#mermaid-svg-Vp4bFYjdbNz7YuZy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Vp4bFYjdbNz7YuZy .icon-shape p,#mermaid-svg-Vp4bFYjdbNz7YuZy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Vp4bFYjdbNz7YuZy .icon-shape .label rect,#mermaid-svg-Vp4bFYjdbNz7YuZy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Vp4bFYjdbNz7YuZy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Vp4bFYjdbNz7YuZy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Vp4bFYjdbNz7YuZy :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-Vp4bFYjdbNz7YuZy .rootNode>*{fill:#F8FAFC!important;stroke:#94A3B8!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .rootNode span{fill:#F8FAFC!important;stroke:#94A3B8!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .rootNode tspan{fill:#333!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .blockNode>*{fill:#E0F2FE!important;stroke:#7DD3FC!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .blockNode span{fill:#E0F2FE!important;stroke:#7DD3FC!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .blockNode tspan{fill:#333!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .pendingNode>*{fill:#FFE4E6!important;stroke:#FDA4AF!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .pendingNode span{fill:#FFE4E6!important;stroke:#FDA4AF!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .pendingNode tspan{fill:#333!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .handlerNode>*{fill:#DCFCE7!important;stroke:#86EFAC!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .handlerNode span{fill:#DCFCE7!important;stroke:#86EFAC!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .handlerNode tspan{fill:#333!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .questionNode>*{fill:#FFFFFF!important;stroke:#CBD5E1!important;stroke-width:1.5px!important;stroke-dasharray:4 4!important;color:#555!important;rx:4!important;ry:4!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .questionNode span{fill:#FFFFFF!important;stroke:#CBD5E1!important;stroke-width:1.5px!important;stroke-dasharray:4 4!important;color:#555!important;rx:4!important;ry:4!important;}#mermaid-svg-Vp4bFYjdbNz7YuZy .questionNode tspan{fill:#555!important;}

    进程的信号相关信息

    Block 阻塞信号集

    Pending 未决信号集

    Handler 处理动作

    是否被阻塞?

    是否已经产生?

    应该如何处理?

    在这里插入图片描述

    3.2 Block:阻塞信号集

      阻塞信号集记录:

      哪些信号当前被进程阻塞了。

    例如:

    信号Block
    SIGHUP(1) 0
    SIGINT(2) 1
    SIGQUIT(3) 0

    其中:

    • 0:没有阻塞;
    • 1:已经阻塞。

    上面的表格表示:

      SIGINT 被阻塞,而 SIGHUP 和 SIGQUIT 没有被阻塞。

    阻塞信号集也叫作:

      信号屏蔽字(Signal Mask)。

      这里的屏蔽,指的是阻止信号递达,而不是忽略信号。

    3.3 Pending:未决信号集

      未决信号集记录:

      哪些信号已经产生,但是还没有递达。

    例如:

    信号Pending
    SIGHUP(1) 0
    SIGINT(2) 1
    SIGQUIT(3) 0

    表示:

      SIGINT 已经产生,但是还没有被递达。

      为什么会出现这种情况?

      因为信号可能被阻塞了。

    例如:

    #mermaid-svg-fReDvmQlq3U4lzAV{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fReDvmQlq3U4lzAV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fReDvmQlq3U4lzAV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fReDvmQlq3U4lzAV .error-icon{fill:#552222;}#mermaid-svg-fReDvmQlq3U4lzAV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fReDvmQlq3U4lzAV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fReDvmQlq3U4lzAV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fReDvmQlq3U4lzAV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fReDvmQlq3U4lzAV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fReDvmQlq3U4lzAV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fReDvmQlq3U4lzAV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fReDvmQlq3U4lzAV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fReDvmQlq3U4lzAV .marker.cross{stroke:#333333;}#mermaid-svg-fReDvmQlq3U4lzAV svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fReDvmQlq3U4lzAV p{margin:0;}#mermaid-svg-fReDvmQlq3U4lzAV .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-fReDvmQlq3U4lzAV .cluster-label text{fill:#333;}#mermaid-svg-fReDvmQlq3U4lzAV .cluster-label span{color:#333;}#mermaid-svg-fReDvmQlq3U4lzAV .cluster-label span p{background-color:transparent;}#mermaid-svg-fReDvmQlq3U4lzAV .label text,#mermaid-svg-fReDvmQlq3U4lzAV span{fill:#333;color:#333;}#mermaid-svg-fReDvmQlq3U4lzAV .node rect,#mermaid-svg-fReDvmQlq3U4lzAV .node circle,#mermaid-svg-fReDvmQlq3U4lzAV .node ellipse,#mermaid-svg-fReDvmQlq3U4lzAV .node polygon,#mermaid-svg-fReDvmQlq3U4lzAV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fReDvmQlq3U4lzAV .rough-node .label text,#mermaid-svg-fReDvmQlq3U4lzAV .node .label text,#mermaid-svg-fReDvmQlq3U4lzAV .image-shape .label,#mermaid-svg-fReDvmQlq3U4lzAV .icon-shape .label{text-anchor:middle;}#mermaid-svg-fReDvmQlq3U4lzAV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fReDvmQlq3U4lzAV .rough-node .label,#mermaid-svg-fReDvmQlq3U4lzAV .node .label,#mermaid-svg-fReDvmQlq3U4lzAV .image-shape .label,#mermaid-svg-fReDvmQlq3U4lzAV .icon-shape .label{text-align:center;}#mermaid-svg-fReDvmQlq3U4lzAV .node.clickable{cursor:pointer;}#mermaid-svg-fReDvmQlq3U4lzAV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fReDvmQlq3U4lzAV .arrowheadPath{fill:#333333;}#mermaid-svg-fReDvmQlq3U4lzAV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fReDvmQlq3U4lzAV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fReDvmQlq3U4lzAV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fReDvmQlq3U4lzAV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fReDvmQlq3U4lzAV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fReDvmQlq3U4lzAV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fReDvmQlq3U4lzAV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fReDvmQlq3U4lzAV .cluster text{fill:#333;}#mermaid-svg-fReDvmQlq3U4lzAV .cluster span{color:#333;}#mermaid-svg-fReDvmQlq3U4lzAV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fReDvmQlq3U4lzAV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fReDvmQlq3U4lzAV rect.text{fill:none;stroke-width:0;}#mermaid-svg-fReDvmQlq3U4lzAV .icon-shape,#mermaid-svg-fReDvmQlq3U4lzAV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fReDvmQlq3U4lzAV .icon-shape p,#mermaid-svg-fReDvmQlq3U4lzAV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fReDvmQlq3U4lzAV .icon-shape .label rect,#mermaid-svg-fReDvmQlq3U4lzAV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fReDvmQlq3U4lzAV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fReDvmQlq3U4lzAV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fReDvmQlq3U4lzAV :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-fReDvmQlq3U4lzAV .sourceNode>*{fill:#FFF3E0!important;stroke:#FFB74D!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .sourceNode span{fill:#FFF3E0!important;stroke:#FFB74D!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .sourceNode tspan{fill:#333!important;}#mermaid-svg-fReDvmQlq3U4lzAV .actionNode>*{fill:#E3F2FD!important;stroke:#64B5F6!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .actionNode span{fill:#E3F2FD!important;stroke:#64B5F6!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .actionNode tspan{fill:#333!important;}#mermaid-svg-fReDvmQlq3U4lzAV .blockedNode>*{fill:#FFE4E6!important;stroke:#FDA4AF!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .blockedNode span{fill:#FFE4E6!important;stroke:#FDA4AF!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .blockedNode tspan{fill:#333!important;}#mermaid-svg-fReDvmQlq3U4lzAV .pendingNode>*{fill:#F3E8FF!important;stroke:#D8B4FE!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .pendingNode span{fill:#F3E8FF!important;stroke:#D8B4FE!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .pendingNode tspan{fill:#333!important;}#mermaid-svg-fReDvmQlq3U4lzAV .resultNode>*{fill:#F1F5F9!important;stroke:#94A3B8!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .resultNode span{fill:#F1F5F9!important;stroke:#94A3B8!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-fReDvmQlq3U4lzAV .resultNode tspan{fill:#333!important;}

    SIGINT 产生

    检查 Block

    发现 SIGINT 被阻塞

    Pending 对应位置 1

    暂时不能递达

    因此:

      信号被阻塞时,产生的信号会保持在未决状态。

    3.4 Handler:信号处理动作

      Handler 记录的是:

      每一种信号应该执行什么处理动作。

    例如:

    信号处理动作
    SIGHUP SIG_DFL
    SIGINT SIG_IGN
    SIGQUIT 自定义函数 sighandler

      这里的处理动作有三种:

    SIG_DFL // 默认处理动作
    SIG_IGN // 忽略信号
    handler // 用户自定义处理函数

    例如:

    signal(SIGINT, SIG_IGN);

      表示把 SIGINT 对应的处理动作设置为忽略。

    而:

    signal(SIGINT, SIG_DFL);

      表示恢复 SIGINT 的默认处理动作。

    至于:

    signal(SIGINT, handler);

      则表示使用用户自定义的处理函数。

      这里可以把 Handler 理解成一份信号处理规则表:信号递达时,内核根据对应的处理动作进行处理。


    四、三种信息如何配合工作?

      现在我们把 Block、Pending 和 Handler 放在一起。

      假设当前进程的状态如下:

    信号BlockPendingHandler
    SIGHUP(1) 0 0 SIG_DFL
    SIGINT(2) 1 1 SIG_IGN
    SIGQUIT(3) 1 0 handler

      我们逐个分析。

    情况一:SIGHUP

    Block = 0
    Pending = 0
    Handler = SIG_DFL

    表示:

      没有阻塞,也没有产生过该信号。

      因此当前没有需要处理的事情。

    情况二:SIGINT

    Block = 1
    Pending = 1
    Handler = SIG_IGN

    表示:

      SIGINT 已经产生,但是被阻塞了。

      因此它只能保持未决状态,暂时不能递达。

      虽然它的处理动作是忽略,但此时还轮不到执行忽略动作。

    因为:

      只要信号还处于阻塞状态,就不会递达。

    情况三:SIGQUIT

    Block = 1
    Pending = 0
    Handler = handler

    表示:

      SIGQUIT 被阻塞了,但是还没有产生。

      因此它既不需要处理,也没有进入未决状态。

      通过这三个例子,我们就能理解:

      Block 决定信号能不能递达,Pending 记录信号有没有产生但尚未递达,Handler 决定递达时执行什么动作。


    五、sigset_t:信号集

      前面提到了 Block 和 Pending,它们都可以用位图的形式表示。

    例如:

    信号编号12345
    Block 0 1 🔴 0 0 1 🔴
    Pending 0 1 🔵 1 🔵 0 0

      可以看到,每个信号只需要一个 bit,就能表示它当前对应的状态。

    • 在 Block 中,1 表示该信号被阻塞;
    • 在 Pending 中,1 表示该信号处于未决状态。

      既然都可以使用位图表示,那么 Linux 就提供了一个信号集类型:

    sigset_t

      它用来表示一组信号。

    5.1 sigset_t 是什么?

      sigset_t 是系统提供的信号集类型。

      我们可以通过它保存一组信号,然后把这个信号集交给相关系统调用进行操作。

    例如:

    sigset_t set;

      定义了一个信号集变量。

      注意:

      不要自己猜测 sigset_t 内部究竟是几个整数,也不要直接打印它的内存内容。

      它的内部实现由系统决定。

      我们只需要使用系统提供的函数操作它即可。


    六、信号集操作函数

      Linux 提供了五个常用函数,用于操作 sigset_t。

    #include <signal.h>

    int sigemptyset(sigset_t *set);

    int sigfillset(sigset_t *set);

    int sigaddset(sigset_t *set, int signo);

    int sigdelset(sigset_t *set, int signo);

    int sigismember(const sigset_t *set, int signo);

    6.1 sigemptyset:清空信号集

    int sigemptyset(sigset_t *set);

    作用:

      初始化信号集,使其中不包含任何信号。

    例如:

    sigset_t set;

    sigemptyset(&set);

    执行之后:

    set = {}

    表示当前信号集为空。

    6.2 sigfillset:填满信号集

    int sigfillset(sigset_t *set);

    作用:

      初始化信号集,使其包含系统支持的所有信号。

    例如:

    sigset_t set;

    sigfillset(&set);

    可以理解为:

    set = {系统支持的所有信号}

      注意,某些信号不能被阻塞,例如 SIGKILL 和 SIGSTOP。即使它们出现在信号集中,也不能通过屏蔽字真正阻塞它们。

    6.3 sigaddset:向信号集中添加信号

    int sigaddset(sigset_t *set, int signo);

    作用:

      向指定信号集中添加一个信号。

    例如:

    sigset_t set;

    sigemptyset(&set);

    sigaddset(&set, SIGINT);
    sigaddset(&set, SIGQUIT);

    现在信号集包含:

    { SIGINT, SIGQUIT }

    6.4 sigdelset:从信号集中删除信号

    int sigdelset(sigset_t *set, int signo);

    作用:

      从信号集中删除指定信号。

    例如:

    sigdelset(&set, SIGINT);

    执行之后:

    { SIGQUIT }

    6.5 sigismember:判断信号是否在信号集中

    int sigismember(const sigset_t *set, int signo);

    作用:

      判断指定信号是否包含在信号集中。

    返回值:

    • 1:包含该信号;
    • 0:不包含该信号;
    • -1:调用出错。

    例如:

    if (sigismember(&set, SIGINT) == 1)
    {
    cout << "SIGINT 在信号集中" << endl;
    }

    6.6 函数总结

    函数作用
    sigemptyset 清空信号集
    sigfillset 填满信号集
    sigaddset 添加信号
    sigdelset 删除信号
    sigismember 判断信号是否存在

      这几个函数的返回值:

    • 前四个函数:成功返回 0,失败返回 -1;
    • sigismember():包含返回 1,不包含返回 0,出错返回 -1。

      特别注意:使用 sigset_t 变量之前,一定要先调用 sigemptyset() 或 sigfillset() 初始化。


    七、sigprocmask:修改进程的阻塞信号集

      前面我们已经知道:

      Block 记录哪些信号被阻塞。

      那么问题来了:

      我们应该如何修改 Block 呢?

      Linux 提供了一个非常重要的函数:

    #include <signal.h>

    int sigprocmask(int how,
    const sigset_t *set,
    sigset_t *oldset);

    它的作用是:

      读取或修改当前进程的信号屏蔽字,也就是阻塞信号集。

    7.1 参数介绍

    参数一:how

      表示应该如何修改当前的信号屏蔽字。

      它有三种取值:

    参数作用理解方式
    SIG_BLOCK 添加阻塞信号 把 set 中的信号加入屏蔽字
    SIG_UNBLOCK 解除阻塞信号 把 set 中的信号从屏蔽字删除
    SIG_SETMASK 替换整个屏蔽字 直接将屏蔽字设置为 set

      我们分别来看。

    1. SIG_BLOCK:添加阻塞

    sigprocmask(SIG_BLOCK, &set, nullptr);

    表示:

      把 set 中的信号加入当前进程的阻塞信号集。

    例如:

    原来的 Block:

    { SIGINT }

    现在 set:

    { SIGQUIT }

    执行之后:

    { SIGINT, SIGQUIT }

    可以理解为:

    Block = Block | set;

    2. SIG_UNBLOCK:解除阻塞

    sigprocmask(SIG_UNBLOCK, &set, nullptr);

    表示:

      解除 set 中信号的阻塞状态。

    例如:

    原来的 Block:

    { SIGINT, SIGQUIT }

    现在 set:

    { SIGINT }

    执行之后:

    { SIGQUIT }

    可以理解为:

    Block = Block & ~set;

    3. SIG_SETMASK:直接替换

    sigprocmask(SIG_SETMASK, &set, nullptr);

    表示:

      直接把当前进程的阻塞信号集替换为 set。

    例如:

    原来的 Block:

    { SIGINT, SIGQUIT }

    现在 set:

    { SIGTERM }

    执行之后:

    { SIGTERM }

    可以理解为:

    Block = set;

      这三种操作一定要区分清楚:

    • SIG_BLOCK:添加;
    • SIG_UNBLOCK:删除;
    • SIG_SETMASK:整体替换。

    7.2 参数二:set

      set 是一个输入参数。

      它表示我们希望添加、解除或者设置哪些信号。

    例如:

    sigset_t set;

    sigemptyset(&set);
    sigaddset(&set, SIGINT);

    sigprocmask(SIG_BLOCK, &set, nullptr);

      表示阻塞 SIGINT。

    7.3 参数三:oldset

      oldset 是一个输出参数。

      如果它不是空指针,函数会把修改之前的信号屏蔽字保存到 oldset 中。

    例如:

    sigset_t set;
    sigset_t oldset;

    sigemptyset(&set);
    sigaddset(&set, SIGINT);

    sigprocmask(SIG_BLOCK, &set, &oldset);

    执行之后:

    • 当前进程的 Block 增加了 SIGINT;
    • oldset 保存修改之前的 Block。

      这样做有什么用?

      方便我们之后恢复原来的信号屏蔽字。

    例如:

    sigprocmask(SIG_SETMASK, &oldset, nullptr);

      这就把当前信号屏蔽字恢复成之前保存的状态。

    7.4 返回值

    成功:0
    失败:–1

      还有一个非常重要的细节:

      如果解除阻塞时,发现有对应的未决信号,那么这些信号可能会立即递达。

      也就是说:

      解除阻塞不只是修改 Block,还可能触发 Pending 中信号的递达。


    八、sigpending:读取未决信号集

      前面介绍了 sigprocmask(),它可以修改 Block。

      那么,Pending 应该如何读取呢?

      Linux 提供了:

    #include <signal.h>

    int sigpending(sigset_t *set);

    作用:

      读取当前进程的未决信号集,并通过 set 参数返回。

    例如:

    sigset_t pending;

    sigpending(&pending);

      执行之后,pending 中就保存了当前进程的未决信号信息。

      我们可以结合前面学习的 sigismember() 判断某个信号是否处于未决状态:

    if (sigismember(&pending, SIGINT))
    {
    cout << "SIGINT 处于未决状态" << endl;
    }

    返回值:

    成功:0
    失败:-1

    注意:   sigpending() 只能读取 Pending,不能直接修改 Pending。

      用户程序不能直接把内核中的未决信号位图随意改成 1 或 0。

      信号的产生和递达由内核管理。


    九、动手实验:阻塞 SIGINT,观察 Pending 的变化

      理论讲了这么多,现在我们通过一个实验,把前面学到的知识全部串起来。

    9.1 实验目标

      我们希望实现:

  • 捕捉 SIGINT;
  • 阻塞 SIGINT;
  • 不断读取并打印 Pending;
  • 按下 Ctrl+C,观察 SIGINT 是否进入 Pending;
  • 等待一段时间后,解除对 SIGINT 的阻塞;
  • 观察信号是否递达。
  • 9.2 完整代码

    #include <iostream>
    #include <unistd.h>
    #include <signal.h>
    #include <cstdio>

    using namespace std;

    // 打印当前进程的未决信号集
    void PrintPending(const sigset_t &pending)
    {
    cout << "curr process[" << getpid() << "] pending: ";

    // 这里打印 31~1 号标准信号的状态
    for (int signo = 31; signo >= 1; —signo)
    {
    if (sigismember(&pending, signo))
    {
    cout << "1";
    }
    else
    {
    cout << "0";
    }
    }

    cout << endl;
    }

    // SIGINT 的处理函数
    void handler(int signo)
    {
    cout << signo << " 号信号被递达!" << endl;
    }

    int main()
    {
    // 1. 注册 SIGINT 的处理函数
    signal(SIGINT, handler);

    // 2. 创建阻塞信号集和旧信号屏蔽字
    sigset_t block_set;
    sigset_t old_set;

    sigemptyset(&block_set);
    sigemptyset(&old_set);

    // 3. 将 SIGINT 添加到 block_set
    sigaddset(&block_set, SIGINT);

    // 4. 阻塞 SIGINT,并保存原来的信号屏蔽字
    sigprocmask(SIG_BLOCK, &block_set, &old_set);

    int cnt = 15;

    while (true)
    {
    // 5. 获取当前进程的未决信号集
    sigset_t pending;
    sigpending(&pending);

    // 6. 打印 Pending
    PrintPending(pending);

    —cnt;

    // 7. 计数结束,恢复原来的信号屏蔽字
    if (cnt == 0)
    {
    cout << "解除对 SIGINT 的阻塞!" << endl;

    sigprocmask(SIG_SETMASK, &old_set, nullptr);
    }

    sleep(1);
    }

    return 0;
    }

      编译运行:

    在这里插入图片描述

    9.3 实验现象

      程序刚运行时,Pending 中没有 SIGINT:

    在这里插入图片描述

      此时按下:

    Ctrl + C

      由于我们已经阻塞了 SIGINT,进程不会立即终止,而是继续运行。

      Pending 中对应的位置变成了 1:

    在这里插入图片描述

    这说明:

      SIGINT 已经产生,但是因为被阻塞,所以暂时处于未决状态。

      等计数结束,程序执行:

    sigprocmask(SIG_SETMASK, &old_set, nullptr);

      恢复原来的信号屏蔽字。

      如果原来的屏蔽字没有阻塞 SIGINT,那么之前处于 Pending 状态的 SIGINT 就可以递达。

      于是执行:

    在这里插入图片描述

      如果使用默认处理动作,解除阻塞后进程则会按照 SIGINT 的默认动作终止。

    9.4 为什么 Ctrl+\\ 仍然可以终止程序?

      实验过程中,你可能会发现:

      虽然 Ctrl+C 无法终止程序,但是 Ctrl+\\ 仍然可以终止程序。

      这是为什么?

      因为:

    • Ctrl+C 产生 SIGINT,我们只阻塞了 SIGINT;
    • Ctrl+\\ 产生 SIGQUIT,它没有被阻塞。

      因此,SIGQUIT 仍然可以正常递达,并执行默认处理动作。

      这也说明:

      阻塞某个信号,并不意味着阻塞所有信号。


    十、常规信号为什么会丢失?

      现在我们再思考一个问题:

      如果 SIGINT 已经被阻塞,那么我们连续按下十次 Ctrl+C,Pending 会记录十次吗?

    答案是:

      不会。

      对于 Linux 的常规信号,如果某个信号在递达之前产生多次,通常只会记录一次。

    例如:

    #mermaid-svg-dDscV3OULumbebCX{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dDscV3OULumbebCX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dDscV3OULumbebCX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dDscV3OULumbebCX .error-icon{fill:#552222;}#mermaid-svg-dDscV3OULumbebCX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dDscV3OULumbebCX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dDscV3OULumbebCX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dDscV3OULumbebCX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dDscV3OULumbebCX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dDscV3OULumbebCX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dDscV3OULumbebCX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dDscV3OULumbebCX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dDscV3OULumbebCX .marker.cross{stroke:#333333;}#mermaid-svg-dDscV3OULumbebCX svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dDscV3OULumbebCX p{margin:0;}#mermaid-svg-dDscV3OULumbebCX .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-dDscV3OULumbebCX .cluster-label text{fill:#333;}#mermaid-svg-dDscV3OULumbebCX .cluster-label span{color:#333;}#mermaid-svg-dDscV3OULumbebCX .cluster-label span p{background-color:transparent;}#mermaid-svg-dDscV3OULumbebCX .label text,#mermaid-svg-dDscV3OULumbebCX span{fill:#333;color:#333;}#mermaid-svg-dDscV3OULumbebCX .node rect,#mermaid-svg-dDscV3OULumbebCX .node circle,#mermaid-svg-dDscV3OULumbebCX .node ellipse,#mermaid-svg-dDscV3OULumbebCX .node polygon,#mermaid-svg-dDscV3OULumbebCX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dDscV3OULumbebCX .rough-node .label text,#mermaid-svg-dDscV3OULumbebCX .node .label text,#mermaid-svg-dDscV3OULumbebCX .image-shape .label,#mermaid-svg-dDscV3OULumbebCX .icon-shape .label{text-anchor:middle;}#mermaid-svg-dDscV3OULumbebCX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dDscV3OULumbebCX .rough-node .label,#mermaid-svg-dDscV3OULumbebCX .node .label,#mermaid-svg-dDscV3OULumbebCX .image-shape .label,#mermaid-svg-dDscV3OULumbebCX .icon-shape .label{text-align:center;}#mermaid-svg-dDscV3OULumbebCX .node.clickable{cursor:pointer;}#mermaid-svg-dDscV3OULumbebCX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dDscV3OULumbebCX .arrowheadPath{fill:#333333;}#mermaid-svg-dDscV3OULumbebCX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dDscV3OULumbebCX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dDscV3OULumbebCX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dDscV3OULumbebCX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dDscV3OULumbebCX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dDscV3OULumbebCX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dDscV3OULumbebCX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dDscV3OULumbebCX .cluster text{fill:#333;}#mermaid-svg-dDscV3OULumbebCX .cluster span{color:#333;}#mermaid-svg-dDscV3OULumbebCX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dDscV3OULumbebCX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dDscV3OULumbebCX rect.text{fill:none;stroke-width:0;}#mermaid-svg-dDscV3OULumbebCX .icon-shape,#mermaid-svg-dDscV3OULumbebCX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dDscV3OULumbebCX .icon-shape p,#mermaid-svg-dDscV3OULumbebCX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dDscV3OULumbebCX .icon-shape .label rect,#mermaid-svg-dDscV3OULumbebCX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dDscV3OULumbebCX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dDscV3OULumbebCX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dDscV3OULumbebCX :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-dDscV3OULumbebCX .startNode>*{fill:#F8FAFC!important;stroke:#94A3B8!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-dDscV3OULumbebCX .startNode span{fill:#F8FAFC!important;stroke:#94A3B8!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-dDscV3OULumbebCX .startNode tspan{fill:#333!important;}#mermaid-svg-dDscV3OULumbebCX .statusNode>*{fill:#F3E8FF!important;stroke:#D8B4FE!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-dDscV3OULumbebCX .statusNode span{fill:#F3E8FF!important;stroke:#D8B4FE!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-dDscV3OULumbebCX .statusNode tspan{fill:#333!important;}

    第 1 次

    第 2 次

    第 3 次

    SIGINT 多次产生

    Pending[2] = 1

    Pending[2] 仍然是 1

    Pending[2] 仍然是 1

    为什么?

      因为常规信号的未决状态采用位图记录。

      一个 bit 只能表示两种状态:

    0:没有处于未决状态
    1:处于未决状态

      它并不记录这个信号究竟产生了多少次。

      因此,多个相同的常规信号在递达之前产生,通常会合并成一个。

    10.1 实时信号可以排队

      Linux 还提供了实时信号。

      与常规信号不同,实时信号支持排队。

    例如:

    #mermaid-svg-50wE1Qgjhb2BGhdA{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-50wE1Qgjhb2BGhdA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-50wE1Qgjhb2BGhdA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-50wE1Qgjhb2BGhdA .error-icon{fill:#552222;}#mermaid-svg-50wE1Qgjhb2BGhdA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-50wE1Qgjhb2BGhdA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-50wE1Qgjhb2BGhdA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-50wE1Qgjhb2BGhdA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-50wE1Qgjhb2BGhdA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-50wE1Qgjhb2BGhdA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-50wE1Qgjhb2BGhdA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-50wE1Qgjhb2BGhdA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-50wE1Qgjhb2BGhdA .marker.cross{stroke:#333333;}#mermaid-svg-50wE1Qgjhb2BGhdA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-50wE1Qgjhb2BGhdA p{margin:0;}#mermaid-svg-50wE1Qgjhb2BGhdA .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-50wE1Qgjhb2BGhdA .cluster-label text{fill:#333;}#mermaid-svg-50wE1Qgjhb2BGhdA .cluster-label span{color:#333;}#mermaid-svg-50wE1Qgjhb2BGhdA .cluster-label span p{background-color:transparent;}#mermaid-svg-50wE1Qgjhb2BGhdA .label text,#mermaid-svg-50wE1Qgjhb2BGhdA span{fill:#333;color:#333;}#mermaid-svg-50wE1Qgjhb2BGhdA .node rect,#mermaid-svg-50wE1Qgjhb2BGhdA .node circle,#mermaid-svg-50wE1Qgjhb2BGhdA .node ellipse,#mermaid-svg-50wE1Qgjhb2BGhdA .node polygon,#mermaid-svg-50wE1Qgjhb2BGhdA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-50wE1Qgjhb2BGhdA .rough-node .label text,#mermaid-svg-50wE1Qgjhb2BGhdA .node .label text,#mermaid-svg-50wE1Qgjhb2BGhdA .image-shape .label,#mermaid-svg-50wE1Qgjhb2BGhdA .icon-shape .label{text-anchor:middle;}#mermaid-svg-50wE1Qgjhb2BGhdA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-50wE1Qgjhb2BGhdA .rough-node .label,#mermaid-svg-50wE1Qgjhb2BGhdA .node .label,#mermaid-svg-50wE1Qgjhb2BGhdA .image-shape .label,#mermaid-svg-50wE1Qgjhb2BGhdA .icon-shape .label{text-align:center;}#mermaid-svg-50wE1Qgjhb2BGhdA .node.clickable{cursor:pointer;}#mermaid-svg-50wE1Qgjhb2BGhdA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-50wE1Qgjhb2BGhdA .arrowheadPath{fill:#333333;}#mermaid-svg-50wE1Qgjhb2BGhdA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-50wE1Qgjhb2BGhdA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-50wE1Qgjhb2BGhdA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-50wE1Qgjhb2BGhdA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-50wE1Qgjhb2BGhdA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-50wE1Qgjhb2BGhdA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-50wE1Qgjhb2BGhdA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-50wE1Qgjhb2BGhdA .cluster text{fill:#333;}#mermaid-svg-50wE1Qgjhb2BGhdA .cluster span{color:#333;}#mermaid-svg-50wE1Qgjhb2BGhdA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-50wE1Qgjhb2BGhdA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-50wE1Qgjhb2BGhdA rect.text{fill:none;stroke-width:0;}#mermaid-svg-50wE1Qgjhb2BGhdA .icon-shape,#mermaid-svg-50wE1Qgjhb2BGhdA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-50wE1Qgjhb2BGhdA .icon-shape p,#mermaid-svg-50wE1Qgjhb2BGhdA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-50wE1Qgjhb2BGhdA .icon-shape .label rect,#mermaid-svg-50wE1Qgjhb2BGhdA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-50wE1Qgjhb2BGhdA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-50wE1Qgjhb2BGhdA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-50wE1Qgjhb2BGhdA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-50wE1Qgjhb2BGhdA .actionNode>*{fill:#E0F2FE!important;stroke:#7DD3FC!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-50wE1Qgjhb2BGhdA .actionNode span{fill:#E0F2FE!important;stroke:#7DD3FC!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-50wE1Qgjhb2BGhdA .actionNode tspan{fill:#333!important;}#mermaid-svg-50wE1Qgjhb2BGhdA .queueNode>*{fill:#FFE4E6!important;stroke:#FDA4AF!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-50wE1Qgjhb2BGhdA .queueNode span{fill:#FFE4E6!important;stroke:#FDA4AF!important;stroke-width:2px!important;color:#333!important;rx:6!important;ry:6!important;}#mermaid-svg-50wE1Qgjhb2BGhdA .queueNode tspan{fill:#333!important;}

    实时信号产生第 1 次

    进入队列 (节点 1)

    实时信号产生第 2 次

    进入队列 (节点 2)

    实时信号产生第 3 次

    进入队列 (节点 3)

      等到可以递达时,再按照队列顺序处理。

    因此:

    信号类型多次产生时的处理
    常规信号 通常合并,不保证逐次递达
    实时信号 支持排队,可以逐次递达

    十一、Core Dump:进程异常终止时留下的调试信息

      在上一篇中,我们已经介绍过 Core Dump。

      这里再结合信号递达机制简单回顾一下。

      当某些信号递达时,如果它们的默认处理动作是 Core,进程异常终止时就可能生成 core 文件。

    例如:

    • SIGQUIT
    • SIGFPE
    • SIGSEGV

      Core Dump 可以理解为:

      进程异常终止时,将用于调试的进程状态信息保存到文件中,方便开发者事后排查问题。

    11.1 为什么没有生成 core 文件?

    可以先查看:

    ulimit -a

    其中:

    core file size (blocks, -c) 0

      表示当前 shell 对应的 core 文件大小限制为 0。

      也就是说,默认不允许生成 core 文件。

      可以使用:

    ulimit -c unlimited

      允许生成不受该大小限制约束的 core 文件。

    11.2 使用 GDB 调试 core 文件

      如果程序发生异常并生成了 core 文件,可以使用:

    gdb ./testsig core

      进入 GDB 后:

    bt

      查看函数调用栈,帮助定位程序异常的位置。

    11.3 waitpid 如何判断是否产生 Core?

      父进程可以通过 waitpid() 获取子进程的退出状态。

      判断时不要自己手动拆位,直接使用系统提供的宏:

    宏作用
    WIFEXITED(status) 判断是否正常退出
    WEXITSTATUS(status) 获取正常退出时的退出码
    WIFSIGNALED(status) 判断是否被信号终止
    WTERMSIG(status) 获取导致终止的信号编号
    WCOREDUMP(status) 判断是否产生了 core dump

      其中,WCOREDUMP() 是 Linux 等系统提供的扩展接口,使用时需要注意平台兼容性。

    记住:

      waitpid() 返回的 status 是编码后的状态信息,不是简单的退出码。


    十二、本篇总结

      学到这里,我们已经把 Linux 信号的保存机制串起来了。

    12.1 三个核心信息

    名称作用记录内容
    Block 阻塞信号集 哪些信号被阻塞
    Pending 未决信号集 哪些信号已经产生但尚未递达
    Handler 处理动作 信号递达时应该执行什么动作

    12.2 常用函数

    函数作用
    sigemptyset 初始化为空信号集
    sigfillset 初始化为包含系统支持信号的信号集
    sigaddset 添加信号
    sigdelset 删除信号
    sigismember 判断信号是否存在
    sigprocmask 读取或修改信号屏蔽字
    sigpending 读取未决信号集

    12.3 信号产生到递达的完整流程

    在这里插入图片描述

    12.4 面试易错点

    问题正确理解
    未决和阻塞是一回事吗? 不是。Pending 表示信号已经产生但尚未递达,Block 表示信号是否被阻塞
    阻塞和忽略一样吗? 不一样。阻塞发生在递达之前,忽略是递达时的处理动作
    sigprocmask 修改哪个集合? 修改 Block,也就是信号屏蔽字
    sigpending 修改哪个集合? 它只能读取 Pending,不能直接修改
    sigemptyset 有什么作用? 初始化一个空信号集
    SIG_BLOCK 和 SIG_SETMASK 有什么区别? 前者添加阻塞信号,后者整体替换屏蔽字
    常规信号会排队吗? Linux 中常规信号在递达前多次产生通常只记录一次
    实时信号会排队吗? 支持排队
    解除阻塞后会发生什么? 对应的未决信号可能立即递达
    SIGKILL 可以被阻塞吗? 不可以

    结语:

      今天的内容到这里就结束了,希望你能有所收获~

    干货整理到手抖,觉得有用的话,赏个三连回回血?__(:ᗤ」ㄥ)_ _

    赞(0)
    未经允许不得转载:171主机测评 » 【Linux 系统篇(三十七)】信号(三):信号是如何保存的?
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址