当前位置: 首页 > news >正文

《Linux C编程实战》笔记:信号的捕捉和处理

Linux系统中对信号的处理主要由signal和sigaction函数来完成,另外还会介绍一个函数pause,它可以用来响应任何信号,不过不做任何处理

signal函数

#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);

可以分解为以下几个部分:

  1. signal 是一个函数,它接受两个参数:signumhandler
  2. signum 是一个整数,表示信号的编号,例如 SIGINT 表示中断信号。
  3. 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成员用来说明信号处理的一些其他相关操作。

这个字段的取值可以是以下几个常量之一,或者它们的按位组合:

  1. SA_RESETHAND或SA_ONESHOT: 信号处理程序执行一次后,就恢复为默认的处理方式(SIG_DFL),即重新设置为系统默认的信号处理函数。这样,下次再接收到相同信号时,会再次调用用户设置的处理函数。

  2. SA_RESTART: 如果系统调用由信号中断,那么自动重启该系统调用。这通常用于防止由信号中断的系统调用在中断后不会继续执行,而是返回错误。

  3. SA_NODEFER或SA_NOMASK: 在信号处理程序执行期间,不阻塞同样的信号。也就是说,如果进程在执行信号处理程序时再次收到相同的信号,处理程序会被再次调用。默认情况下,执行信号处理程序时,相同的信号会被阻塞,直到处理完成。

  4. SA_NOCLDSTOP: 如果设置了这个标志,子进程的停止状态(stopped)不会生成 SIGCHLD 信号。通常,当子进程停止或继续执行时,会发送 SIGCHLD 信号给父进程。

  5. SA_SIGINFO: 如果设置了这个标志,信号处理程序是 sa_sigaction 而不是 sa_handlersa_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函数来完成&#xff0c;另外还会介绍一个函数pause&#xff0c;它可以用来响应任何信号&#xff0c;不过不做任何处理 signal函数 #include <signal.h> void (*signal(int signum, void (*handler)(int)))(int);可以分解…...

python算法与数据结构---单调栈与实践

单调栈 单调栈是一个栈&#xff0c;里面的元素的大小按照它们所在栈的位置&#xff0c;满足一定的单调性&#xff1b; 性质&#xff1a; 单调递减栈能找到左边第一个比当前元素大的元素&#xff1b;单调递增栈能找到左边第一个比当前元素小的元素&#xff1b; 应用场景 一般用…...

文心一言使用分享

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

【C++干货铺】C++11新特性——lambda表达式 | 包装器

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 C98中的排序 lambda表达式 lambda表达式语法 表达式中的各部分说明 lambda表达式的使用 基本的使用 [var]值传递捕捉变量var ​编辑 [&var]引用传递捕…...

在 EggJS 中实现 Redis 上锁

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

Unity-场景

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

MATLAB R2023b for Mac 中文

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

01 MyBatisPlus快速入门

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

HarmonyOS 应用开发入门

HarmonyOS 应用开发入门 前言 DevEco Studio Release版本为&#xff1a;DevEco Studio 3.1.1。 Compile SDK Release版本为&#xff1a;3.1.0&#xff08;API 9&#xff09;。 构建方式为 HVigor&#xff0c;而非 Gradle。 最新版本已不再支持 &#xff08;”Java、JavaScrip…...

【机器学习300问】9、梯度下降是用来干嘛的?

当你和我一样对自己问出这个问题后&#xff0c;分析一下&#xff01;其实我首先得知道梯度下降是什么&#xff0c;也就它的定义。其次我得了解它具体用在什么地方&#xff0c;也就是使用场景。最后才是这个问题&#xff0c;梯度下降有什么用&#xff1f;怎么用&#xff1f; 所以…...

第13章 1 进程和线程

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

什么是中间件?

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

汽车售后服务客户满意度调查报告

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

初始RabbitMQ(入门篇)

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

JVM:Java类加载机制

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

要经历痛苦,才能在赚钱路上觉醒!

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

LeetCode 第381场周赛个人题解

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

数据结构之二叉树的性质与存储结构

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

机器视觉检测设备在连接器外观缺陷检测中的应用

作为传输电流或信号连接两个有源器件的器件&#xff0c;连接器被广泛应用于各个行业&#xff0c;从手机、平板、电脑&#xff0c;到冰箱、空调、洗衣机&#xff0c;再到汽车、国防、航空&#xff0c;处处是它的所在。每个电子产品少了连接器将无法运作&#xff0c;因此&#xf…...

ChatGPT vs 文心一言(AI助手全面比较)

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

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...