【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信号设置为忽略,这样一来,父进程收到子进程退出的信号后就不会再拿一部分时间或资源来处理子进程了!


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


相关文章
|
17天前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
1月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
78 34
|
13天前
|
Linux
Linux:守护进程(进程组、会话和守护进程)
守护进程在 Linux 系统中扮演着重要角色,通过后台执行关键任务和服务,确保系统的稳定运行。理解进程组和会话的概念,是正确创建和管理守护进程的基础。使用现代的 `systemd` 或传统的 `init.d` 方法,可以有效地管理守护进程,提升系统的可靠性和可维护性。希望本文能帮助读者深入理解并掌握 Linux 守护进程的相关知识。
28 7
|
12天前
|
Linux Shell
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
32 5
|
12天前
|
Linux 应用服务中间件 nginx
Linux 进程管理基础
Linux 进程是操作系统中运行程序的实例,彼此隔离以确保安全性和稳定性。常用命令查看和管理进程:`ps` 显示当前终端会话相关进程;`ps aux` 和 `ps -ef` 显示所有进程信息;`ps -u username` 查看特定用户进程;`ps -e | grep &lt;进程名&gt;` 查找特定进程;`ps -p &lt;PID&gt;` 查看指定 PID 的进程详情。终止进程可用 `kill &lt;PID&gt;` 或 `pkill &lt;进程名&gt;`,强制终止加 `-9` 选项。
20 3
|
1月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
49 17
|
1月前
|
消息中间件 Linux C++
c++ linux通过实现独立进程之间的通信和传递字符串 demo
的进程间通信机制,适用于父子进程之间的数据传输。希望本文能帮助您更好地理解和应用Linux管道,提升开发效率。 在实际开发中,除了管道,还可以根据具体需求选择消息队列、共享内存、套接字等其他进程间通信方
68 16
|
8月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
8月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
243 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
7月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。