Linux系统编程系列之线程的信号处理
一、为什么要有线程的信号处理
由于多线程程序中线程的执行状态是并发的,因此当一个进程收到一个信号时,那么究竟由进程中的哪条线程响应这个信号就是不确定的,只能取决于哪条线程刚好在信号达到的瞬间被调度,这种不确定性在程序逻辑中一般是不能接受的。
二、解决办法
1、在多线程进程中选定某条线程去响应信号
2、其余线程对该信号进行屏蔽
三、相关函数API接口
1、发送信号给指定线程
// 在进程内部,只允许在线程之间进行发送 int pthread_kill(pthread_t thread, int sig);// 接口说明返回值:成功返回0,失败返回错误码参数thread:接收信号的线程号参数sig:待发送的信号// 在进程之间进行的信号发送 int kill(pid_t pid, int sig);// 接口说明返回值:成功返回0,失败返回-1参数pid:接受信号的进程号参数sig:待发送的信号
2、发送带参数的信号给指定线程
// 发送带参数的信号给指定线程 // 线程间 int pthread_sigqueue(pthread_t thread, int sig,const union sigval value);// 接口说明返回值:成功返回0,失败返回-1参数thread:待接收信号的线程号参数sig:待发送的信号参数value:额外携带的参数// 进程间int sigqueue(pid_t pid, int sig, const union sigval value);// 接口说明返回值:成功返回0,失败返回-1参数pid:待接收信号的进程号参数sig:待发送的信号参数value:额外携带的参数
3、屏蔽指定信号
// 屏蔽指定信号 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);// 接口说明返回值:成功返回0,失败返回-1// 参数解析: 1、how:操作命令字,比如阻塞、解除阻塞等SIG_BLOCK:阻塞set中的信号(原有正在阻塞的信号保持阻塞)SIG_SETMASK:阻塞set中的信号(原有正在阻塞的信号自动解除)SIG_UNBLOCK:解除set中的信号2、set:当前要操作的信号集 3、oldset:若为非空,则将原有阻塞信号集保留到该oldset中 注意:该函数的操作参数不是单个信号,而是信号集。// 信号集操作函数组 int sigemptypset(sigset_t *set); // 清空信号集set int sigfillset(sigset_t *set); // 将所有信号加入信号集set中 int sigaddset(sigset_t *set, int signum); // 将信号signum添加到信号集set中 int sigdelset(sigset_t *set, int signum); // 将信号signum从信号集set中剔除 int sigsimember(const sigset_t *set, int signum); // 测试信号signum是否在信号集set中
四、案例
1、使用线程结合信号的方式完成数据的接收和发送,要求一条线程发送数据同时发送信号指定某条线程接收数据,另外有多余线程做伪任务。
// 多线程信号处理的案例#include <stdio.h> #include <pthread.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <signal.h>char data[100]; pthread_t tid1, tid2, tid3;// 信号响应函数 void recv_handler(int sig) {printf("\nmy tid is %ld\n", pthread_self());printf("read data: %s\b", data);memset(data, 0, sizeof(data)); }// 线程1的例程函数 void *routine1(void *arg) {printf("I am recv_routine, my tid = %ld\n", tid1);// 设置线程分离 pthread_detach(pthread_self()); while(1){printf("please input data:\n");fgets(data, sizeof(data), stdin);pthread_kill(tid2, 34); // 给线程2发送信号printf("send data success\n");} }// 线程2的例程函数,用来接收数据 void *routine2(void *arg) {// 注册信号响应函数signal(34, recv_handler);printf("I am routine2, my tid = %ld\n", tid2);// 设置线程分离 pthread_detach(pthread_self());while(1){pause();} }// 线程3的例程函数 void *routine3(void *arg) {printf("I am routine3, my tid = %ld\n", tid3);// 设置线程分离 pthread_detach(pthread_self());while(1){pause();} }int main(int argc, char *argv[]) {// 创建线程1,用来发送和接收数据errno = pthread_create(&tid1, NULL, routine1, NULL);if(errno == 0){printf("pthread create routine1 success, tid = %ld\n", tid1);}else{perror("pthread create routine1 fail\n");}// 创建线程2,用来做多余线程errno = pthread_create(&tid2, NULL, routine2, NULL);if(errno == 0){printf("pthread create routine2 success, tid = %ld\n", tid2);}else{perror("pthread create routine2 fail\n");}// 创建线程3,用来做多余线程errno = pthread_create(&tid3, NULL, routine3, NULL);if(errno == 0){printf("pthread create routine3 success, tid = %ld\n", tid3);}else{perror("pthread create routine3 fail\n");}// 一定要加这个,否则主函数直接退出,相当于进程退出,所有线程也退出// 或者加上while(1)等让主函数不退出pthread_exit(0);return 0; }
2、使用线程结合信号的方式完成数据的接收和发送,要求一条线程完成数据的发送和接收,另外两个线程屏蔽信号,做伪任务。
// 多线程信号处理的案例#include <stdio.h> #include <pthread.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <signal.h>char data[100]; sigset_t sigs_set; // 信号集 pid_t pid; pthread_t tid1, tid2, tid3;// 信号响应函数 void recv_handler(int sig) {printf("\nmy tid is %ld\n", pthread_self());printf("read data: %s\b", data);memset(data, 0, sizeof(data)); }// 线程1的例程函数 void *routine1(void *arg) {printf("I am routine1, my tid = %ld\n", tid1);// 设置线程分离 pthread_detach(pthread_self()); while(1){printf("please input data:\n");fgets(data, sizeof(data), stdin);printf("send data success\n");kill(pid, 34); // 给进程(所有线程)发送信号} }// 线程2的例程函数,用来接收数据 void *routine2(void *arg) {printf("I am routine2, my tid = %ld\n", tid2);// 屏蔽(阻塞)信号集中的信号sigprocmask(SIG_BLOCK, &sigs_set, NULL);// 设置线程分离 pthread_detach(pthread_self());while(1){pause();} }// 线程3的例程函数 void *routine3(void *arg) {printf("I am routine3, my tid = %ld\n", tid3);// 设置线程分离 pthread_detach(pthread_self());// 屏蔽(阻塞)信号集中的信号sigprocmask(SIG_BLOCK, &sigs_set, NULL);while(1){pause();} }int main(int argc, char *argv[]) {// 注册信号响应函数signal(34, recv_handler);sigemptyset(&sigs_set); // 清空信号集sigaddset(&sigs_set, 34); // 把34信号加到信号集中pid = getpid(); // 获取进程号// 创建线程1,用来发送和接收数据errno = pthread_create(&tid1, NULL, routine1, NULL);if(errno == 0){printf("pthread create routine1 success, tid = %ld\n", tid1);}else{perror("pthread create routine1 fail\n");}// 创建线程2,用来做多余线程errno = pthread_create(&tid2, NULL, routine2, NULL);if(errno == 0){printf("pthread create routine2 success, tid = %ld\n", tid2);}else{perror("pthread create routine2 fail\n");}// 创建线程3,用来做多余线程errno = pthread_create(&tid3, NULL, routine3, NULL);if(errno == 0){printf("pthread create routine3 success, tid = %ld\n", tid3);}else{perror("pthread create routine3 fail\n");}// 一定要加这个,否则主函数直接退出,相当于进程退出,所有线程也退出// 或者加上while(1)等让主函数不退出pthread_exit(0);return 0; }
五、总结
多线程进程中的信号处理可以采用选定某一条线程来接收信号,其余线程屏蔽该信号的做法,可以结合案例加深对多线程中信号的处理。
相关文章:

Linux系统编程系列之线程的信号处理
一、为什么要有线程的信号处理 由于多线程程序中线程的执行状态是并发的,因此当一个进程收到一个信号时,那么究竟由进程中的哪条线程响应这个信号就是不确定的,只能取决于哪条线程刚好在信号达到的瞬间被调度,这种不确定性在程序逻…...

【C语言】青蛙跳台阶 —— 详解
一、问题描述 跳台阶_牛客题霸_牛客网 (nowcoder.com) LCR 127. 跳跃训练 - 力扣(LeetCode) 二、解题思路 1、当 n 1 时,一共只有一级台阶,那么显然青蛙这时就只有一种跳法 2、当 n 2 时,一共有两级台阶ÿ…...

Java - 基本数据类型和封装类型
基本类型有默认值,而包装类型初始为null。然后再根据这两个特性进行分业务使用,在阿里巴巴的规范里所有的POJO类必须使用包装类型,而在本地变量推荐使用基本类型。 Java语言提供了八种基本类型。六种数字类型(四个整数型ÿ…...
day-63 代码随想录算法训练营(19) 图论 part 02
1020.飞地的数量 分析:求不跟边界接壤的陆地的数量 思路一:深度优先遍历 先从四个侧边找陆地,然后进行深度优先遍历,把所有接壤的陆地(1)全部转换成海洋(0) 深度优先遍历…...

SpringBoot的全局异常拦截
在 Spring Boot 中,可以通过使用 ControllerAdvice 注解和 ExceptionHandler 注解来实现全局异常拦截。 RestControllerAdvice RestControllerAdvice 是 Spring Framework 提供的注解,用于定义全局异常处理类,并且结合 ExceptionHandler 注…...
『力扣每日一题11』:转换成小写字母
一、题目 给你一个字符串 s ,将该字符串中的大写字母转换成相同的小写字母,返回新的字符串。 示例 1: 输入:s "Hello" 输出:"hello"示例 2: 输入:s "here" 输…...

复习Day07:链表part03:21. 合并两个有序链表、2. 两数相加
之前的blog链接:https://blog.csdn.net/weixin_43303286/article/details/131700482?spm1001.2014.3001.5501 我用的方法是在leetcode再过一遍例题,明显会的就复制粘贴,之前没写出来就重写,然后从拓展题目中找题目来写。辅以Lab…...

Ubuntu中启动HDFS后没有NameNode解决办法
关闭进程: stop-dfs.sh 格式化: hadoop namenode -format 出现报错信息: 23/10/03 22:27:04 WARN fs.FileUtil: Failed to delete file or dir [/usr/data/hadoop/tmp/dfs/name/current/fsimage_0000000000000000000.md5]: it still exi…...

AWS-Lambda之导入自定义包-pip包
参考文档: https://repost.aws/zh-Hans/knowledge-center/lambda-import-module-error-python https://blog.csdn.net/fxtxz2/article/details/112035627 简单来说,以 " alibabacloud_dyvmsapi20170525 " 包为例 ## 创建临时目录 mkdir /tmp cd ./tmp …...
MAC 如何解决GitHub下载速度慢的问题
说在前面 解决github下载速度慢的方法很多,本文主要介绍通过Git镜像的方式解决下载慢的问题。 主要步骤有:1、找到gitconfig文件, 2、通过git命令查看当前生效的config 配置 3、使用git config命令编辑并添加国内镜像源 1、gitconfig 文件在…...

Redis与分布式-哨兵模式
接上文 Redis与分布式-主从复制 1.哨兵模式 启动一个哨兵,只需要修改配置文件即可, sentinel monitor lbwnb 1247.0.0.1 6001 1先将所有服务关闭,然后修改配置文件,redis Master,redis Slave,redis Slave…...

创建型设计模式 原型模式 建造者模式 创建者模式对比
创建型设计模式 单例 工厂模式 看这一篇就够了_软工菜鸡的博客-CSDN博客 4.3 原型模式 4.3.1 概述 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。 4.3.2 结构 原型模式包含如下角色: 抽象原型类:规定了…...

HTML详细基础(二)文件路径
目录 一.相对路径 二.绝对路径 三.超链接标签 四.锚点链接 首先,扩展一些HTML执行的原理: htmL(hypertext markup Language) 是一种规范(或者说是一种标准),它通过标记符(tag)来标记要显示…...

大数据-玩转数据-Flink 海量数据实时去重
一、海量数据实时去重说明 借助redis的Set,需要频繁连接Redis,如果数据量过大, 对redis的内存也是一种压力;使用Flink的MapState,如果数据量过大, 状态后端最好选择 RocksDBStateBackend; 使用布隆过滤器,…...
1.在vsCode上创建Hello,World
(1).编译器的安装配置 使用vsCode进行编写c语言,首先需要安装gcc编译器,可以自己去寻找资料或者gcc官网进行下载. 下载好后,将文件夹放入到自己指定的目录后,配置系统环境变量,将path指向编译器的bin目录 进入bin目录打开cmd,输入gcc -v,然后就会成功输出信息. (2).vsCode配…...
XrayGLM - 医学大模型
文章目录 关于 XrayGLM研究背景VisualGLM-6B 关于 XrayGLM XrayGLM: 首个会看胸部X光片的中文多模态医学大模型 | The first Chinese Medical Multimodal Model that Chest Radiographs Summarization. 基于VisualGLM-6B 微调 github : https://github.com/WangRongsheng/Xra…...
Hive 常见数据倾斜场景及解决方案(Map\Join\Reduce端)
目录 MapReduce流程简述a) Map倾斜b) Join倾斜c) Reduce倾斜 首先回顾一下MapReduce的流程 MapReduce流程简述 输入分片: MapReduce 作业开始时,输入数据被分割成多个分片,每个分片大小一般在 16MB 到 128MB 之间。这些分片会被分配给不同的…...
C++中的四种强制类型转换符详解
前 言 C 既支持 C 风格的类型转换,又有自己风格的类型转换。C 风格的转换格式很简单,但是有不少缺点: 转换太过随意,可以在任意类型之间转换。你可以把一个指向 const 对象的指针转换成指向非 const 对象的指针,把一…...
Windows电脑多开器的优缺点对比
Windows电脑多开器是一种能够让用户同时运行多个应用程序、游戏或者网页的软件工具。它的作用在于让用户能够更加高效地工作、学习或者娱乐。但是,这种软件也存在一些优劣势的对比。 优点: 提升工作效率。多开器可以让用户同时打开多个应用程序或者网页…...

Java笔记六(面向对象:类与对象)
面向对象编程的本质:以类的方式组织代码,以对象的组织(封装)数据 抽象 三大特征:封装 继承 多态 从认识角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...