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

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...