【Linux】 信号的保存 | 捕捉

这篇具有很好参考价值的文章主要介绍了【Linux】 信号的保存 | 捕捉。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

对于信号,主要涉及到信号的产生、保存和捕获,之前谈到了信号的产生,这里主要介绍信号产生后如何进行保存和捕捉处理的原理。

一、信号的保存

1.阻塞信号

相关概念

  • 实际执行处理信号的动作称为信号递达Delivery
  • 信号从产生到递达的过程称为信号未决Pending
  • 进程可以阻塞、忽略某个信号。
  • 被阻塞就只有产生和未决,忽略是在递达后的处理。

2.内核结构

之前讲过OS发送信号给进行,会在进程PCB表上的位图标记 0-》1,并回调函数指针的方法。

实际上,内核会位PCB的信号维护三张表Block(阻塞表)、Pending(未决)、handler(函数指针数组)

【Linux】 信号的保存 | 捕捉,linux,linux,运维,服务器

说明:

  • Pending表就是标记我们之前谈到的位图,0/1标记某一位是否收到信号。
  • Block表也是位图 ,代表某一信号是否被阻塞,对特定的信号进行屏蔽。
  • Handler表是函数指针数组,内容 :0 表示默认 1是忽略,还有用户自定回调函数。

描述这一过程:
在信号没有创建之前,Block表中的某一位先会被设置位0和1,标记是否被阻塞。

信号产生时,会在进程控制块的Pengding表中将对应信号位的0-》1。

再校验Block表,如果表上的比特位是1 ,代表被阻塞,将不会递达。直到阻塞被解除。

总结:

进程task_struct中会维护三张表。三张表共同维护信号的识别。

信号的阻塞,不会影响产生。

一个信号没有被递达,并且接收到多次,pending表会默认最后一次发送的信号。


信号的捕捉

信号在什么时候被处理?

进程从内核态到用户态的时候,进行信号的检测和处理

用户态是一种受控的状态,能访问的资源是有限的。

内核态是OS的一种工作状态,能访问到大部分资源。

系统调用必定发生身份从用户态到内核态的转变,因为我们无法通过用户态进行系统调用,

系统调用是比较费时间的,要避免频繁的系统调用。

用户态和内核态

如何对用户态和内核态进行区分?

在CPU上有一个CR3寄存器,是一个2比特位的。00 01 10 11

1表示内核,3表示用户态

进程如何调用系统调用接口?

用户态只能访问自己的【0,3】GB的内存空间。

内核态能让用户以OS的身份访问【3,4】GB。

进程需要被加载到内存中,OS需要维护进程PCB。实际上我们平时说的页表是【0,3】GB的用户级别页表。每一份进程都需要维护一张

【3,4】GB有对应的内核级页表。因为内核级的内容不会被用户身份访问,所以只需要维护一张内核级页表,这个页表将给CS寄存器保存。

故如果进程需要调用系统调用,就像我们平常调用库函数一样,就是在进程地址空间中跳跃。

先在CR3寄存器中切换身份,然后通过寄存器CS找到内核级页表,就能找到对应的内核内容。

【Linux】 信号的保存 | 捕捉,linux,linux,运维,服务器


内核如何实现捕捉

进程的信号在合适的时候被处理,从内核转到用户级,先检测再处理。

【Linux】 信号的保存 | 捕捉,linux,linux,运维,服务器

描述这一过程

  • CPU执行用户代码时,会先以用户态执行,遇到系统调用接口时,会切换身份调用系统调用。执行完成后到进程的PCB内查看信号列表,如果pending表全为0,或者pending为 1 block为 1阻塞也直接返回。
  • 如果pending为1,block为0,且handler存在自定义的方法,则会将内核态切换为用户态,调用用户的方法。(这一切换是为了防止内内容被用户破坏)。执行完毕后会现在内核态检测信号的位置,通过特定系统调用返回。

注意:
在信号调用handler方法时,就会将pending表上的1-》0

如果处理完毕后,pending表上还有信号没被处理,则会执行handler方法。

抽象图帮助记忆

【Linux】 信号的保存 | 捕捉,linux,linux,运维,服务器

一共会经过四次身份切换,只有在第一次身份切换时,才进行信号的检测与处理!


信号操作函数

sigset_t

位图类型

它在Linux下的定义

#define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
	unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;
 
typedef __sigset_t sigset_t;
  • 对于panding表,0表示没接收到信号,1表示接收到信号
  • 对于block表,0表示信号没有被阻塞,1表示信号被阻塞

sigset_t函数

因为sigset_t 是位图,它的每一位比特位可以表示pending表和block表的内容,因此我们可以通过逻辑关系的方法修改比特位的内容,但是这样过于繁琐。就由下面这些函数操作位图。

#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);
  • sigemptyset函数:将位图表设置为全0,(清0)
  • sigfillset函数:将位图上的所有比特位置为有效信号(置位)
  • sigaddset函数:将signo的信号置为有效信号
  • sigdelset函数:将signo信号置零
  • sigisemember函数:判断signo信号是否在位图中。

注意:
sigset_t 创建后,需要初始化(置位/清零)

我们操作的表与进程中的表没有关系。需要继续调用系统调用写入。


sigprocmask

sigprocmask函数可以用于读取或更改进程的信号屏蔽字(阻塞信号集),该函数原型如下:

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

一般而言,阻塞和未决表在进行修改时,都会保存上一张表

参数说明:

  • oset不为空时,读取当前进程的阻塞表输出到oset表中
  • set不为空时,依旧how和set更改当前的阻塞表
  • set和oset都不为空时,会保存旧表更改阻塞表

how参函数

想要如何操作信号屏蔽字,此参数有三个可选值:

  • 1.SIG_BLOCK:就是把对应信号的bit位改为1,即就是阻塞该信号
  • 2.SIG_UNBLOCK:就是把对应信号的bit位改为0,即就是使该信号不阻塞
  • 3.SIG_SETMASK:设置当前信号屏蔽字为set所指向的值

返回值:

  • 调用成功返回0,失败返回-1

sigpending函数

       #include <signal.h>

       int sigpending(sigset_t *set);

操作pending表,读取当前进程的pending表,通过set返回

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


下面是举例运用

使用sigprocmask函数阻塞2号信号和40号信号

要求:阻塞2号信号和40号信号, 分别给进程发送5次2号信号和5次40号信号,观察结果

 1 #include <iostream>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 #include <signal.h>
  5 
  6 
  7 void pendingPrin(sigset_t* s)
  8 {
  9   for(int i=31;i>0;i--)
 10   {
 11     if(sigismember(s,i))
 12     {
 13       std:: cout<<1;
 14     }
 15     else std::cout<<0;
 16   }
 17   std::cout<<std::endl;
 18   std::cout<<"----------------------------------------------"<<std::endl;
 19 }
 20 
 21 
 22 void handler(int signo)
 23 {
 24   std::cout<<"get a signo: "<<signo<<std::endl;
 25 }
 26 
 27 int main()
 28 {
 29   std::cout<<"my pid is: "<<getpid()<<std::endl;
 30   //自定义处理2信号和40信号
 31   signal(2,handler);
 32   signal(40,handler);
 33   sigset_t set,s;
 34   sigemptyset(&set);
 35   sigaddset(&set,2);
 36   sigaddset(&set,40);
 37   sigprocmask(SIG_BLOCK,&set,nullptr);
 38 
 39   while(true)
 40   {
 41     sigpending(&s);
 42     std::cout<<"pending: ";
 43     pendingPrin(&s);
 44     std::cout<<"  block: ";
 45     pendingPrin(&set);
 46     sleep(3);
 47   }
 48   return 0;
 49 }                                                                                                                 
~

【Linux】 信号的保存 | 捕捉,linux,linux,运维,服务器


sigaction

捕捉信号,除了之前谈到的signal之外,sigaction对特定信号捕捉。

#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

 参数说明:

  • signum:要捕捉的信号编号
  • act:不为空,则为用户自定行为。
  • oldact:导出原信号的处理行为。

act和oldact都是sigacgtion类型的结构体,定义如下

struct sigaction {
	void(*sa_handler)(int);
	void(*sa_sigaction)(int, siginfo_t *, void *);
	sigset_t   sa_mask;
	int        sa_flags;
	void(*sa_restorer)(void);
};
  • 第一个参数:sa_handler

是一个函数指针 SIG_IGN是,忽略处理, SIG_DEF是默认处理,handler自定义处理。

  • 第二个参数:sa_sigaction

实时信号的处理函数。

  • 第三个参数是:sa_mask

是阻塞信号集,就和之前谈到的阻塞位图一致‘

  • 选项sa_flags

通常设为0

举例使用

  1 #include<iostream>
  2 #include <signal.h>
  3 #include <sys/types.h>
  4 #include <unistd.h>
  5 
  6 void handler(int signo)
  7 {
  8   std::cout<<"get a signo: "<<signo<<std::endl;
  9 }
 10 
 11 
 12 int main()
 13 {
 14   std::cout<<"get a pid"<<getpid()<<std::endl;
 15   struct sigaction ac,oac;
 16   ac.sa_handler=handler;
 17   ac.sa_flags=0;
 18   sigemptyset(&ac.sa_mask);
 19   sigaction(2,&ac,&oac);
 20   while(true)
 21   {
 22     std::cout<<"main running ..."<<std::endl;
 23     sleep(2);                                                                                                     
 24   }
 25   return 0;
 26 }
~

【Linux】 信号的保存 | 捕捉,linux,linux,运维,服务器


相关知识

信号的主体部分已经介绍完毕,下面还有几个相关知识点:

volatile关键字

保持内存的可见性

程序提高优化级别(例如debug到relase的转变)会使当前只读变量放进寄存器,而如何后续的变量由信号触发变化,信号变化handler是在内存中,这时候就会对一个变量形成俩份,导致寄存器中保持的不受改变。

volatile关键字声明在类型前,告诉编译器不要做过度的优化,保持内存的可见性。


SIGCHLD信号

看上去不那么实用的信号

为了避免出现僵尸进程,子进程在结束后,父进程需要等待,回收资源。等待方式可以是阻塞等待,也可以是轮询等待。但是这俩种方式都有延迟。

GIGCHILD信号,在子进程结束后,立马会去父进程发送信号,让父进程捕捉信号。

但是这个方法也有缺点,假设有多个子进程同时发送信号,但是父进程,没来得急回收,就会导致信号被阻塞,实际最后子进程不能完全被父进程杀掉。

那就必须调用自己的方法,handler函数不断的调用waitpid。

实际上

父进程调用signal或sigaction函数将SIGCHLD信号的处理动作设置为SIG_IGN,子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。

系统默认的忽略动作和用户用signal或sigaction函数自定义的忽略通常是没有区别的,但这是一个特列。此方法对于Linux可用,但不保证在其他UNIX系统上都可用。故而SIGCHLD是比较不实用的信号。文章来源地址https://www.toymoban.com/news/detail-833060.html


到了这里,关于【Linux】 信号的保存 | 捕捉的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 运维 | 查看 Linux 服务器 IP 地址

    大多数在操作 Linux 系统时,我们经常需要知道服务器的 IP 比便于后续的一系列操作,这时候有快速查看主机 IP 的命令行操作,能够有效的帮助我们 本章节主要记录一些常用查看服务器 IP 的命令,希望对大家有所帮助。 查看 Linux 服务器的 IP 地址的命令大体上有以下几种。

    2024年04月27日
    浏览(81)
  • linux服务器,nginx日志切割保存

    我们都知道,默认情况下,nginx的项目log是一直被累计写入的,随着时间越久,那么这个文件就会越大,这个时候如果我们要去做一些查找和排查就会比较困难,因为日志文件太大,操作起来比较费劲。 因此我们为了规避这个问题,提出日志切割的方案。 那日志切割的原理是

    2024年02月06日
    浏览(87)
  • 【运维】Linux 跨服务器复制文件文件夹

    如果是云服务 建议用内网ip scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的。可能会稍微影响一下速度。当你服务器硬盘变为只读 read only system时,用scp可以帮你把文件移出来

    2024年02月08日
    浏览(74)
  • 【Linux 服务器运维】定时任务 crontab 详解 | 文末送书

    本文思维导图概述的主要内容: 1.1 什么是 crontab Crontab 是一个在 Unix 和 Linux 操作系统上 用于定时执行任务 的工具。它允许用户创建和管理计划任务,以便在特定的时间间隔或时间点自动运行命令或脚本。Crontab 是 cron table 的缩写, cron 指的是 Unix 系统中的一个后台进程,它

    2024年02月08日
    浏览(92)
  • 【Linux运维】shell脚本检查服务器内存和CPU利用率

    在管理服务器时候写了一个 shell脚本,在服务上实现每天凌晨3点查系统的指定文件夹下的容量大小,如果超过10G就要删除3天前的内容,还要时刻查询内存和cpu利用率,如果超过80%就要提示用户出现过载 将以上代码保存为一个.sh文件,然后通过crontab在每天凌晨3点运行即可:

    2024年02月09日
    浏览(67)
  • Linux服务器常见运维性能测试(1)综合跑分unixbench、superbench

    最近需要测试一批服务器的相关硬件性能,以及在常规环境下的硬件运行稳定情况,需要持续拷机测试稳定性。所以找了一些测试用例。本次测试包括在服务器的高低温下性能记录及压力测试,高低电压下性能记录及压力测试,常规环境下CPU满载稳定运行的功率记录。 这个系

    2024年02月04日
    浏览(82)
  • Linux本地部署1Panel服务器运维管理面板并实现公网访问

    1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等 下面我们介绍在Linux 本地安装1Panel 并结合cpolar 内网穿透工具实现远程访问1Panel 管理界面 执行如下命令一键安装 1Panel: 安

    2024年02月04日
    浏览(98)
  • [1Panel]开源,现代化,新一代的 Linux 服务器运维管理面板

    本期测评试用一下1Panel这款面板。1Panel是国内飞致云旗下开源产品。整个界面简洁清爽,后端使用GO开发,前端使用VUE的Element-Plus作为UI框架,整个面板的管理都是基于docker的,想法很先进。官方还提供了视频的使用教程,本期为大家按照本专栏的基本内容进行多方面的测评。

    2024年02月07日
    浏览(94)
  • Linux服务器常见运维性能测试(3)CPU测试super_pi、sysbench

    最近需要测试一批服务器的相关硬件性能,以及在常规环境下的硬件运行稳定情况,需要持续拷机测试稳定性。所以找了一些测试用例。本次测试包括在服务器的高低温下性能记录及压力测试,高低电压下性能记录及压力测试,常规环境下CPU满载稳定运行的功率记录。 这个系

    2024年02月02日
    浏览(55)
  • 华为云云耀云服务器L实例评测 | Linux系统宝塔运维部署H5游戏

    本章节内容,我们主要介绍华为云耀服务器L实例,从云服务的优势讲起,然后讲解华为云耀服务器L实例资源面板如何操作,如何使用宝塔运维服务,如何使用运维工具可视化安装nginx,最后部署一个自研的H5的小游戏(6岁的小朋友玩的很开心😁)。 前端的同学如果想把自己

    2024年02月07日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包