linux多线程信号处理

2023-05-16

在linux下,每个进程都有自己的signal mask,这个信号掩码指定哪个信号被阻塞,哪个不会被阻塞,通常用调用sigmask来处理。同时每个进程还有自己的signal action,这个行为集合指定了信号该如何处理,通常调用sigaction来处理。

使用了多线程后,便有些疑问:

信号发生时,哪个线程会收到
是不是每个线程都有自己的mask及action
每个线程能按自己的方式处理信号么

首先,信号的传递是根据情况而定的:


如果是异常产生的信号(比如程序错误,像SIGPIPE、SIGEGV这些),则只有产生异常的线程收到并处理。
如果是用pthread_kill产生的内部信号,则只有pthread_kill参数中指定的目标线程收到并处理。
如果是外部使用kill命令产生的信号,通常是SIGINT、SIGHUP等job control信号,则会遍历所有线程,直到找到一个不阻塞该信号的线程,然后调用它来处理。(一般从主线程找起),注意只有一个线程能收到。

其次,每个线程都有自己独立的signal mask,但所有线程共享进程的signal action。这意味着,你可以在线程中调用pthread_sigmask(不是sigmask)来决定本线程阻塞哪些信号。但你不能调用sigaction来指定单个线程的信号处理方式。如果在某个线程中调用了sigaction处理某个信号,那么这个进程中的未阻塞这个信号的线程在收到这个信号都会按同一种方式处理这个信号。另外,注意子线程的mask是会从主线程继承而来的。

第三个问题,因为signal action共享的问题,已经知道不能。

下面以一个例子说明:

/*threadsig.c*/
#include <signal.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void sighandler(int signo);
 
void *
thr1_fn(void *arg)
{
    struct sigaction    action;
    action.sa_flags = 0;
    action.sa_handler = sighandler;
       
    sigaction(SIGINT, &action, NULL);
    
    pthread_t   tid = pthread_self();
    int     rc;
 
    printf("thread 1 with tid:%lu\n", tid);
    rc = sleep(60);
    if (rc != 0)
        printf("thread 1... interrupted at %d second\n", 60 - rc);
    printf("thread 1 ends\n");
    return NULL;
}
 
void *
thr2_fn(void *arg)
{
    struct sigaction    action;
    pthread_t       tid = pthread_self();
    int         rc, err;
   
    printf("thread 2 with tid:%lu\n", tid);
     
    action.sa_flags = 0;
    action.sa_handler = sighandler;
       
    err = sigaction(SIGALRM, &action, NULL);
     
    rc = sleep(60);
    if (rc != 0)
        printf("thread 2... interrupted at %d second\n", 60 - rc);
    printf("thread 2 ends\n");
    return NULL;
}
 
void *
thr3_fn(void *arg)
{
    pthread_t   tid = pthread_self();
    sigset_t    mask;
    int     rc, err;
   
    printf("thread 3 with tid%lu\n", tid);
 
     
    sigemptyset(&mask); /* 初始化mask信号集 */
   
    sigaddset(&mask, SIGALRM);
    err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
    if (err != 0)
    {
        printf("%d, %s/n", rc, strerror(rc));
        return NULL;
    }
   
    rc = sleep(10);
    if (rc != 0)
        printf("thread 3... interrupted at %d second\n", 60 - rc);
    err = pthread_sigmask( SIG_UNBLOCK,&mask,NULL );
    if ( err != 0 )
    {
        printf("unblock %d, %s/n", rc, strerror(rc));
        return NULL;
    }
    
    rc = sleep(10);
    if (rc != 0)
        printf("thread 3... interrupted at %d second after unblock\n", 60 - rc);
    printf("thread 3 ends\n");
    return NULL;
 
    return NULL;
}
 
int
main(void)
{
    int     rc, err;
    pthread_t   thr1, thr2, thr3, thrm = pthread_self();
 
    printf("thread main with pid %lu\n",thrm);
    err = pthread_create(&thr1, NULL, thr1_fn, NULL);
    if (err != 0) {
        printf("error in creating pthread:%d\t%s\n",err, strerror(rc));
        exit(1);
    }
 
     
/*  pthread_kill(thr1, SIGALRM);    send a SIGARLM signal to thr1 before thr2 set the signal handler, then the whole process will be terminated*/
    err = pthread_create(&thr2, NULL, thr2_fn, NULL);
    if (err != 0) {
        printf("error in creating pthread:%d\t%s\n",err, strerror(rc));
        exit(1);
    }
     
    err = pthread_create(&thr3, NULL, thr3_fn, NULL);
    if (err != 0) {
        printf("error in creating pthread:%d\t%s\n",err, strerror(rc));
        exit(1);
    }
 
    sleep(10);
    //内部产生的信号,只有指定的线程能收到,因此要向所有线程发送
    pthread_kill(thr1, SIGALRM);
    pthread_kill(thr2, SIGALRM);
    pthread_kill(thr3, SIGALRM);
    pthread_kill(thr3, SIGALRM);
    pthread_kill(thr3, SIGALRM);
    sleep(5);
    pthread_join(thr1, NULL);   /*wait for the threads to complete.*/
    pthread_join(thr2, NULL);
    pthread_join(thr3, NULL);
    printf("main ends\n");
    return 0;
}
 
void
sighandler(int signo)
{
    pthread_t   tid = pthread_self();
     
    printf("thread with pid:%lu receive signo:%d\n", tid, signo);
    return;
}


在上面的代码中,主线程创建三个线程。线程1注册SIGINT信号(即ctrl+c) ,线程2注册SIGALRM,线程三则是先阻塞SIGALRM,然后解除阻塞。

编译后看运行结果:

xzc@xzc-HP-ProBook-4446s:~/code/test$ gcc -o threadsig threadsig.c -pthread
xzc@xzc-HP-ProBook-4446s:~/code/test$ ./threadsig 
thread main with pid 139946922108736
thread 2 with tid:139946905396992
thread 1 with tid:139946913789696
thread 3 with tid139946897004288
^Cthread with pid:139946922108736 receive signo:2
thread with pid:139946913789696 receive signo:14
thread 1... interrupted at 4 second
thread 1 ends
thread with pid:139946905396992 receive signo:14
thread 2... interrupted at 4 second
thread 2 ends
^Cthread with pid:139946922108736 receive signo:2
^Cthread with pid:139946922108736 receive signo:2
thread with pid:139946897004288 receive signo:14
thread 3 ends
main ends
xzc@xzc-HP-ProBook-4446s:~/code/test$ 


在第一行红色的地方,主线程正在sleep,我按下ctrl+c,只有主线程收到并处理了信号。说明进程会从主线程开始查找不阻塞该信号的线程来处理job control类的信号。

由于主线程sleep被打断,随后向三个线程发送了SIGALRM,线程1、2由于没有阻塞该信号,被迫从sleep中醒来,并结束进程。进程3仍在sleep。

在第二行红色的地方,线程3第一次sleep终于完成,解除了对SIGALRM的阻塞。于是马上收到被阻塞的SIGALRM(发送3次,只收到一次)。PS:请注意信号阻塞与忽略的区别。


pthread & signal 
 
pthread线程和信号


所有的异步信号发到整个进程的所有线程(异步信号如kill, lwp_kill, sigsend, kill等调用产生的都是,异步信号也称为中断),而且所有线程共享信号的处理行为(即sigaction的设置,对于同一信号的设置,某一线程的更改会影响到所有线程)。但每个线程可以有自己的mask来阻止信号的发送,所以可以通过线程对mask的设置来决定信号发送到哪个线程。设置mask的函数为:

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

此外,线程可以通过sleep(超过指定时间或调用的进程/线程捕捉到某个信号并从信号处理程序返回时,sleep返回)或者sigwait来等待一个或多个信号发生。

#include <signal.h>
int pthread_sigwait(const sigset_t *restrict set, int *restrict signop);

给进程发送信号可以调用kill,同样给线程调用信号可以使用pthread_kill

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

可以发送一个0的signo来检查线程是否存在,如果信号的默认行为是终止进程(例如SIGARLM),那么把该信号发送给某个线程会杀掉整个进程的所有线程。

另外注意ALARM是进程资源,并且所有线程共享相同的ALARM,设置一个alarm()会发送SIGARLM信号给所有线程,所以他们不可能互补干扰的使用alarm()。
 
here comes an example:
 

   /*threadsig.c*/ 
    #include <signal.h> 
    #include <pthread.h> 
    #include <stdio.h> 
     
    void sighandler(int signo); 
     
    void * 
    thr1_fn(void *arg) 
    { 
        pthread_t   tid = pthread_self(); 
        int     rc; 
     
        printf("thread 1 with tid:%u\n", tid); 
        rc = sleep(60); 
        if (rc != 0) 
            printf("thread 1... interrupted at %d second\n", 60 - rc); 
        printf("thread 1 ends\n"); 
        return NULL; 
    } 
     
    void * 
    thr2_fn(void *arg) 
    { 
        struct sigaction    action; 
        pthread_t       tid = pthread_self();   
        int         rc, err;   
       
        printf("thread 2 with tid:%u\n", tid);   
         
        action.sa_flags = 0; 
        action.sa_handler = sighandler; 
           
        err = sigaction(SIGALRM, &action, NULL); 
         
        rc = sleep(60); 
        if (rc != 0) 
            printf("thread 2... interrupted at %d second\n", 60 - rc); 
        printf("thread 2 ends\n"); 
        return NULL; 
    } 
     
    void * 
    thr3_fn(void *arg) 
    { 
        pthread_t   tid = pthread_self();   
        sigset_t    mask;   
        int     rc, err;   
       
        printf("thread 3 with tid%u\n", tid); 
     
         
        sigemptyset(&mask); /* 初始化mask信号集 */   
       
        sigaddset(&mask, SIGALRM);   
        err = pthread_sigmask(SIG_BLOCK, &mask, NULL);   
        if (err != 0)   
        {   
            printf("%d, %s/n", rc, strerror(rc));   
            return NULL;   
        } 
       
        rc = sleep(60); 
            if (rc != 0) 
                    printf("thread 3... interrupted at %d second\n", 60 - rc); 
            printf("thread 3 ends\n"); 
            return NULL; 
     
        return NULL; 
    } 
     
    int 
    main(void) 
    { 
        int     rc, err;   
        pthread_t   thr1, thr2, thr3, thrm = pthread_self(); 
     
        printf("thread main with pid %u\n", (unsigned int)thrm); 
        err = pthread_create(&thr1, NULL, thr1_fn, NULL); 
        if (err != 0) { 
            printf("error in creating pthread:%d\t%s\n",err, strerror(rc)); 
            exit(1); 
        } 
     
         
    /*  pthread_kill(thr1, SIGALRM);    send a SIGARLM signal to thr1 before thr2 set the signal handler, then the whole process will be terminated*/ 
        err = pthread_create(&thr2, NULL, thr2_fn, NULL); 
        if (err != 0) { 
            printf("error in creating pthread:%d\t%s\n",err, strerror(rc)); 
            exit(1); 
        } 
         
        err = pthread_create(&thr3, NULL, thr3_fn, NULL); 
        if (err != 0) { 
            printf("error in creating pthread:%d\t%s\n",err, strerror(rc)); 
            exit(1); 
        } 
     
        sleep(3); 
        pthread_kill(thr1, SIGALRM); 
        pthread_kill(thr2, SIGALRM); 
        pthread_kill(thr3, SIGALRM); 
        pthread_join(thr1, NULL);   /*wait for the threads to complete.*/ 
        pthread_join(thr2, NULL); 
        pthread_join(thr3, NULL); 
        printf("main ends\n"); 
        return 0; 
    } 
     
    void  
    sighandler(int signo) 
    { 
        pthread_t   tid = pthread_self(); 
         
        printf("thread with pid:%u receive signo:%d\n", tid, signo); 
        return; 
    } 
luffy@luffy-laptop:~/workspace/myapue$ ./threadsig 
thread main with pid 3557979936
thread 1 with tid:3549923072
thread 2 with tid:3541530368
thread 3 with tid3533137664
thread with pid:3549923072 receive signo:14
thread with pid:3541530368 receive signo:14
thread 2... interrupted at 3 second
thread 1... interrupted at 3 second
thread 1 ends
thread 2 ends    #then wait for 27 seconds and thread-3 ends
thread 3 ends
main ends

 
thr2设置的信号处理程序sighandler也应用到其他线程,thr3由于设置mask所有阻塞了SIGARLM信号。

 
Reference:
APUE


Linux线程信号 

1. 概念


    按照 POSIX, 异步 (外部) 信号发送到整个进程.
    所有线程共享同一个设置, 即通过 sigaction 设置的线程处置方法.
    每个线程有自己的信号掩码, 线程库根据该掩码决定将信号发送到哪个线程.
    由于Linux 线程实现上的独特性, 外部信号始终发送到特定的线程.  

2. 例子

#include <pthread.h>
#include <stdio.h>
#include <sys/signal.h>
#define NUMTHREADS 3
void sighand(int signo);
void *threadfunc(void *parm)
{
    pthread_t             tid = pthread_self();
    int                   rc;
    printf("Thread %u entered/n", tid);
    rc = sleep(30); /* 若有信号中断则返回剩余秒数 */
    printf("Thread %u did not get expected results! rc=%d/n", tid, rc);
    return NULL;
}
void *threadmasked(void *parm)
{
    pthread_t             tid = pthread_self();
    sigset_t              mask;
    int                   rc;
    printf("Masked thread %lu entered/n", tid);
    sigfillset(&mask); /* 将所有信号加入mask信号集 */
    /* 向当前的信号掩码中添加mask信号集 */
    rc = pthread_sigmask(SIG_BLOCK, &mask, NULL);
    if (rc != 0)
    {
        printf("%d, %s/n", rc, strerror(rc));
        return NULL;
    }
    rc = sleep(15);
    if (rc != 0)
    {
        printf("Masked thread %lu did not get expected results! rc=%d /n", tid, rc);
        return NULL;
    }
    printf("Masked thread %lu completed masked work/n", tid);
    return NULL;
}
int main(int argc, char **argv)
{
    int                     rc;
    int                     i;
    struct sigaction        actions;
    pthread_t               threads[NUMTHREADS];
    pthread_t               maskedthreads[NUMTHREADS];
    printf("Enter Testcase - %s/n", argv[0]);
    printf("Set up the alarm handler for the process/n");
    memset(&actions, 0, sizeof(actions));
    sigemptyset(&actions.sa_mask); /* 将参数set信号集初始化并清空 */
    actions.sa_flags = 0;
    actions.sa_handler = sighand;
    /* 设置SIGALRM的处理函数 */
    rc = sigaction(SIGALRM,&actions,NULL);
    printf("Create masked and unmasked threads/n");
    for(i=0; i<NUMTHREADS; ++i)
    {
        rc = pthread_create(&threads[i], NULL, threadfunc, NULL);
        if (rc != 0)
        {
            printf("%d, %s/n", rc, strerror(rc));
            return -1;
        }
        rc = pthread_create(&maskedthreads[i], NULL, threadmasked, NULL);
        if (rc != 0)
        {
            printf("%d, %s/n", rc, strerror(rc));
            return -1;
        }
    }
    sleep(3);
    printf("Send a signal to masked and unmasked threads/n");
     /* 向线程发送SIGALRM信号 */
    for(i=0; i<NUMTHREADS; ++i)
    {
        rc = pthread_kill(threads[i], SIGALRM);
        rc = pthread_kill(maskedthreads[i], SIGALRM);
    }
    printf("Wait for masked and unmasked threads to complete/n");
    for(i=0; i<NUMTHREADS; ++i) {
        rc = pthread_join(threads[i], NULL);
        rc = pthread_join(maskedthreads[i], NULL);
    }
    printf("Main completed/n");
    return 0;
}
void sighand(int signo)
{
    pthread_t             tid = pthread_self();
    printf("Thread %lu in signal handler/n", tid);
    return;
}


3. 打印结果

Enter Testcase - ./test
Set up the alarm handler for the process
Create masked and unmasked threads
Thread 3085065104 entered
Masked thread 3076672400 entered
Thread 3068279696 entered
Masked thread 3059886992 entered
Thread 3051494288 entered
Masked thread 3043101584 entered
Send a signal to masked and unmasked threads
Thread 3085065104 in signal handler
Thread 3085065104 did not get expected results! rc=27
Thread 3068279696 in signal handler
Thread 3068279696 did not get expected results! rc=27
Thread 3051494288 in signal handler
Thread 3051494288 did not get expected results! rc=27
Wait for masked and unmasked threads to complete
Masked thread 3076672400 completed masked work
Masked thread 3059886992 completed masked work
Masked thread 3043101584 completed masked work
Main completed
 

4. 相关函数 

sigaction(查询或设置信号处理方式)


#include<signal.h>
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);

sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。


如参数结构sigaction定义如下

struct sigaction
{
   void (*sa_handler) (int);
   sigset_t sa_mask;
   int sa_flags;
   void (*sa_restorer) (void);
}

sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数,其他意义请参考signal()。
sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号搁置。
sa_restorer 此参数没有使用。
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。

 
sigfillset(将所有信号加入此信号集)

#include<signal.h>
int sigfillset(sigset_t * set);
 
sigfillset()用来将参数set信号集初始化,然后把所有的信号加入到此信号集里。
 
sigemptyset(初始化信号集)  

#include<signal.h>
int sigemptyset(sigset_t *set);
 
sigemptyset()用来将参数set信号集初始化并清空。
 
pthread_sigmask(更改或检查调用线程的信号掩码)
 
#include <pthread.h>
#include<signal.h>
int pthread_sigmask(int how, const sigset_t *new, sigset_t *old);
 
how用来确定如何更改信号组,可以为以下值之一:

    SIG_BLOCK:向当前的信号掩码中添加new,其中new表示要阻塞的信号组。
    SIG_UNBLOCK:向当前的信号掩码中删除new,其中new表示要取消阻塞的信号组。
    SIG_SETMASK:将当前的信号掩码替换为new,其中new表示新的信号掩码。

pthread_kill(向线程发送信号)
 
#include <pthread.h>
#include<signal.h>
int pthread_kill(thread_t tid, int sig);
 
pthread_kill()将信号sig发送到由tid指定的线程。tid所指定的县城必须与调用线程在同一个进程中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux多线程信号处理 的相关文章

随机推荐

  • 用MATLAB仿真仿射队形变换(affine formation maneuver)

    文章目录 写在前面如何仿真静态编队控制构建stress matrixMATLAB求解LMI问题静态编队控制源代码 如何仿真时变轨迹和队形变换轨迹生成时变leader控制律时变轨迹和队形变换源代码 写在前面 原论文标题 xff1a Affin
  • 多智能体一致性(Consensus)中的矩阵理论(Matrix Theory)

    文章目录 写在前面一致性算法连续时间离散时间 一致性证明连续时间离散时间 矩阵理论特征值和特征向量特征多项式代数重数几何重数 总结 写在前面 最近在看一些分布式优化的文章 xff0c 但是大部分文章都是用的离散时间算法 我之前一直研究的是连
  • 【论文笔记】利用平滑度加速分布式优化——梯度跟踪法(Gradient Tracking)

    文章目录 写在前面问题描述和算法收敛性证明 写在前面 原论文 xff1a Harnessing Smoothness to Accelerate Distributed Optimization 本文是Qu 20181的笔记 xff0c 主
  • 迭代器是什么,C++ STL迭代器(iterator)用法详解

    无论是序列容器还是关联容器 xff0c 最常做的操作无疑是遍历容器中存储的元素 xff0c 而实现此操作 xff0c 多数情况会选用 迭代器 xff08 iterator xff09 来实现 那么 xff0c 迭代器到底是什么呢 xff1f
  • C++ priority_queue 用法详解

    不出所料 xff0c priority queue 容器适配器定义了一个元素有序排列的队列 默认队列头部的元素优先级最高 因为它是一个队列 xff0c 所以只能访问第一个元素 xff0c 这也意味着优先级最高的元素总是第一个被处理 但是如何
  • 数据库|SQLite编程实例---在线词典

    在线词典项目实例 1 在线词典功能分析 在线词典功能分析框图 2 服务器端和客户端流程图 服务器端流程示意图 客户端流程示意图 3 服务器端和客户端源码 服务器端源码 span class token comment dict server
  • 【力扣刷题】动态规划问题的思考与总结

    文章目录 什么是动态规划解决动态规划问题的步骤基础递推算法子问题求和爬楼梯杨辉三角 子问题最值最大子序和 二维DP数组非最优子结构 买卖股票 xff08 多状态递推 xff09 打家劫舍 xff08 分治算法 xff09 贪心算法背包问题
  • 百度2014移动研发笔试题目

    答案仅供参考 一 简答题 1 简述计算机的存储系统分为哪几个层次 xff0c 为什么这样的分层能够提高程序的执行效率 所谓存储系统的层次结构 xff0c 就是把各种不同存储容量 存取速度和价格的存储器按层次结构组成多层存储器 xff0c 并
  • 横屏layout-land失效的细节问题 以及谈谈不同设置模式的优缺点

    android configChanges 61 34 orientation keyboardHidden screenSize 34 设置之后手机可以识别横屏布局 xff0c 但是不会重建 xff0c 但是pad不识别 xff0c 头疼
  • 数学公式汇总

    高等数学公式篇 平方关系 xff1a sin 2 43 cos 2 61 1 tan 2 43 1 61 sec 2 cot 2 43 1 61 csc 2 积的关系 xff1a sin 61 tan cos cos 61 cot sin
  • KEIL ARM 6.12 compiler 编译__ASM 错误的解决方法

    1 问题 KEIL compiler 设置为 use default compiler version 5 可以正确编译以下汇编和C混合代码 xff0c 更改编译器为V6 12后不识别 ASM关键字 xff0c 并对汇编语法报错 替换为 A
  • 面试题:从给定的N个正数中选取若干个数之和最接近M

    这道题跟捞鱼问题一样 xff0c 都是刚进实验室新生培训那会儿做过的题目 xff0c 不过这个是一师姐当时找工作的面试题 如题 xff0c 并输出该子序列 测试用例 xff1a 2 xff0c 9 xff0c 5 xff0c 7 xff0c
  • 贝叶斯最优分类器

    贝叶斯常常有两个问题 xff1a xff08 1 xff09 给定训练数据 xff0c 最可能的假设是什么 xff1f xff08 2 xff09 给定训练数据 xff0c 对新实例的最可能分类是什么 xff1f 第一个问题用最大后验概率
  • 奇异值分解SVD应用—LSI/LSA

    原文 xff1a http blog csdn net abcjennifer article details 8131087 xff08 有看不懂的地方 xff0c 原文评论有点解答 xff09 潜在语义索引 xff08 Latent S
  • EAGAIN、EWOULDBLOCK、EINTR与非阻塞 长连接

    EAGAIN EWOULDBLOCK EINTR与非阻塞 长连接 EWOULDBLOCK用于非阻塞模式 xff0c 不需要重新读或者写 EINTR指操作被中断唤醒 xff0c 需要重新读 写 在Linux环境下开发经常会碰到很多错误 设置e
  • WebSocket 实战

    本文介绍了 HTML5 WebSocket 的由来 xff0c 运作机制及客户端和服务端的 API 实现 xff0c 重点介绍服务端 xff08 基于 Tomcat7 xff09 及客户端 xff08 基于浏览器原生 HTML5 API x
  • FreeRTOS系列|FreeRTOS简介

    FreeRTOS简介 1 RTOS简介 RTOS全称为 Real Time Operation System xff0c 即实时操作系统 RTOS强调的是实时性 xff0c 又分为硬实时和软实时 硬实时要求在规定的时间内必须完成操作 xff
  • makefile 编写之32 or 64位机器

    一 Makefile 判断 64位机器 ARCH 61 shell uname m BIT32 61 i686 BIT64 61 x86 64 all clean ifeq ARCH BIT64 64 echo x86 64 make Su
  • linux多行注释

    1 多行注释 xff1a 1 首先按esc进入命令行模式下 xff0c 按下Ctrl 43 v xff0c 进入列 xff08 也叫区块 xff09 模式 2 在行首使用上下键选择需要注释的多行 3 按下键盘 xff08 大写 xff09
  • linux多线程信号处理

    在linux下 xff0c 每个进程都有自己的signal mask xff0c 这个信号掩码指定哪个信号被阻塞 xff0c 哪个不会被阻塞 xff0c 通常用调用sigmask来处理 同时每个进程还有自己的signal action xf