深入理解Linux中信号处理过程

🔥🔥 欢迎来到小林的博客!!
      🛰️博客主页:✈️林 子
      🛰️博客专栏:✈️ Linux
      🛰️社区 :✈️ 进步学堂
      🛰️欢迎关注:👍点赞🙌收藏✍️留言

目录

信号阻塞

信号的常见概念

  • 实际执行信号处理动作成为递达。
  • 信号从产生到递达之间的状态,成为未决。
  • 进程可以选择阻塞某个信号。
  • 被阻塞的信号产生时会保持在未决状态,直到进程接触对此信号的阻塞,才执行递达动作。
  • 阻塞和忽略是不同的,只要信号被阻塞,就不会被递达,而忽略递达的一种处理方式。

递达的三种处理方式

1.默认

一般默认的处理方式就是终止。

2.忽略

不对该信号做处理。

3.自定义

类似handler函数,自己指定函数处理信号。

默认和忽略是什么区别? 默认是一种默认的处理方式,和忽略的处理方式是直接不处理。

在内核中的表示

信号是由操作系统发送给进程的,而信号不一定会被立即处理,那么这就意味着进程必须有保存信号的能力!由此我们可以推断出,信号一定是以一个数据结构存储在进程控制块(PCB)这个结构体当中!而我们 1-31 个信号可以想象成对应的 1 - 31 个比特位。比特为1 则说明收到该信号,为0则说明没有收到该信号。

而在内核中的表示方式为:

block 是阻塞表,对应的数组下标是 1 - 31 个信号。 pending 是递达表,为1则说明被递达。 为0则说明没有被递达。handler 是一个函数指针数组,每个元素是下标对应的信号处理的函数指针。

如何阻塞信号

信号阻塞是一种什么场景? 简单来说! 被阻塞的信号不会被递达! 递达就是对信号的处理!再通俗一点,阻塞就是阻止对信号的处理,就是暂时先不处理该信号! 等解除阻塞后再处理该信号。

如何阻塞信号?

我们可以利用int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 这个函数对指定信号进行阻塞, 而set是输入型数据,oset是输出型数据。

sigset_t

sigset_t 是一个位图,这个位图不能让用户直接操作,而是通过系统调用接口来修改这个位图。 这个位图 block 和pending都可以使用,每个bit位都是一个未决标志。而信号并不需要记录收到多少次,只需要记录收到或者没收到,对应0和1 。这个位图被称为信号集 , 在阻塞信号集中(block),这个标志位表示是否被阻塞。在未决信号集(pending)中表示是否处于未决。

信号集的操作函数

c 复制代码
#include <<signal.h>>
int sigemptyset(sigset_t *set);   //该信号集的所有bit位置为0
int sigfillset(sigset_t *set);   //初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。
int sigaddset (sigset_t *set, int signo); //往位图添加信号
int sigdelset(sigset_t *set, int signo);  //往位图删除信号
int sigismember(const sigset_t *set, int signo); //信号集中是否包含某种信号

这四个函数都是成功返回0,出错返回-1。sigismember 包含则返回1,不包含则返回0,出错返回-1。

**sigprocmask 阻塞函数 **

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

调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。调用成功返回0 ,调用失败返回-1

set是要修改的位图,oset是保存旧位图。

int how参数:

SIG_BLOCK : set 包含了我们希望添加到信号屏蔽的信号,相当于 mask = mask | set

SIG_UNBLOCK : set 包含了我们希望解除阻塞的信号,相当于 mask = mask & ~set

SIG_SETMASK : 设置当前屏蔽字为set指向的值,mask = set

sigpending 读取未决信号集

int sigpending(sigset_t *set);

读取当前进程的未决信号集,调用成功返回0,调用失败返回-1。

了解了以上操作函数之后,我们接下来可以写个代码做个小实验。

c 复制代码
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>

void show(sigset_t* set)
{
  int i = 0;
  for(i  =1;  i <= 31 ; i++)
  {
    if(sigismember(set,i))
      printf("1");
    else printf("0");
  }
  printf("\n");
}

int main()
{

  sigset_t set,p; //定义信号集

  sigemptyset(&set); //初始化信号集
  sigemptyset(&p); 

  sigaddset(&set,2); //往set信号集添加一个2号信号
  sigprocmask(SIG_SETMASK,&set,NULL) ; //设置屏蔽信号集为set
  while(1)
  {
    sigpending(&p); //获取信号集
    show(&p); //打印信号集
    sleep(1);
  }
  return 0;
}

然后我们执行程序后 按 ctrl + c 发送2号信号,看看会发生什么。

我们会发现两个现象。

1.第二个比特位由 0 置 1 ,证明该信号被保存进PCB里的位图结构

2. 发送2号信号后,程序并没有终止。

所以,我们可以知道,2号信号被屏蔽了。 只有解除屏蔽时才会处理2号信号,否则信号将一直处于未决状态。

而操作系统给进程发送信号,本质就是往进程控制块(PCB)内部的位图结构的对应位置由0置1 , 阻塞信号也是相同道理, 而handler 表 存放的是处理信号的函数的地址。

信号处理全过程

要知道信号处理的全过程,我们要清楚2个概念。用户态和内核态。

用户态就是用户代码和数据被访问或执行的时候,此时所处的状态就是用户态。

OS的代码和数据被执行的时候,计算机所处的状态就叫做内核态。

理性认识

当我们的进程在执行系统调用时,会转换为内核态,因为用户态不能执行OS的代码和数据!因为用户没有权限,因为操作系统不相信任何人!

而实际上当一个进程执行的时间片到了之后,操作系统会进入内核态。把该进程下掉,然后把新执行的进程放上来,再转换为用户态执行该进程。 而CPU为了分清楚当前是内核态还是用户态,会有一个CR寄存器来保存当前的用户状态。

而我们还要知道,用户态使用的是用户级页表,每个用户级页表都是独立的,因为进程具有独立性!

而操作系统也有一份系统级页表,系统级页表被所有进程所共享!!这就是为什么在不同的进程在使用系统调用时,都能找到同一份操作系统提供的代码和数据!本质就是因为所有进程共享系统级页表!!

有了以上前置知识之后,我们就可以来剖析信号处理的全过程。

首先,进程在CPU调度时会从用户态陷入内核态 -> 陷入内核态先不着急返回,先去看是否收到信号,如果收到信号,再看该信号是否被阻塞,如果没被阻塞,那么执行对应的handler操作,如果是默认,那么直接释放这个进程,如果是忽视,那么直接返回到用户态,如果是自定义处理,那么从内核态返回到用户态,执行进程中的handler处理信号函数 -> 再陷入内核态,执行sys_sigreturn()函数 -> 返回用户态。

我们可以用一个无穷大的符号来总结。

为什么不在内核态处理信号,反而回到用户态处理信号? 原因很简单! 因为操作系统不相信任何人! 如果你在handler函数进行一些非法操作,例如 rm -r * 。那么这样的破坏是非常大的,所以要转换到用户态来处理信号。如果处理方式是忽视,那么直接返回用户态。如果是默认,那么释放掉进程。

相关推荐
qq_3129201122 分钟前
Nginx限流与防爬虫与安全配置方案
运维·爬虫·nginx·安全
GanGuaGua29 分钟前
Linux系统:线程的互斥和安全
linux·运维·服务器·c语言·c++·安全
lsnm38 分钟前
【LINUX网络】IP——网络层
linux·服务器·网络·c++·网络协议·tcp/ip
全糖去冰吃不了苦38 分钟前
ELK 集群部署实战
运维·jenkins
不掰手腕1 小时前
在UnionTech OS Server 20 (统信UOS服务器版) 上离线安装PostgreSQL (pgsql) 数据库
linux·数据库·postgresql
Lynnxiaowen1 小时前
今天继续昨天的正则表达式进行学习
linux·运维·学习·正则表达式·云计算·bash
努力学习的小廉1 小时前
深入了解linux系统—— POSIX信号量
linux·运维·服务器
刘一说2 小时前
CentOS部署ELK Stack完整指南
linux·elk·centos
从零开始的ops生活2 小时前
【Day 50 】Linux-nginx反向代理与负载均衡
linux·nginx
IT成长日记2 小时前
【Linux基础】Linux系统配置IP详解:从入门到精通
linux·运维·tcp/ip·ip地址配置