linux多线程环境下信号处理的基本轮廓

多线程下信号处理

现在做一个测试:
假设:由父线程发送两个特定的信号到进程id,然后我们去查看线程接受信号的状态,看看会发生什么。

备注:
处理信号的线程: 只是调用sigwait()阻塞地等待信号的线程。
多个子线程: worker thread.

通过控制变量,可以观察到以下情况:

  1. 父线程与多个子线程
    父线程和子线程均不屏蔽信号。
    现象:一旦父线程向进程id发送信号,似乎总是先被父线程处理信号。其他线程无法收到信号。

  2. 父线程 + 一个信号处理线程 + 多个子线程(近水楼台先得月)
    父线程、子线程和处理线程均不屏蔽信号。
    现象:一旦父线程向进程id发送信号,似乎总是先被父线程处理信号。其他线程无法收到信号。

  3. 一个信号处理线程+多个子线程
    父线程屏蔽信号。
    现象:一旦父线程向进程id发送信号,信号似乎总是先被处理线程接受。但其他线程偶尔也能收到信号。

使用似乎,从现象上看,接受信号的优先级似乎是 信号发送的线程 > 处理信号的线程 > 其他子线程,但实际上,只要线程没有屏蔽信号,都有可能接受到相关的信号。

测试代码: 读者可根据控制条件作相应的改变。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

using namespace std;

void sig_handler(int signum)
{
    static int j = 0;
    static int k = 0;
    pthread_t  sig_ppid = pthread_self(); 
    // used to show which thread the signal is handled in.
   
    if (signum == SIGUSR1) {
        printf("thread %u, receive SIGUSR1 No. %d\n", sig_ppid, j);
        j++;
    //SIGRTMIN should not be considered constants from userland, 
    //there is compile error when use switch case
    } else if (signum == SIGRTMIN) {
        printf("thread %u, receive SIGRTMIN No. %d\n", sig_ppid, k);
        k++;
    }
}

void* worker_thread(void *)
{
    pthread_t  ppid = pthread_self();
    pthread_detach(ppid);
    
    while (1) {
        
        printf("I'm worker thread %u, I'm alive\n", ppid);
        sleep(10);
    }
}

void* sigmgr_thread(void *)
{
    sigset_t   waitset, oset;
    siginfo_t  info;
    int        rc;
    pthread_t  ppid = pthread_self();

    pthread_detach(ppid);

    printf("I am signal handle thread id: %u\n", ppid);

    sigemptyset(&waitset);
    sigaddset(&waitset, SIGRTMIN);
    sigaddset(&waitset, SIGUSR1);
    
    // 测试使用,模拟繁重的初始任务
    sleep(5);

    while (1)  {
        rc = sigwaitinfo(&waitset, &info);
        if (rc != -1) {
            printf("sigwaitinfo() fetch the signal - %d\n", rc);
            sig_handler(info.si_signo);
        } else {
            printf("sigwaitinfo() returned err: %d; %s\n", errno, strerror(errno));
        }
    }
}


int main()
{
    sigset_t bset, oset;
    int             i;
    pid_t           pid = getpid();
    pthread_t       ppid;
    
    signal(SIGUSR1, sig_handler);
    signal(SIGRTMIN, sig_handler);

    printf("main thread id: %u\n", pthread_self());
    
	if (pthread_sigmask(SIG_BLOCK, &bset, &oset) != 0)
        printf("!! Set pthread mask failed\n");
    
    sigemptyset(&bset);
    sigaddset(&bset, SIGRTMIN);
    sigaddset(&bset, SIGUSR1);
  
    // SIGUSR1 and SIGRTMIN synchronously
    pthread_create(&ppid, NULL, sigmgr_thread, NULL);
  
    // 创建5个工作线程
    for (i = 0; i < 5; i++) {
        pthread_create(&ppid, NULL, worker_thread, NULL);
    }
   
    // 屏蔽父线程对SIGUSR1、SIGRTMIN的接收
    if (pthread_sigmask(SIG_BLOCK, &bset, &oset) != 0)
        printf("!! Set pthread mask failed\n");

    // send out 50 SIGUSR1 and SIGRTMIN signals
    for (i = 0; i < 50; i++) {
        kill(pid, SIGUSR1);
        printf("main thread, send SIGUSR1 No. %d\n", i);
        kill(pid, SIGRTMIN);
        printf("main thread, send SIGRTMIN No. %d\n", i);
        sleep(10);
    }
    exit (0);
}

小总结

通过上面我们可以看出信号处理在多线程下有以下特点:
1.向进程id发出的信号,所有线程都能收到,但信号就像水管里的水一样,被不同线程依次获取,不是所有线程都能收到该信号。
2.子线程会继承父线程的信号屏蔽集与处理函数
3.各线程间的执行是独立,不受信号处理的影响,也就是说信号的到达不会将所有线程都中断。


信号安全的定义?

异步信号安全(async-signal-safe)

SIGNAL-SAFETY(7)中是这么提及

异步信号安全函数可以在signal handler中安全地调用。但绝大多数函数都是非异步信号安全的。特别的,不可重入函数普遍都不能在signal handler中安全地调用

一个函数是异步信号安全的,要么是因为它是可重入的,或是因为它在信号方面是原子的。(例如,不会被signal handler中断)

可重入(reentrant)

多次调用一个函数,不会因为调用顺序的不同,造成不同的结果。因而,多数维护一个全局变量的函数都不是可重入的。因为不同的调用顺序可能会导致全局状态被破坏,从产生非预期结果。

参考

linux可重入、异步信号安全和线程安全

signal-safety(7)

Linux 多线程应用中如何编写安全的信号处理函数

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值