《Linux C编程实战》笔记:信号的捕捉和处理
Linux系统中对信号的处理主要由signal和sigaction函数来完成,另外还会介绍一个函数pause,它可以用来响应任何信号,不过不做任何处理
signal函数
#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
可以分解为以下几个部分:
signal
是一个函数,它接受两个参数:signum
和handler
。signum
是一个整数,表示信号的编号,例如SIGINT
表示中断信号。handler
是一个指向函数的指针,该函数负责处理收到的信号。
signal
函数返回一个函数指针,该指针指向之前注册的信号处理函数。
写成这样可能更好理解,我看我的系统里的源码也是长这样的:
#include<signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
sighandler_t指的类型是一个函数指针,指向的函数是一个int类型参数,返回值为void类型。
signal会根据参数signum指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。如果参数handler不是函数指针,则必须是常数SIG_IGN (忽略该信号)或SIG_DFL (对该信号执行默认操作)。handler 是一个函数指针,它所指向的函数的类型是sighandler t,即它所指向的函数有一个int型参数,且返回值的类型为void。
signal函数执行成功时返回以前的信号处理函数指针,当有错误发生时返回SIG_ERR(即-1)。
注意:SIGKILL和SIGSTOP这两个信号不能被捕捉或忽略。
示例程序1
该示例程序演示了signal的使用
#include<stdio.h>
#include<signal.h>
void handler_sigint(int signo){printf("recv SIGINT\n");
}
int main(){if(signal(SIGINT,handler_sigint)==SIG_ERR){perror("Error setting signal handler");return 1;}while(1);return 0;
}
首先使用signal安装信号SIGINT的处理函数,然后进入死循环。当接收到SIGINT信号时,程序自动跳转到信号处理函数执行,打印出提示信息,然后返回主函数继续死循环。
SIGINT这个信号按Ctrl+C就可以产生了,信号的介绍可看前一篇《Linux C编程实战》笔记:Linux信号介绍-CSDN博客
执行结果如下:
最后按下Ctrl+\组合键向进程发送SIGQUIT信号。由于程序本身没有处理SIGQUIT信号,按照默认处理方式,进程退出。
sigaction函数
sigaction函数可以用来检查或设置进程在接收信号时的动作。函数原型如下
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数说明:
signum
: 要处理的信号编号。act
: 一个指向struct sigaction
结构的指针,指定新的信号处理方式。oldact
: 一个指向struct sigaction
结构的指针,用于存储之前的信号处理方式。
sigaction会根据参数signum指定的信号编号来设置信号的处理函数。参数signum可以是SIGKILL和SIGSTOP以外的任何信号。如果参数act不是空指针,则为signum设置新的信号处理函数;如果oldact不是空指针,则旧的信号处理函数将被存储在oldact中。struct sigaction的定义如下:
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
是用于指定信号处理函数的指针,sa_mask
是一个信号集,用于指定在信号处理函数执行期间要阻塞的信号,sa_flags
是用于设置信号处理的一些标志,sa_restorer
是用于指定一个恢复函数。
sa_handler和sa_sigaction在某些体系结构上被定义为共用体,即这两个值在某一时刻只有一个有效。
数据成员sa_restorer 已经作废,不再使用,POSIX标准也不支持该数据成员。
sa_handler可以是常数SIG_DFL或SIG_IGN,或者是一个信号处理函数的函数名。信号处理函数只
有一个参数即信号编号。该参数和参数sa_sigaction实际上都是函数指针。
sa_ sigaction也是用来指定信号signum的处理函数,但是它有3个参数,第一个参数是信号编号;
第二个参数是一个指向siginfo_t结构的指针;第三个参数是一个指向任何类型的指针,一般不使用。
sa_mask成员声明了一个信号集,在调用信号捕捉函数之前,该信号集会增加到进程的信号屏蔽码中,新的信号屏蔽码会自动包括正在处理的信号(sa_flags未指定SA_NODEFER或SA_NOMASK).当从信号捕捉函数返回时,进程的信号屏蔽码会恢复为原来的值。因此,当处理一个给定的信号时,如果这种信号再次发生,那么它会被阻塞直到本次信号处理结束为止。若这种信号发生了多次,则对于不可靠信号,它只会被阻塞一次, 即本次信号处理结束以后只会再处理一次(相当于丢失了信号);对于可靠信号(实时信号),则会被阻塞多次,即信号不会丢失,信号发生了多少次就会调用信号处理函数多少次。
sa_flags成员用来说明信号处理的一些其他相关操作。
这个字段的取值可以是以下几个常量之一,或者它们的按位组合:
SA_RESETHAND或SA_ONESHOT
: 信号处理程序执行一次后,就恢复为默认的处理方式(SIG_DFL
),即重新设置为系统默认的信号处理函数。这样,下次再接收到相同信号时,会再次调用用户设置的处理函数。
SA_RESTART
: 如果系统调用由信号中断,那么自动重启该系统调用。这通常用于防止由信号中断的系统调用在中断后不会继续执行,而是返回错误。
SA_NODEFER或SA_NOMASK
: 在信号处理程序执行期间,不阻塞同样的信号。也就是说,如果进程在执行信号处理程序时再次收到相同的信号,处理程序会被再次调用。默认情况下,执行信号处理程序时,相同的信号会被阻塞,直到处理完成。
SA_NOCLDSTOP
: 如果设置了这个标志,子进程的停止状态(stopped)不会生成SIGCHLD
信号。通常,当子进程停止或继续执行时,会发送SIGCHLD
信号给父进程。
SA_SIGINFO
: 如果设置了这个标志,信号处理程序是sa_sigaction
而不是sa_handler
。sa_sigaction
允许更多的信息传递给信号处理程序。这些标志可以通过按位或(
|
)的方式组合使用。
当使用三参数的sa_sigaction来指定信号处理函数时,它的第二个参数可以用来传递数据,其定义如下:
#include <signal.h>struct siginfo_t {int si_signo; // 信号编号int si_errno; // 与信号相关的错误编号int si_code; // 信号代码pid_t si_pid; // 发送信号的进程IDuid_t si_uid; // 发送信号的用户IDint si_status; // 子进程的退出状态或信号clock_t si_utime; // 用户态运行时间clock_t si_stime; // 内核态运行时间sigval_t si_value; // 信号值int si_int; // 附加的整数值void* si_ptr; // 附加的指针值int si_overrun; // 未处理的timer信号的数量int si_timerid; // 产生timer信号的timer IDvoid* si_addr; // 发生错误的内存地址long si_band; // 通用的事件描述int si_fd; // 文件描述符short si_addr_lsb; // 最低有效字节的地址int si_tid; // 产生signal的线程IDstruct {int si_trapno; // 陷阱的编号short si_addr_lsb; // 最低有效字节的地址(覆盖si_addr_lsb)} si_perfdata;
};
其中,所有的信号都有si_signo、si_errno和si_code 这3个数据成员,分别表示信号编号,errno值和信号产生的原因。其他成员则根据信号的不同含有不同的意义,接收信号的进程只能读这些成员的值,而不能进行设置。si_int 和si_ptr 可以用来传递数据,后面会演示其用法。其余的数据成员则根据不同的信号存在不同的组合,了解即可。
sigaction函数执行成功时返回0,当有错误发生时返回-1,错误代码存入errno。
注意:Linux下signal函数是由sigaction实现的。
示例程序2
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int temp=0;
void handler_sigint(int signo){printf("\nrecv SIGINT\n");sleep(5);temp+=1;printf("the value of temp is:%d\n",temp);printf("in handler_sigint,after sleep\n");
}
int main(){struct sigaction act;act.sa_handler=handler_sigint;act.sa_flags=SA_NOMASK;//允许嵌套sigaction(SIGINT,&act,nullptr);while (1);return 0;
}
运行结果
主要看第二和第三次按下Ctrl+C。我们设置的是按下Ctrl+C时,信号的处理函数会睡5s,第三次Ctrl+C正是在这5s内按下的,由于我们设定了sa_flags的值为SA_NOMASK,因此程序又一次响应了SIGINT,程序从sleep()处嵌套调用信号处理函数handler_sigint,再一次打印出"recv SIGINT",睡眠5s后,将temp的值打印出来并返回到本次信号处理程序的跳入点sleep()处,然后再打印出temo的值并返回到主函数。
从程序执行可以看到,temp 的值随着信号处理函数被调用的次数的增加而递增,而由于实际应用中信号总是随机发生的,这样temp的值也会随机变化。如果main函数或其他地方还用到了这个全局变量,则程序将产生不可预料的结果。我们称这种数据会被破坏的函数为不可重入函数。编写信号处理程序时要注意不要使用不可重入函数。一般来说,满足下列条件之一的函数是不可重入的。
使用了静态的数据结构,如getgrgid(),全局变量等。
函数实现时,调用了malloc或者 free函数。
函数实现时,使用了标准I/O函数。
将程序中的SA_NOMASK这一行去掉,重新编译运行,执行时快速按下Ctrl+c三次以上,结果如下
可以看到确实按了三次Ctrl+c,但是函数只执行了两次,因为SIGINT是不可靠信号,不可靠信号不支持排队,从而有可能丢失信号。第三个信号就丢失了。
pause函数
pause函数使调用进程挂起直至捕捉到一个信号
#include<unistd.h>
int pause(void);
pause函数会令目前的进程暂停,知道被信号所中断。该函数只返回-1并将errno设置为EINTR。
相关文章:

《Linux C编程实战》笔记:信号的捕捉和处理
Linux系统中对信号的处理主要由signal和sigaction函数来完成,另外还会介绍一个函数pause,它可以用来响应任何信号,不过不做任何处理 signal函数 #include <signal.h> void (*signal(int signum, void (*handler)(int)))(int);可以分解…...

python算法与数据结构---单调栈与实践
单调栈 单调栈是一个栈,里面的元素的大小按照它们所在栈的位置,满足一定的单调性; 性质: 单调递减栈能找到左边第一个比当前元素大的元素;单调递增栈能找到左边第一个比当前元素小的元素; 应用场景 一般用…...

文心一言使用分享
ChatGPT 和文心一言哪个更好用? 一个直接可以用,一个还需要借助一些工具,还有可能账号会消失…… 没有可比性。 通用大模型用于特定功能的时候需要一些引导技巧。 import math import time def calculate_coordinate(c, d, e, f, g, h,…...

【C++干货铺】C++11新特性——lambda表达式 | 包装器
个人主页点击直达:小白不是程序媛 C系列专栏:C干货铺 代码仓库:Gitee 目录 C98中的排序 lambda表达式 lambda表达式语法 表达式中的各部分说明 lambda表达式的使用 基本的使用 [var]值传递捕捉变量var 编辑 [&var]引用传递捕…...

在 EggJS 中实现 Redis 上锁
配置环境 下载 Redis Windows 访问 https://github.com/microsoftarchive/redis/releases 选择版本进行下载 - 勾选 [配置到环境变量] - 无脑下一步并安装 命令行执行:redis-cli -v 查看已安装的 Redis 版本,能成功查看就表示安装成功啦~ Mac brew i…...

Unity-场景
创建场景 创建新的场景后: 文件 -> 生成设置 -> Build中的场景 -> 将项目中需要使用的场景拖进去 SceneTest public class SceneTest : MonoBehaviour {// Start is called before the first frame updatevoid Start(){// 两个类: 场景类、场…...

MATLAB R2023b for Mac 中文
MATLAB R2023b 是 MathWorks 发布的最新版本的 MATLAB,适用于进行算法开发、数据可视化、数据分析以及数值计算等任务的工程师和科学家。它包含了一系列新增功能和改进,如改进了数据导入工具,增加了对数据帧和表格对象的支持,增强…...

01 MyBatisPlus快速入门
1. MyBatis-Plus快速入门 版本 3.5.31并非另起炉灶 , 而是MyBatis的增强 , 使用之前依然要导入MyBatis的依赖 , 且之前MyBatis的所有功能依然可以使用.局限性是仅限于单表操作, 对于多表仍需要手写 项目结构: 先导入依赖,比之前多了一个mybatis-plus…...

HarmonyOS 应用开发入门
HarmonyOS 应用开发入门 前言 DevEco Studio Release版本为:DevEco Studio 3.1.1。 Compile SDK Release版本为:3.1.0(API 9)。 构建方式为 HVigor,而非 Gradle。 最新版本已不再支持 (”Java、JavaScrip…...

【机器学习300问】9、梯度下降是用来干嘛的?
当你和我一样对自己问出这个问题后,分析一下!其实我首先得知道梯度下降是什么,也就它的定义。其次我得了解它具体用在什么地方,也就是使用场景。最后才是这个问题,梯度下降有什么用?怎么用? 所以…...

第13章 1 进程和线程
文章目录 程序和进程的概念 p173函数式创建子进程Process类常用的属性和方法1 p175Process类中常用的属性和方法2 p176继承式创建子进程 p177进程池的使用 p178并发和并行 p179进程之间数据是否共享 p180队列的基本使用 p180使用队列实现进程之间的通信 p182函数式创建线程 p18…...

什么是中间件?
文章目录 为什么需要中间件?中间件生态漫谈数据库中间件读写分离分库分表引进数据库中间件MyCat 服务端代理模式ShardingJDBC 客户端代理模式 总结 IT 系统从单体应用逐渐向分布式架构演变,高并发、高可用、高性能、分布式等话题变得异常火热,…...

汽车售后服务客户满意度调查报告
本文由群狼调研(长沙旅行社满意度调查)出品,欢迎转载,请注明出处。汽车售后服务客户满意度调查报告通常包括以下内容: 1.调研概况:介绍调研的目的、背景和范围,包括调研的时间、地点和样本规模等…...

初始RabbitMQ(入门篇)
消息队列(MQ) 本质上就是一个队列,一个先进先出的队列,队列中存放的内容是message(消息),是一种跨进程的通信机制,用于上下游传递消息, 为什么使用MQ: 削峰填谷: MQ可以很好的做一个缓冲机制,例如在一个系统中有A和B两个应用,A是接收用户的请求的,然后A调用B进行处理. 这时…...

JVM:Java类加载机制
Java类加载机制的全过程: 加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始, 这是为了支持Java…...

要经历痛苦,才能在赚钱路上觉醒!
新手赚钱,一个秘诀就够了! 黎明前的黑暗实际是最漫长的,就如同开发进度99%到100%这个过程尤其漫长。赚钱的路上起初就是黑暗,不断地摸索最终才能走出迷雾,真正的迎接朝阳。如果有一段路程,十来公里的路线&a…...

LeetCode 第381场周赛个人题解
目录 100191. 输入单词需要的最少按键次数 I 原题链接 题目描述 思路分析 AC代码 100188. 按距离统计房屋对数目 I 原题链接 题目描述 思路分析 AC代码 100192. 输入单词需要的最少按键次数 II 原题链接 题目描述 思路分析 AC代码 100213. 按距离统计房屋对数目…...

数据结构之二叉树的性质与存储结构
数据结构之二叉树的性质与存储结构 1、二叉树的性质2、二叉树的存储结构 数据结构是程序设计的重要基础,它所讨论的内容和技术对从事软件项目的开发有重要作用。学习数据结构要达到的目标是学会从问题出发,分析和研究计算机加工的数据的特性,…...

机器视觉检测设备在连接器外观缺陷检测中的应用
作为传输电流或信号连接两个有源器件的器件,连接器被广泛应用于各个行业,从手机、平板、电脑,到冰箱、空调、洗衣机,再到汽车、国防、航空,处处是它的所在。每个电子产品少了连接器将无法运作,因此…...

ChatGPT vs 文心一言(AI助手全面比较)
随着人工智能的不断发展,ChatGPT(OpenAI)和文心一言都代表了当前先进的自然语言处理技术。它们在智能回复、语言准确性和知识库丰富度等方面都有各自的优势。在下面的比较中,我们将从多个角度探讨这两个AI助手,帮助你更…...

MSPM0L1306例程学习-UART部分(2)
MSPM0L1306例程学习系列 1.背景介绍 写在前边的话: 这个系列比较简单,主要是围绕TI官网给出的SDK例程进行讲解和注释。并没有针对模块的具体使用方法进行描述。所有的例程均来自MSPM0 SDK的安装包,具体可到官网下载并安装: https://www.ti…...

Baichuan2百川模型部署的bug汇总
1.4bit的量化版本最好不要在Windows系统中运行,大概原因报错原因是bitsandbytes不支持window,bitsandbytes-windows目前仅支持8bit量化。 2. 报错原因是机器没有足够的内存和显存,offload_folder设置一个文件夹来保存那些离线加载到硬盘的权…...

ChatGPT 如何解决 “Something went wrong. lf this issue persists ….” 错误
Something went wrong. If this issue persists please contact us through our help center at help.openai.com. ChatGPT经常用着用着就出现 “Something went wrong” 错误,不管是普通账号还是Plus账号,不管是切换到哪个节点,没聊两次就报…...

怎么移除WordPress后台工具栏的查看站点子菜单?如何改为一级菜单?
默认情况下,我们在WordPress后台想要访问前端网站,需要将鼠标移动到左上角的站点名称,然后点击下拉菜单中的“查看站点”才行,而且还不是新窗口打开。那么有没有办法将这个“查看站点”子菜单变成一级菜单并显示在顶部管理工具栏中…...

WEB-前端 表格标签-合并单元格
目录 合并单元方式 : 跨行合并 : 跨列合并 : 目标单元格 : 跨行的话 跨列的话 合并的步骤 : 跨行合并 : 跨列合并 : 特殊情况下,可以把多个单元格合并为一个单元格,我们呢先…...

[计算机网络]基本概念
目录 1.ip地址和端口号 1.1IP地址 1.2端口号 2.认识协议 2.1概念: 2.2知名协议的默认端口 3.五元组 4.协议分层 4.1分层的作用 4.2OSI七层模型 4.3TCP/IP五层(四层)模型 编辑4.4网络设备对应的分层: 编辑以下为跨…...

Flutter 综述
Flutter 综述 1 介绍1.1 概述1.2 重要节点1.3 移动开发中三种跨平台框架技术对比1.4 flutter 技术栈1.5 IDE1.6 Dart 语言1.7 应用1.8 框架 2 Flutter的主要组成部分3 资料书籍 《Flutter实战第二版》Dart 语言官网Flutter中文开发者社区flutter 官网 4 搭建Flutter开发环境参考…...

Pixels:重新定义游戏体验的区块链农场游戏
数据源:Pixels Dashboard 作者:lesleyfootprint.network 最近,Pixels 通过从 Polygon 转移到 Sky Mavis 旗下的 Ronin 网络,完成了一次战略性的转变。 Pixels 每日交易量 Pixels 在 Ronin 网络上的受欢迎程度急剧上升…...

【JavaEE】文件操作 —— IO
文件操作 —— IO 1. 文件的属性 文件内容文件大小文件路径文件名称 2. 文件的管理 采用树形结构进行管理。 3. 文件路径 分为两种:相对、绝对路径。 相对路径:相对于当前位置的路径,以“./xxx.xxx”为标志绝对路径:以从盘符…...

推荐新版AI智能聊天系统网站源码ChatGPT NineAi
Nine AI.ChatGPT是基于ChatGPT开发的一个人工智能技术驱动的自然语言处理工具,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,真正像人类一样来聊天交流,甚至能完成撰写邮件、视频脚本、文案、翻译、代…...