Linux | 探索 Linux 信号机制:信号的产生和自定义捕捉

在这里插入图片描述

信号是 Linux 操作系统中非常重要的进程控制机制,用来异步通知进程发生某种事件。理解信号的产生、阻塞、递达、捕捉等概念,可以帮助开发者更好地编写健壮的应用程序,避免由于未处理的信号导致程序异常退出。本文将带你从基础概念开始,深入探讨信号处理的常见方式。

1. 信号的基本概念

在 Linux 系统中,信号是一种软中断机制,主要用于进程之间的异步通信。信号的产生和递达并不会按照进程的执行顺序发生,而是通过操作系统将某种事件(如用户输入、硬件异常等)通知进程。每个信号都有唯一的编号和宏定义名称。例如,SIGINT(编号2)是一个常见的信号,通常由按下 Ctrl+C 产生。

信号的产生的四种方式:

  1. 终端输入产生信号:例如,当用户在终端中按下 Ctrl+C 时,系统会发送 SIGINT 信号给当前前台进程,进而终止进程。
  2. 系统调用产生信号:开发者可以通过调用 kill 函数向指定进程发送信号,例如 kill -SIGKILL <PID>
  3. 软件条件产生信号:某些软件事件会自动触发信号。例如,SIGPIPE 信号在管道破裂时触发。
  4. 硬件异常产生信号:例如,执行非法内存访问会触发 SIGSEGV 信号。

信号处理一般有三种方式:

  • 忽略信号:进程忽略该信号。
  • 执行默认动作:进程按照信号的默认处理方式处理,例如 SIGKILL 会导致进程直接退出。
  • 捕捉信号:进程通过自定义函数捕捉信号并进行处理。

2. 信号的自定义捕捉

Linux 提供了 signalsigaction 系统调用,允许开发者自定义信号的处理函数,即捕捉信号。

a. 自定义捕捉终端产生的 SIGINT 2号信号:

#include <stdio.h>
#include <signal.h>void handler(int sig) {printf("Received signal: %d\n", sig);
}int main() {signal(SIGINT, handler); // 捕捉SIGINT信号while (1) {printf("Waiting for signal...\n");sleep(1);}return 0;
}
程序解释

handler 函数捕捉到 SIGINT 信号并打印信号编号。当用户在终端中按下 Ctrl+C,进程不会立即终止,而是调用自定义的 handler 函数。
在这里插入图片描述

b. 自定义捕捉由软件条件产生的 SIGALRM 信号

SIGALRM 信号通常由 alarm() 函数产生,用于在设定的时间后通知进程。

alarm() 函数与 SIGALRM 信号
alarm() 函数的作用是设置一个闹钟,指定经过若干秒后系统向进程发送 SIGALRM 信号。此信号的默认处理行为是终止进程,但我们可以通过自定义信号处理函数来捕捉并处理 SIGALRM 信号。
alarm() 函数的定义如下:

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
  • 参数 seconds:指定在多少秒后发送 SIGALRM 信号。如果参数为 0,则取消先前设置的闹钟。
  • 返回值:返回先前设置的闹钟还剩余的时间。如果没有设置过闹钟,返回值为 0。例如,设定闹钟为 30 秒,闹钟执行了 20 秒后取消并重设为 15 秒,之前的闹钟还剩下 10 秒,这个时间会作为 alarm() 函数的返回值。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>// 自定义的SIGALRM信号处理函数
void handle_alarm(int sig) {printf("Alarm signal received: %d. Time's up!\n", sig);
}int main() {// 注册SIGALRM信号的处理函数signal(SIGALRM, handle_alarm);// 设置闹钟为1秒alarm(1);// 计数器,在闹钟响起之前一直计数int count = 0;while (1) {printf("Counting: %d\n", count++);sleep(1); // 每秒计数一次}return 0;
}
程序解释
  1. 信号处理函数 handle_alarm
    SIGALRM 信号产生时,操作系统会调用此函数。此函数接收信号编号作为参数,并打印出提示信息。

  2. 设置闹钟
    程序通过调用 alarm(1) 设置了一个 1 秒的闹钟。即 1 秒后,系统会发送 SIGALRM 信号,触发信号处理函数 handle_alarm

  3. 计数器
    程序进入一个无限循环,每秒钟打印一次计数。在 1 秒内,计数器会输出几次,直到接收到 SIGALRM 信号并终止循环。

运行该程序时,输出:

Counting: 0
Alarm signal received: 14. Time's up!

程序开始计数,并且在 1 秒后接收到 SIGALRM 信号,调用信号处理函数,输出“Alarm signal received”信息,随后程序被信号终止。

拓展
  1. 重新设置闹钟:如果在 SIGALRM 之前再次调用 alarm(),会取消之前的闹钟并重新设置。例如,假如 alarm(5)alarm(1) 之后被调用,系统将在 5 秒而非 1 秒后发送 SIGALRM 信号。

  2. 取消闹钟alarm(0) 可以取消先前设置的闹钟,程序将不会再收到 SIGALRM 信号。

3. 信号阻塞与未决信号

信号的三种状态:阻塞、未决和递达

阻塞(Block):

阻塞是指进程可以暂时不处理某些信号。当信号被阻塞时,即使信号产生了,也不会立即处理。信号会保持在阻塞状态,直到解除阻塞。

未决(Pending):

未决是指信号已经产生,但由于被阻塞,无法递达。信号会处于未决状态,等待解除阻塞。当信号解除阻塞后,未决信号会递达给进程。

递达(Delivery):

递达是指信号从产生到被进程处理的过程。当信号未被阻塞或解除阻塞后,信号会递达给进程,触发默认处理动作或自定义的信号处理函数。
在这里插入图片描述

进程可以选择阻塞(Block)某个信号。被阻塞的信号在产生时会处于未决状态,直到进程解除对该信号的阻塞后,才会执行相关的处理动作。
注意:阻塞和忽略是不同的概念。阻塞信号意味着信号不会被递达,直到解除阻塞。而忽略则是在信号递达后选择不进行处理的一种方式。

C语言中的信号相关函数

sigset_t 是一个用于表示信号集的数据类型,每个信号用一个位来表示它的状态。这个类型可以用来存储和操作进程的信号屏蔽字(阻塞信号集),以及未决信号集。

  1. sigemptyset(sigset_t *set): 初始化一个信号集,将其中的所有信号位清零,即该信号集不包含任何信号。

  2. sigfillset(sigset_t *set): 初始化一个信号集,将其中的所有信号位设置为1,即该信号集包含所有信号。

  3. sigaddset(sigset_t *set, int signo): 在信号集中添加一个信号,使其对应的位被设置为1。signo 是要添加的信号的编号。

  4. sigdelset(sigset_t *set, int signo): 从信号集中删除一个信号,使其对应的位被设置为0。signo 是要删除的信号的编号。

  5. sigismember(const sigset_t *set, int signo): 检查信号集中的某个信号是否被设置为1。返回值为非零表示信号被设置(即有效),为0表示信号未被设置(即无效)。

  6. sigprocmask(int how, const sigset_t *set, sigset_t *oset): 读取或更改进程的信号屏蔽字(阻塞信号集)。

  7. sigpending(sigset_t *set): 读取当前进程的未决信号集。

使用 sigprocmask添加block阻塞集

在某些场景下,进程可能不希望立即处理某个信号,这时可以选择阻塞该信号。当信号被阻塞时,信号会进入未决状态,直到解除阻塞后才会递达。可以使用 sigprocmask 函数来设置信号的阻塞状态。

#include <stdio.h>
#include <signal.h>int main() {sigset_t set;sigemptyset(&set);sigaddset(&set, SIGINT);  // 阻塞SIGINT信号sigprocmask(SIG_BLOCK, &set, NULL);printf("SIGINT is blocked, press Ctrl+C...\n");sleep(10); // 期间按下Ctrl+C不会终止程序return 0;
}

SIGINT 信号被阻塞,按下 Ctrl+C 时,进程不会立刻退出,而是被阻塞,不能被递达,需要等到信号被解除阻塞后才能处理信号。

如果将所有信号都使用sigprocmask函数添加到block阻塞位图当中,是不是就能产生一个无法被退出的无敌进程?
答:SIGKILL (9号信号)和 SIGSTOP(19号信号)是特殊的,无法被阻塞、忽略或捕获。我们依然可以通过这些信号杀死进程,所以这样操作并不能使进程成为“无敌”的进程。(操作系统设计者早就想到了~~)

4. 常见信号的递达过程和处理方式

信号的递达过程
  1. 信号抵达的检查: 当系统从内核态返回用户态时,会首先检查当前进程的未决信号(pending signals)。这时,系统处于内核态,有权限检查进程的信号状态。

  2. 处理未决信号:

    • 如果发现有未决信号,并且该信号没有被阻塞,系统会决定如何处理这些信号。
    • 对于默认处理动作或忽略的信号,系统会执行默认动作或忽略信号,然后清除对应的未决标志位。
      在这里插入图片描述
  3. 指定信号动作:

    • 如果信号的处理动作是用户自定义的,系统会返回用户态,执行用户定义的处理函数。执行完自定义处理函数后,用户态的处理程序会通过 sigreturn 系统调用返回内核态,清除对应的未决标志位。如果没有新的信号要处理,系统会直接返回用户态,从主控制流程中上次被中断的地方继续执行。
      在这里插入图片描述
      在这里插入图片描述
  4. 为什么执行自定义函数是需要由内核态切换到内核态:

    • 尽管内核态具有高权限,但操作系统设计中不允许直接在内核态执行用户代码。原因是用户代码可能包含非法操作,如清空数据库等,这在用户态时权限不足,但在内核态时可能会造成严重后果。
    • 操作系统必须确保用户代码的合法性,以防止安全风险。因此,操作系统会严格控制用户代码的执行,确保系统安全和稳定。
信号的处理方式
信号编号宏定义名称默认动作说明
1SIGHUP终止进程终端挂起时发送此信号
2SIGINT终止进程用户按下 Ctrl+C
9SIGKILL终止进程(不可捕捉)直接终止进程
11SIGSEGV终止进程并生成 core段错误,非法内存访问
15SIGTERM终止进程请求进程终止

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/428892.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利士策分享,自我和解:通往赚钱与内心富足的和谐之道

利士策分享&#xff0c;自我和解&#xff1a;通往赚钱与内心富足的和谐之道 在这个快节奏、高压力的时代&#xff0c;我们往往在追求物质财富的同时&#xff0c;忽略了内心世界的和谐与平衡。 赚钱&#xff0c;作为现代生活中不可或缺的一部分&#xff0c;它不仅仅是生存的手段…

【Godot4.3】胶囊形的偏移获取法

概述 之前用半圆弧拼接的方式求过胶囊形&#xff0c;在逐渐熟练使用Geometry2D的过程中&#xff0c;发现通过线段求端点是圆角类型的偏移多边形&#xff0c;获得的就是胶囊形。 所以我们有了第二种胶囊形求法。 测试代码 tool extends Node2D## 横向宽度 export var width:…

Java-数据结构-排序-(一) (。・ω・。)

文本目录&#xff1a; ❄️一、排序的概念及引用&#xff1a; ➷ 排序的概念&#xff1a; ➷ 常见的排序算法&#xff1a; ❄️二、插入排序的实现&#xff1a; ➷ 1、直接插入排序&#xff1a; ☞ 直接插入排序的基本思想&#xff1a; ☞ 直接插入排序的实现&#xff1a; ▶…

安全热点问题

安全热点问题 1.DDOS2.补丁管理3.堡垒机管理4.加密机管理 1.DDOS 分布式拒绝服务攻击&#xff0c;是指黑客通过控制由多个肉鸡或服务器组成的僵尸网络&#xff0c;向目标发送大量看似合法的请求&#xff0c;从而占用大量网络资源使网络瘫痪&#xff0c;阻止用户对网络资源的正…

HarmonyOS Next开发----使用XComponent自定义绘制

XComponent组件作为一种绘制组件&#xff0c;通常用于满足用户复杂的自定义绘制需求&#xff0c;其主要有两种类型"surface和component。对于surface类型可以将相关数据传入XComponent单独拥有的NativeWindow来渲染画面。 由于上层UI是采用arkTS开发&#xff0c;那么想要…

【医疗大数据】基于 B2B 的医疗保健系统中大数据信息管理的安全和隐私问题分析

基于 B2B 的医疗保健系统中大数据信息管理的安全和隐私问题分析 1、引言 1-1 医疗大数据的特点 10 V模型&#xff1a;在医疗领域&#xff0c;大数据的特点被描述为10 V&#xff0c;包括价值&#xff08;Value&#xff09;、体量&#xff08;Volume&#xff09;、速度&#xf…

Leetcode Hot 100刷题记录 -Day16(旋转图像)

旋转图像 问题描述&#xff1a; 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在原地旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1 输入&#xff1a;matrix [[1,2,3],[4,5,6]…

Python学习——【4.2】数据容器:tuple元组

文章目录 【4.2】数据容器&#xff1a;tuple元组一、元组的定义格式二、元组的特点三、元组的操作&#xff08;一&#xff09;常见操作&#xff08;二&#xff09;循环遍历 【4.2】数据容器&#xff1a;tuple元组 一、元组的定义格式 为什么需要元组 列表是可以修改的。如果想…

【网络安全】分享4个高危业务逻辑漏洞

未经许可,不得转载。 文章目录 正文逻辑漏洞1逻辑漏洞2逻辑漏洞3逻辑漏洞4其它正文 该目标程序是一家提供浏览器服务的公司,其核心功能是网页抓取和多账户登录操作,类似于浏览器中的隐身模式,但更加强大和高效。通过该平台,用户可以轻松管理并同时运行数百个隐身浏览器实…

Navicate 链接Oracle 提示 Oracle Library is not loaded ,账号密码都正确地址端口也对

Navicate 链接Oracle 提示 Oracle Library is not loaded ,账号密码都正确地址端口也对的问题 解决办法 出现 Oracle Library is not loaded 错误提示&#xff0c;通常是因为 Navicat 无法找到或加载 Oracle 客户端库&#xff08;OCI.dll&#xff09;。要解决这个问题&#x…

【自动驾驶】决策规划算法 | 数学基础(三)直角坐标与自然坐标转换Ⅱ

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

Centos中关闭swap分区,关闭内存交换

概述&#xff1a; Swap 分区是 Linux 系统中扩展物理内存的一种机制。Swap的主要功能是当全部的RAM被占用并需要更多内存时&#xff0c;用磁盘空间代理RAM内存。Swap对虚拟化技术资源损耗非常大&#xff0c;一般虚拟化是不允许开启交换空间的&#xff0c;如果不关闭Swap&…

LED显示屏迎来革新:GOB封装技术引领行业新风尚

在我们日常生活中&#xff0c;LED显示屏无处不在&#xff0c;从繁华的街头广告牌到家庭娱乐中心的大屏幕电视&#xff0c;它们都以鲜明的色彩和清晰的画质吸引着我们的目光。然而&#xff0c;在LED显示屏技术日新月异的今天&#xff0c;一种名为GOB&#xff08;Glue On Board&a…

ChatCADChatCAD+:Towards a Universal and Reliable Interactive CAD using LLMs

ChatCAD&#xff08;论文链接&#xff1a;[2302.07257] ChatCAD: Interactive Computer-Aided Diagnosis on Medical Image using Large Language Models (arxiv.org)&#xff09; 网络流程图&#xff1a; 辅助阅读&#xff1a; 基于大型语言模型的医学图像交互式计算机辅助诊…

7、论等保的必要性

数据来源&#xff1a;7.论等保的必要性_哔哩哔哩_bilibili 等级保护必要性 降低信息安全风险 等级保护旨在降低信息安全风险&#xff0c;提高信息系统的安全防护能力。 风险发现与整改 开展等级保护的最重要原因是通过测评工作&#xff0c;发现单位系统内外部的安全风险和脆弱…

基于SpringBoot的考研助手系统+LW参考示例

系列文章目录 1.基于SSM的洗衣房管理系统原生微信小程序LW参考示例 2.基于SpringBoot的宠物摄影网站管理系统LW参考示例 3.基于SpringBootVue的企业人事管理系统LW参考示例 4.基于SSM的高校实验室管理系统LW参考示例 5.基于SpringBoot的二手数码回收系统原生微信小程序LW参考示…

c++9月20日

1.思维导图 2.顺序表 头文件 #ifndef RECTANGLE_H #define RECTANGLE_H#include <iostream>using namespace std;using datatype int ;//类型重定义class Seqlist { private://私有权限datatype *ptr; //指向堆区申请空间的起始地址int size;//堆区空间的长度int len …

在python爬虫中xpath方式提取lxml.etree._ElementUnicodeResult转化为字符串str类型

简单提取网页中的数据时发现的 当通过xpath方式提取出需要的数据的text文本后想要转为字符串&#xff0c;但出现lxml.etree._ElementUnicodeResult的数据类型不能序列化&#xff0c;在网上查找到很多说是编码问题Unicode编码然后解码什么的&#xff1b;有些是(导入的xml库而不…

【24华为杯数模研赛赛题思路已出】国赛B题思路丨附参考代码丨免费分享

2024年华为杯研赛B题解题思路 B题 WLAN组网中网络吞吐量建模 问题1 请根据附件WLAN网络实测训练集中所提供的网络拓扑、业务流量、门限、节点间RSSI的测试基本信息&#xff0c;分析其中各参数对AP发送机会的影响&#xff0c;并给出影响性强弱的顺序。通过训练的模型&#xff…

在SpringBoot项目中利用Redission实现布隆过滤器(布隆过滤器的应用场景、布隆过滤器误判的情况、与位图相关的操作)

文章目录 1. 布隆过滤器的应用场景2. 在SpringBoot项目利用Redission实现布隆过滤器3. 布隆过滤器误判的情况4. 与位图相关的操作5. 可能遇到的问题&#xff08;Redission是如何记录布隆过滤器的配置参数的&#xff09;5.1 问题产生的原因5.2 解决方案5.2.1 方案一&#xff1a;…