【linux 多线程并发】多线程模型下的信号通信处理,与多进程处理的比较,属于相同进程的线程信号分发机制

07线程信号处理

专栏内容

  • 参天引擎内核架构
    本专栏一起来聊聊参天引擎内核架构,以及如何实现多机的数据库节点的多读多写,与传统主备,MPP的区别,技术难点的分析,数据元数据同步,多主节点的情况下对故障容灾的支持。

  • 手写数据库toadb
    本专栏主要介绍如何从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让大家能跟上并且可以一起开发,让每个需要的人成为参与者。
    本专栏会定期更新,对应的代码也会定期更新,每个阶段的代码会打上tag,方便阶段学习。

开源贡献

  • toadb开源库

个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

文章目录

  • 07线程信号处理
  • 前言
  • 概述
  • 线程信号处理流程
    • 进程信号的处理
    • 线程信号的处理
  • 信号阻塞掩码设置
    • 参数取值说明
  • 发送信号
  • 信号等待
  • 代码案例
  • 总结
  • 结尾

前言

现代的CPU都是多core处理器,而且在intel处理器中每个core又可以多个processor,形成了多任务并行处理的硬件架构,在服务器端的处理器上架构又有一些不同,传统的采用SMP,也就是对称的多任务处理架构,每个任务都可以对等的访问所有内存,外设等,而如今在ARM系列CPU上,多采用NUMA架构,它将CPU核分了几个组,给每个组的CPU core分配了对应的内存和外设,CPU访问对应的内存和外设时速度最优,跨组访问时性能会降底一些。

随着硬件技术的持续发展,它们对一般应用的性能优化能力越来越强,同时对于服务器软件的开发,提出更高要求,要想达到极高的并发和性能,就需要充分利用当前硬件架构的特点,对它们进行压榨。那么,我们的应用至少也是要采用多任务架构,不管是多线程还是多进程的多任务架构,才可以充分利用硬件的资源,达到高效的处理能力。

当然多任务框架的采用,不仅仅是多线程的执行,需要对多任务下带来的问题进行处理,如任务执行返回值获取,任务间数据的传递,任务执行次序的协调;当然也不是任务越多处理越快,要避免线程过多导致操作系统夯住,也要防止任务空转过快导致CPU使用率飙高。

本专栏主要介绍使用多线程与多进程模型,如何搭建多任务的应用框架,同时对多任务下的数据通信,数据同步,任务控制,以及CPU core与任务绑定等相关知识的分享,让大家在实际开发中轻松构建自已的多任务程序。

概述

信号是linux平台下一个重要的并发通信方式,对于简单的指令可以非常方便的通知到另外的并发任务,同时利用了软中断的机制,让信号的接收变得很高效。

我们知道信号一般针对的都是进程,NTPL线程库和C标准库给我们提供了一些API,可以在线程级别发送和接收信号,同时可以控制信号阻塞状态。

线程级别的信号处理API分为以下几类:

  • 线程信号阻塞状态控制 pthread_sigmask
  • 线程信号发送 pthread_kill
  • 信号等待处理sigwait,sigtimedwait,sigwaitinfo

线程信号处理流程

信号从产生到处理掉,整个过程可能需要花费很长时间,也可能非常快,中间会经过几个过程:

  • 信号产生,也就是发送信号成功;
  • 信号投递,也就是信号被传递给了信号接收者;
  • 信号等待处理,信号接收者将信号放入等待处理队列;如果是非实时信号,队列中只有一个相同信号,实时信号多个相同信号都会入队;如果接收者选择忽略该信号,则信号被丢弃;
  • 信号处理,接收者调用信号处理函数处理信号;这里的信号处理函数可以是用户设置的,也可以是系统默认的;

在这几个过程中,中间两个过程认为是信号未决,等待处理队列可以认为是未决队列,当我们阻塞某个信号时,它就会一直在等待队列中,直到信号阻塞状态取消才会调用信号处理函数。

那么涉及信号处理的,就有三个内容:

  • 信号掩码
  • 信号未决队列
  • 信号处理函数

下面我们来看一下进程与线程信号处理流程,以及它们的不同点。

进程信号的处理

进程会有一个信号掩码(屏蔽字,其中的信号会被阻塞)信号一般会先到达进程,然后再分发到进程所属的线程,当然分发给谁,这就很难确定。

信号未决队列,进程会有一个,它里面会有所有到达的信号;

信号处理函数的设置,也是进程级别,也就是说信号处理函数只有一个,不管那个线程修改了,进程所属的所有线程都会改变。

线程信号的处理

对于线程,它是好像是下级部门,指令下达就有点困难。
信号掩码和信号未决队列,这两个每个线程都会有独立的一份,各线程可以自己设置,默认是从创建者线程继承而来,在线程创建时,被创建的线程的信号未决队列会被初始化为空。

如前面所说,信号处理函数没有线程独立的。

线程的困难就来了,对于信号是否能收到,在进程收到信号时,会分发给不阻塞该信号的其中一个线程,不确定会发给那个线程,对于线程来讲,难啊。

线程也有一个优势,就是获取的信号未决队列,是当前线程的未决列表与进程队列的未决列表的并集。

线程级的信号处理,一般采用将它需要处理的信号阻塞住,进程级的阻塞,这样所有线程都不会处理该信号,然后线程就可以从未决队列检查是否有自己想要的信号了。

信号阻塞掩码设置

#include <signal.h>
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);

通过此函数可以设置线程的信号阻塞掩码值,它与进程信号设置函数sigprocmask功能和使用方法一样,只是应用对象不同,一个是多线程下的,后者是多进程并发。

参数取值说明

how 的取值如下:

  • SIG_BLOCK , set 信号集为阻塞信号,加入到原来的阻塞信号列表中,也就是增加set中的信号到阻塞信号列表中;
  • SIG_UNBLOCK , 在阻塞信号列表中移除set中设置的信号,也就是将set中的信号解除阻塞;当然对于当前没有阻塞的信号,不能进行解除阻塞;
  • SIG_SETMASK , 将阻塞信号列表替换为 set中的信号列表,也就是将阻塞信号列表设置为set中的信号;

set 是当前需要设置的信号列表;

oldset 返回旧的信号列表,可以为NULL,则不接收旧的信号列表;

而信号集的设置, 有一套函数如下案例中的sigemptyset 清空集合, sigaddset 添加信号到集合。

发送信号

向进程发送信号的函数是kill,但是它会向进程内的所有线程都会发送信号;

给某一指定线程发送信号,在NPTL线程库中定义了专门的函数。

#include <signal.h>
int pthread_kill(pthread_t thread, int sig);

参数也非常明确,线程的标识符和需要发送的信号ID;

这里需要特别注意:

  • 只能向当前线程所属的进程范围内的线程发送信号,当然包括自己
  • 线程必须是存在的,不存在时会有些未定义的行为

信号等待

等待信号集set中指定的信号到达,这些信号需要所有线程设置为阻塞状态,在信号未决列表中的信号都会被检测到,取出当前线程需要检测的信号。

#include <signal.h>
int sigwait(const sigset_t *restrict set, int *restrict sig);int sigwaitinfo(const sigset_t *restrict set,siginfo_t *_Nullable restrict info);int sigtimedwait(const sigset_t *restrict set,siginfo_t *_Nullable restrict info,const struct timespec *restrict timeout);

这三个函数都是C标准库提供,并不是NPTL线程库提供的,所以它们的函数命名不带pthread。

当信号没有到达时,会处于阻塞状态;当信号到达时,第一个函数会返回信号数字值,而后两个会返回信号的信号结构siginfo_t;

与前两个不同的时,第三个函数可以指定超时时间,当信号未到达,又到了超时时间时,返回错误-1,此时errno为EAGAIN;

  • 参数说明

set ,信号信,设置了要等待的信号;

代码案例

通过一个例子来看一下。

/* * created by senllang 2024/1/4 * mail : study@senllang.onaliyun.com ** Copyright (c) 2023-2024 senllang* This is licensed under Mulan PSL v2.* You can use this software according to the terms and conditions of the Mulan PSL v2.* You may obtain a copy of Mulan PSL v2 at:* http://license.coscl.org.cn/MulanPSL2* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.* See the Mulan PSL v2 for more details.* */#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>static void *
sig_thread(void *arg)
{sigset_t *set = arg;int s, sig;for (;;){s = sigwait(set, &sig);if (s != 0)perror("sigwait");printf("Signal handling thread got signal %d\n", sig);if(sig == SIGQUIT)break;}
}int main(int argc, char *argv[])
{pthread_t thread;sigset_t set;int s;void *res;/* Block SIGQUIT and SIGUSR1; other threads created by main()will inherit a copy of the signal mask. */sigemptyset(&set);sigaddset(&set, SIGQUIT);sigaddset(&set, SIGUSR1);s = pthread_sigmask(SIG_BLOCK, &set, NULL);if (s != 0)perror("pthread_sigmask");s = pthread_create(&thread, NULL, &sig_thread, &set);if (s != 0)perror("pthread_create");s = pthread_join(thread, &res);if (s != 0)perror( "pthread_join");return 0;    
}

这段程序将信号SIGQUITSIGUSR1添加到阻塞信号列表中,之后这两个信号到达进程时会被阻塞在信号队列中,同时它创建的线程也会继承这个信号掩码。

阻塞信号的意思是,线程收到了,只是将它放入信号未决队列中不调用处理函数,为了检测是否线程收到了这两个信号,我们在子线程中通过信号集进行检测,当收到我们发出的信号时进行打印,当收到SIGQUIT时,退出线程。

执行结果如下:

[senllang@hatch example_07]$ gcc -lpthread threadSignalSetmask.c 
[senllang@hatch example_07]$ ./a.out &
[1] 2560005
[senllang@hatch example_07]$ kill -USR1 %1
Signal handling thread got signal 10
[senllang@hatch example_07]$ kill -USR1 %1
Signal handling thread got signal 10
[senllang@hatch example_07]$ kill -QUIT %1
Signal handling thread got signal 3
[1]+  Done                    ./a.out

可以看到主线程和子线程收到这两个信号后,都没有反应,被阻塞了,而我们的信号集检测发现确实收到了这两个信号;
kill 命令是给进程发达了信号,该信号会给当前进程的所有线程都会发送相同信号,所以主线程和子线程都会收到。

总结

在目前线程信号实现的机制下,多线程的信号处理,信号处理会在单独的某个线程进行。比如进程退出时,也是由进程给每一个线程再发送退出信号,多线程的实现方法与此类似。

在具有事件循环的应用中,在信号的的 handler 中,可以将信号直接放入程序的队列中,立刻返回。这样直到线程从程序的队列中取出这个信号为止,整个线程看起来就像没有“中断”。

本文所涉及的代码已经上传到工程hatchCode, 在multipleThreads/example_07目录下;

结尾

非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!

作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/629454.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git基础知识

简述 git 的安装配置、工作区域划分、文件类型、基本命令。 基础安装与配置 基于 WSL 的 Ubuntu 下的 git 打开或关闭Windows功能->Hyper-V、Virtual Machine Platform、Windows Subsystem for Linux # 1.必须运行 Windows 10 版本 2004 及更高版本&#xff08;内部版本 …

matplotlib绘制动态瀑布图

绘制瀑布图思路&#xff1a;遍历指定文件目录下所有的csv文件&#xff0c;每读一个文件&#xff0c;取文件前20行数据进行保存&#xff0c;如果超过规定的行数300行&#xff0c;将最旧的数据删除&#xff0c;仅保留300行数据进行展示。 网上找的大部分绘制瀑布图的代码&#x…

Spring MVC(三) 国际化

SpringMVC 国际化 1、添加相关依赖2、配置MessageSourceBean方式一&#xff1a;ReloadableResourceBundleMessageSource方式二&#xff1a;ResourceBundleMessageSource 3、添加消息资源文件英文 messages_en.properties中文 messages_zh_CN.properties默认 messages.propertie…

Visual Studio 2022 成功配置QT5.12.10

目录 下载并安装Visual Studio 2022 Qt5.12.10下载 Qt5.12.10安装 Qt VS Tools for Visual Studio 2022下载 Visual Studio 2022配置 测试 下载并安装Visual Studio 2022 下载社区版并安装&#xff0c;这个比较快。 Qt5.12.10下载 官网下载很慢&#xff0c;还不如百度网…

LLM:Training Compute-Optimal Large Language Models

论文&#xff1a;https://arxiv.org/pdf/2203.15556.pdf 发表&#xff1a;2022 前文回顾&#xff1a; OpenAI在2020年提出《Scaling Laws for Neural Language Models》&#xff1a;Scaling Laws(缩放法则&#xff09;也一直影响了后续大模型的训练。其给出的结论是最佳计算效…

day20【LeetCode力扣】142.环形链表Ⅱ

day20【LeetCode力扣】142.环形链表Ⅱ 1.题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了…

STM32 TIM输出比较、PWM波形

单片机学习&#xff01; 目录 一、输出比较简介 二、PWM简介 三、输出比较通道 3.1通用定时器的输出比较部分电路 3.2高级定时器的输出比较部分电路 四、输出模式控制器 五、PWM基本结构 六、PWM参数计算 总结 前言 文章讲述STM32定时器的输出比较功能&#xff0c;它主…

Windows Server 2019配置DNS服务器

正文共&#xff1a;1234 字 31 图&#xff0c;预估阅读时间&#xff1a;1 分钟 我们在给Windows Server添加角色和功能时&#xff0c;会发现有一项“远程桌面服务安装”&#xff0c;它的介绍为“为虚拟桌面基础结构&#xff08;Virtual Desktop Infrastructure&#xff0c;VDI&…

RPC原理介绍与使用(@RpcServiceAnnotation)

Java RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种用于实现分布式系统中不同节点之间通信的技术。它允许在不同的计算机或进程之间调用远程方法&#xff0c;就像调用本地方法一样。 ** 一.Java RPC的原理如下&#xff1a; ** 定义接口&…

Angular系列教程之MVC模式和MVVM模式

文章目录 MVC模式MVVM模式MVC与MVVM的区别Angular如何实现MVVM模式总结 在讨论Angular的时候&#xff0c;我们经常会听到MVC和MVVM这两种设计模式。这两种模式都是为了将用户界面(UI)和业务逻辑分离&#xff0c;使得代码更易于维护和扩展。在这篇文章中&#xff0c;我们将详细介…

介绍一个强大的免费开源.net反编译工具

dnSpy dnSpy 是一个用C#开发&#xff0c;开源的调试器和.NET 汇编编辑器。 即使您没有任何可用的源代码&#xff0c;也可以使用它来编辑和调试程序&#xff0c;并可以把代码导出成.net工程。

高并发服务器-多进程

B站就业班视频-对应52课 28_哔哩哔哩_bilibili 上一篇文章&#xff0c;如果再多开一个终端&#xff08;客户端&#xff09;&#xff0c;发送小写字母&#xff0c;想转换成大写&#xff0c;就没有反应了&#xff0c;关闭这个客户端&#xff0c;服务器也没有丝毫反应&#xff0…

Spring Boot入门

SpringBoot介绍 什么是SpringBoot Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其中“Boot”的意思就是“引导”&#xff0c;Spring Boot 并不是对 Spring 功能上的增强&#xff0c;而是提供了一种快速开发 Spring应用的方式。 特点 • 嵌入的 Tomcat&#xff0c…

01 SpringBoot3快速入门

本次使用3.0.5版本 SpringBoot整合了之前的一切。 需求&#xff1a;浏览器发送/hello请求&#xff0c;返回"Hello,Spring Boot 3!" 总体开发步骤&#xff1a; 1. 创建Maven工程 2. 添加依赖(springboot父工程依赖 , web启动器依赖) 3. 编写启动引导类(springboot项…

速通——决策树(泰坦尼克号乘客生存预测案例)

一、决策树 1、概述 树中每个内部节点表示一个特征上的判断&#xff0c;每个分支代表一个判断结果的输出&#xff0c;每个叶子节点代表一种分类结果 2、建立过程 1. 特征选择&#xff1a;选取有较强分类能力的特征。 2. 决策树生成&#xff1a;根据选择的特征生成决策树。 3.…

Gazebo的模型下载。

git clone zouxu634866/gazebo_modelshttps://gitee.com/zouxu6348660/gazebo_models.git&#xff0c;并完成路径配置。 &#xff08;本文提供了gitee下载&#xff0c;国外的Github下载较慢。&#xff09;

芯课堂 | 华芯微特MCU在PCB板级设计中对ISP引脚的应用

1.应用描述 ISP&#xff08;In System Programming&#xff09;&#xff0c;在系统编程&#xff0c;使用片内驻留出厂引导程序&#xff08;BootROM&#xff09;配合UART / SPI等外设进行烧录。 华芯微特全系MCU的ISP操作说明&#xff1a;当芯片上电后检测到 ISP 引脚持续 5ms…

openssl3.2 - xx_fetch函数参数名称字符串有效值列表

文章目录 openssl3.2 - xx_fetch函数参数名称字符串有效值列表概述笔记xx_fetch函数所在的头文件目录xx_fetch函数所在的头文件列表xx_fetch函数列表每个xx_fetch()API的字符串名称的有效值列表OSSL_DECODER *OSSL_DECODER_fetch();OSSL_ENCODER *OSSL_ENCODER_fetch();EVP_CIP…

LLaMa2 Chat gpt 大模型本地部署初体验

一直想在自己电脑或者测试环境随便找台服务器尝试部署一下“大模型”&#xff0c;但“大模型”对于内存和GPU的要求令人望而却步&#xff0c;层出不穷的各种术语也令人困惑&#xff0c;有点难以下手。 经过一段时间&#xff0c;在百度千帆大模型平台、讯飞星火大模型平台、魔搭…

贪心算法part04 算法

贪心算法part04 算法 ● 860.柠檬水找零 ● 406.根据身高重建队列 ● 452. 用最少数量的箭引爆气球 1.leetcode 860.柠檬水找零 https://leetcode.cn/problems/lemonade-change/description/ class Solution {public boolean lemonadeChange(int[] bills) {//看能不能找零/…