【linux进程信号(二)】信号的保存,处理以及捕捉

简介: 【linux进程信号(二)】信号的保存,处理以及捕捉

1. 前言

上一篇文章了解到信号产生的四种方式,

但是信号产生后,然后呢?需要对信号

进行保存,最后对信号进行处理

如果你没有阅读过前一篇文章,或者不知道信号的默认处理方式,请先阅读这篇文章: 信号的基本概念

本章重点:

本篇文章着重讲解信号保存的方式以及
周边概率,信号阻塞,信号递达.理解
信号处理的默认方式,如何修改默认方法
最后会讲解进程是如何捕捉信号的?


2. 信号阻塞,信号递到和信号忽略

在讲解进程是如何保存信号之前,要

先了解下面几个概念:

  1. 信号递达:

实际执行处理信号的动作叫做信号的递达

  1. 信号的未决状态:

在信号产生到信号递达之间的状态叫做信号的未决状态

  1. 信号阻塞:

一旦一个信号被设置了阻塞之后,那么此进程就不会收到此信号

  1. 信号忽略:

一旦一个信号被设置为忽略,那么当这个信号来临后,进程不会对此信号做处理

  1. 阻塞和忽略的区别:

一个信号被阻塞后,它就不会递达,而忽略是指信号递达后,执行的动作是什么都不做


3. 进程是怎样保存信号的?

在进程的PCB中,存在三张和信号相关的表

更准确的讲,前两个结构是位图,最后一个是表(数组).

  1. block位图:

这个位图代表,在这个进程中,有哪些信号是被阻塞了的?位图的第一个元素为0,代表1号信号没有被阻塞,第n个位置为1,代表n号信号被阻塞了,也就是说0/1代表某个信号是否被阻塞,这个位图又被称为信号屏蔽字

  1. pending位图:

这个位图是进程存储信号的结构,位图中的第n个位置为0/1代表是否收到了n号信号,如若收到,会在后续进行处理,这个位图又被称为信号集

  1. handler数组:

首先,这个数组是一个函数指针数组,里面存储的是函数的地址,数组的n号元素存储的函数地址代表收到n号信号之后,要去处理信号时,需要调用的函数.在上图中,SIG_DFL宏代表这个函数就是此信号的默认处理函数,SIG_IGN宏代表收到这个信号后,直接忽略此信号,当然我们也可以自己写一个函数来充当信号的处理方法,这个后面会讲


4. 信号集操作函数

首先我想隆重介绍的是signal函数:

第一个参数代表要设置几号信号

第二个参数代表,信号到来需要调用哪个函数

下面可以进行一个简单的编码验证:

void mycatch(int signum)
{
    cout<<"进程捕捉到了一个信号,正在自定义处理中... "<<signum<<"pid: "<<getpid()<<endl;
}
int main()
{
    signal(SIGINT,mycatch);//既可以填写定义的宏,也可以直接写数字
    while(1)
    {
       cout<<"我是一个进程,我正在运行...pid: "<<getpid()<<endl;
        sleep(1);
    }
    return 0;
}

下面的内容能掌握的最好:

信号集操作函数概览:

读取或更改信号屏蔽字:

下面是样例代码,有兴趣可以看看:

void showpending(sigset_t& tmp)
  {
      for(int i=1;i<=31;i++)
      {
          if(sigismember(&tmp,i))
              cout<<1;
          else
              cout<<0;
      }
      cout<<endl;
  }
  void blocksig(int sig)//对指定信号对屏蔽
  {
      sigset_t bset;
      sigemptyset(&bset);
      sigaddset(&bset,sig);
      int n = sigprocmask(SIG_BLOCK,&bset,NULL);//只对sig号信号屏蔽
      assert(n==0);
      cout<<"block success!"<<endl;
  }
  int main()
  {
      cout<<getpid()<<endl;
      for(int i=1;i<=31;i++)//将所有信号都屏蔽掉
          blocksig(i);
      sigset_t pending;    
      while(1)
      {
          sigpending(&pending);    
          showpending(pending);
          sleep(1);
      }
      return 0;
  }

5. 进程是如何捕捉信号的?

先说结论:

从内核态返回用户态时,会进行信号的检测和处理

在此之前,大家肯定会有疑问:什么是内核态?什么是用户态?为什么这两个状态会相互切换?下面就来解答这些问题:

用户态指的是程序在执行用户写的代码时,会使用用户态的身份来执行代码,那么什么时候会进入内核态呢?答案是代码中存在系统调用,代码出现异常等情况,操作系统会将身份切换为内核态来执行代码.所以为什么要切换状态呢?直接用用户态执行全部代码难道不行吗?答案一定是否认的,因为群众中有坏人,在执行系统调用时,由于是很底层的代码或函数,所以操作系统是不信任用户的,切换为内核态的一大原因是为了安全性.另一方面,使用内核态执行代码时的优先级非常高.那么操作系统是怎样在两个状态中做切换的呢?答案很简单,CPU有两套寄存器,一套是可见的,一套是不可见的,而在不可见的这一套寄存器中,有一个寄存器叫CR3,它表示当前CPU的执行权限,数值为1代表内核态,数值为3代表用户态

了解完前景知识后,我们就可以得出一些结论:

  1. 当程序执行系统调用时会进入到内核态
  2. 当执行完系统调用后,会回到用户态
  3. 在这期间会进行信号的检测和处理
  4. 如若此时检测到有信号到来,那么会把代码直接跳转到信号处理的函数处
  5. 当信号处理函数返回时还会执行特殊的系统调用,再回到内核态

下面可以用一张图来代表整个过程:

把图片简化一下,就得到了一个无穷大的图像:


6. 总结

大家下来可以去试试将所有的信号都设置为阻塞,或者忽略,会发生什么?一旦这样做,进程运行起来后使用CTRL+c或者CTRL+/都不能终止进程,并且使用kill命令也无法杀掉进程.即使如此,即使所有信号都被屏蔽或忽略,但第九号信号是无法被屏蔽和忽略的,所以任何情况下都可以使用kill -9 pid来杀掉一个进程

信号这一章节是我们学习进程的最后一节,由于信号与进程的紧密关系,所以学习信号至关重要,除此之外,当子进程退出时也会向父进程发生SIG_CHILD信号,假设父进程并不想关心子进程的退出结果,只想执行自己的代码,那么我们可以将SIGCHLD信号设置为忽略,这样一来,父进程收到子进程退出的信号后就不会再拿一部分时间或资源来处理子进程了!


🔎 下期预告:线程的基本概念 🔍


相关文章
|
21天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
47 1
|
9天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
63 13
|
16天前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
24天前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
29天前
|
缓存 算法 Linux
Linux内核的心脏:深入理解进程调度器
本文探讨了Linux操作系统中至关重要的组成部分——进程调度器。通过分析其工作原理、调度算法以及在不同场景下的表现,揭示它是如何高效管理CPU资源,确保系统响应性和公平性的。本文旨在为读者提供一个清晰的视图,了解在多任务环境下,Linux是如何智能地分配处理器时间给各个进程的。
|
1月前
|
网络协议 Linux 虚拟化
如何在 Linux 系统中查看进程的详细信息?
如何在 Linux 系统中查看进程的详细信息?
94 1
|
1月前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
5月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
5月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
192 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
4月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。