信号屏蔽字在多线程环境下的应用

1. 信号屏蔽字和sigprocmask函数


每个进程都有一个信号屏蔽字(signal mask),它规定了当前要阻塞递送到该进程的信号集。对于每个可能的信号,该屏蔽字中都有一位与之对应。

进程可以调用sigprocmask函数来获取或设置这个信号屏蔽字。



2. 信号的产生、递送、未决状态


1)首先,当引发信号的事件发生时,为进程产生(generation)一个信号(或向进程发送一个信号)。

注:事件可以是硬件异常(例如,除以0)、软件条件(例如,alarm计时器超时)、终端产生的信号或调用kill函数。


2)在产生了信号时,内核通常在进程表中设置一个某种形式的标志。当对信号采取这种动作时,我们说向进程递送(delivery)了一个信号。


3)在信号产生和递送之间的时间间隔内,称信号是未决(pending)的。



3.  阻塞信号的未决状态


如1中所说,可以调用sigprocmask函数对信号进行阻塞。

如果为进程产生了一个选择为阻塞的信号,而且对该信号的动作是默认动作或捕捉该信号,则为该进程将此信号保持为未决状态。直到该进程(a)对此信号解除了阻塞(同样通过调用sigprocmask函数),或者(b)将对此信号的动作更改为忽略

进程可以调用sigpending函数来判断哪些信号是设置为阻塞并处于未决状态的。


注:如果某个信号在阻塞期间发生了多次。在大多数UNIX系统中,并不会对信号递送多次,而只会递送一次。





总结上述知识:当一个被设置为阻塞的信号发生时,如果进程对该信号的动作是默认(SIG_DEF)或者捕捉(通过设置自定义处理函数)时,内核使得该信号保持处于未决状态,等待阻塞取消后递送;如果进程对该信号的动作是忽略,内核不会保留该信号,直接忽略。





下面来看这种机制在多线程环境中的应用:


首先需要了解的是:在多线程环境中,每个线程都有自己的信号屏蔽字,但是一个进程中的所有线程共享信号的处理

即:如果在一个线程中修改了对某个信号的处理方式,相当于修改了进程中所有线程对该信号的处理方式。



1. 线程对信号的屏蔽

sigprocmask函数只能用于单线程环境,在多线程环境中与之对应的函数是——pthread_sigmask。该函数与sigprocmask函数用法基本相同。


2. sigwait函数

线程可以通过调用sigwait函数等待一个或多个信号发生。该函数的第一个参数指定了等待的信号集合,第二个参数作为返回值,表明实际接收到的信号。

如果第一个参数指定的信号集中的某个信号在sigwait调用的时候处于未决状态,那么sigwait将无阻塞地返回(立即返回)



结合pthread_mask和sigwait函数,可以简化多线程环境中信号的处理:

为了防止某信号中断线程,可以

(1)将该信号的处理方式设置为系统默认

(2)然后加到每个线程的信号屏蔽字中(调用pthread_mask函数)

(3)安排专用线程,在该线程中调用sigwait函数等待该信号,并在该线程中对该信号进行处理(如果等待到的话)


如果进程中产生了某个信号,由于在所有线程中都对该信号进行了屏蔽,而且该信号的处理方式为系统默认,因此该信号将处于未决状态。此时专用线程调用的sigwait检测到该信号,就会立即返回,表示等待到该信号,进而进行相应的处理。


这种方法可用于实现守护进程对SIG_HUP信号的捕捉。





在多线程环境中有一个问题:如果向一个进程递送一个信号,那么该进程中的哪些线程会接收到这个信号呢?


关于这个问题,《UNIX环境高级编程》一书中是这样说的:进程中的信号是递送到单个线程的。如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去,而其他的信号则被发送到任意一个线程。



为了理解最后一句话,做了一个简单的测试:

1)在main函数中fork一个子进程

2)在子进程中设置SIGUSR1的处理方式为捕捉,并自定义处理函数sig_usr1。处理函数的操作是打印捕捉到信号的线程的线程ID和所在进程ID

3)在子进程中创建一个新的线程。新的线程与主线程共享sig_usr1

4)子进程的主线程和新线程中都调用pause函数等待信号

5)父进程中,先睡眠3秒(给子进程设置信号处理函数和创建新线程的时间),然后向子进程发送SIGUSR1


运行的结果是:总是主线程接收到该信号,而新线程不会接收到信号。(不晓得是不是测试次数比较少的原因。。。)



对上述程序做一点修改:

1)在子进程的主线程中屏蔽SIGUSR1,然后不调用pause等待信号,而是睡眠10秒


a)如果新线程不做修改,仍是调用pause等待信号,那么主线程和新线程都不会捕捉到信号。子进程会随着睡眠到时正常退出。

b)如果新线程不调用pause,而是调用sigwait函数等待SIGUSR1,那么新线程会等待到函数。但是不会调用指定的信号处理函数sig_usr1,而是继续执行。所以当调用sigwait函数等待信号时,需要在等待到之后重新编写关于信号的相应操作

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值