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笔记六(面向对象:类与对象)
面向对象编程的本质:以类的方式组织代码,以对象的组织(封装)数据 抽象 三大特征:封装 继承 多态 从认识角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...
动态规划-1035.不相交的线-力扣(LeetCode)
一、题目解析 光看题目要求和例图,感觉这题好麻烦,直线不能相交啊,每个数字只属于一条连线啊等等,但我们结合题目所给的信息和例图的内容,这不就是最长公共子序列吗?,我们把最长公共子序列连线起…...

