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

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系统编程系列之线程的信号处理

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

【C语言】青蛙跳台阶 —— 详解

一、问题描述 跳台阶_牛客题霸_牛客网 (nowcoder.com) LCR 127. 跳跃训练 - 力扣&#xff08;LeetCode&#xff09; 二、解题思路 1、当 n 1 时&#xff0c;一共只有一级台阶&#xff0c;那么显然青蛙这时就只有一种跳法 2、当 n 2 时&#xff0c;一共有两级台阶&#xff…...

Java - 基本数据类型和封装类型

基本类型有默认值&#xff0c;而包装类型初始为null。然后再根据这两个特性进行分业务使用&#xff0c;在阿里巴巴的规范里所有的POJO类必须使用包装类型&#xff0c;而在本地变量推荐使用基本类型。 Java语言提供了八种基本类型。六种数字类型&#xff08;四个整数型&#xff…...

day-63 代码随想录算法训练营(19) 图论 part 02

1020.飞地的数量 分析&#xff1a;求不跟边界接壤的陆地的数量 思路一&#xff1a;深度优先遍历 先从四个侧边找陆地&#xff0c;然后进行深度优先遍历&#xff0c;把所有接壤的陆地&#xff08;1&#xff09;全部转换成海洋&#xff08;0&#xff09; 深度优先遍历&#xf…...

SpringBoot的全局异常拦截

在 Spring Boot 中&#xff0c;可以通过使用 ControllerAdvice 注解和 ExceptionHandler 注解来实现全局异常拦截。 RestControllerAdvice RestControllerAdvice 是 Spring Framework 提供的注解&#xff0c;用于定义全局异常处理类&#xff0c;并且结合 ExceptionHandler 注…...

『力扣每日一题11』:转换成小写字母

一、题目 给你一个字符串 s &#xff0c;将该字符串中的大写字母转换成相同的小写字母&#xff0c;返回新的字符串。 示例 1&#xff1a; 输入&#xff1a;s "Hello" 输出&#xff1a;"hello"示例 2&#xff1a; 输入&#xff1a;s "here" 输…...

复习Day07:链表part03:21. 合并两个有序链表、2. 两数相加

之前的blog链接&#xff1a;https://blog.csdn.net/weixin_43303286/article/details/131700482?spm1001.2014.3001.5501 我用的方法是在leetcode再过一遍例题&#xff0c;明显会的就复制粘贴&#xff0c;之前没写出来就重写&#xff0c;然后从拓展题目中找题目来写。辅以Lab…...

Ubuntu中启动HDFS后没有NameNode解决办法

关闭进程&#xff1a; stop-dfs.sh 格式化&#xff1a; hadoop namenode -format 出现报错信息&#xff1a; 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包

参考文档&#xff1a; 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下载速度慢的方法很多&#xff0c;本文主要介绍通过Git镜像的方式解决下载慢的问题。 主要步骤有&#xff1a;1、找到gitconfig文件&#xff0c; 2、通过git命令查看当前生效的config 配置 3、使用git config命令编辑并添加国内镜像源 1、gitconfig 文件在…...

Redis与分布式-哨兵模式

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

创建型设计模式 原型模式 建造者模式 创建者模式对比

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

HTML详细基础(二)文件路径

目录 一.相对路径 二.绝对路径 三.超链接标签 四.锚点链接 首先&#xff0c;扩展一些HTML执行的原理&#xff1a; htmL(hypertext markup Language) 是一种规范&#xff08;或者说是一种标准&#xff09;&#xff0c;它通过标记符&#xff08;tag&#xff09;来标记要显示…...

大数据-玩转数据-Flink 海量数据实时去重

一、海量数据实时去重说明 借助redis的Set&#xff0c;需要频繁连接Redis&#xff0c;如果数据量过大, 对redis的内存也是一种压力&#xff1b;使用Flink的MapState&#xff0c;如果数据量过大, 状态后端最好选择 RocksDBStateBackend&#xff1b; 使用布隆过滤器&#xff0c;…...

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流程简述 输入分片&#xff1a; MapReduce 作业开始时&#xff0c;输入数据被分割成多个分片&#xff0c;每个分片大小一般在 16MB 到 128MB 之间。这些分片会被分配给不同的…...

C++中的四种强制类型转换符详解

前 言 C 既支持 C 风格的类型转换&#xff0c;又有自己风格的类型转换。C 风格的转换格式很简单&#xff0c;但是有不少缺点&#xff1a; 转换太过随意&#xff0c;可以在任意类型之间转换。你可以把一个指向 const 对象的指针转换成指向非 const 对象的指针&#xff0c;把一…...

Windows电脑多开器的优缺点对比

Windows电脑多开器是一种能够让用户同时运行多个应用程序、游戏或者网页的软件工具。它的作用在于让用户能够更加高效地工作、学习或者娱乐。但是&#xff0c;这种软件也存在一些优劣势的对比。 优点&#xff1a; 提升工作效率。多开器可以让用户同时打开多个应用程序或者网页…...

Java笔记六(面向对象:类与对象)

面向对象编程的本质&#xff1a;以类的方式组织代码&#xff0c;以对象的组织&#xff08;封装&#xff09;数据 抽象 三大特征&#xff1a;封装 继承 多态 从认识角度考虑是先有对象后有类。对象&#xff0c;是具体的事物。类&#xff0c;是抽象的&#xff0c;是对对象的抽…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...