跨境派

跨境派

跨境派,专注跨境行业新闻资讯、跨境电商知识分享!

当前位置:首页 > 卖家故事 > 【linux 多线程并发】多线程模型下的信号通信处理,与多进程处理的比较,属于相同进程的线程信号分发机制

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

时间:2024-04-03 14:10:36 来源:网络cs 作者:峨乐 栏目:卖家故事 阅读:

标签: 处理  进程  信号  相同  机制  分发  通信  模型 
阅读本书更多章节>>>>

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 %1Signal handling thread got signal 10[senllang@hatch example_07]$ kill -USR1 %1Signal handling thread got signal 10[senllang@hatch example_07]$ kill -QUIT %1Signal handling thread got signal 3[1]+  Done                    ./a.out

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

总结

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

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

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

结尾

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

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

阅读本书更多章节>>>>

本文链接:https://www.kjpai.cn/gushi/2024-04-03/153308.html,文章来源:网络cs,作者:峨乐,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。

文章评论