分类: 系统运维
2012-03-31 23:18:53
处理信号甚至可以用一个基于进程的范型来完成。把线程引入到系统让事情变得更复杂。
每个线程有它自己的信号掩码,但是信号布署被进程 里的所有线程共享。这意味着个体线程可以阻塞信号,但是当一个线程修改了一个给定信号的相关动作时,所有的线程都共享这个动作。因而,如果一个线程选择忽 略一个给定信号,那么另一个线程可以撤消那个选择,通过恢复默认布署或为信号安装一个信号处理机。
信号被分发给进程里的单个线程。如果信号和一个硬件错误或过期的计时器相关,那么信号被发送给其动作导致这个事件的线程。另一方面,其它信号被分发给任意一个线程。
在10.12节,我们讨论了进程如何使用sigprocmask来阻塞信号的分发。sigprocmask的行为在多线程下是无定义的。线程必须使用pthread_sigmask来替代。
pthread_sigmask函数和sigprocmask相同,除了pthread_sigmask和线程工作,并在失败时返回一个错误码而不是设置errno并返回-1。
一个线程可以等待一个或多个信号的发生,通过调用sigwait。
set参数指定了线程正在等待的信号集。在返回时,signop指向的整型将包含被分发的信号号。
如 果set里指定的某个信号在sigwait被调用时正待定,那么sigwait会返回而不被阻塞。在返回前,sigwait从进程的待定信号集里删除这个 信号。为了避免错误行为,一个线程必须在调用sigwait之前阻塞信号。sigwait函数将自动阻塞信号并等待某个被分发。在返回前,sigwait 会恢复线程的信号掩码。如果信号在sigwait被调用时没有被阻塞,那么在它线程完成它的sigwait调用前,会有时间间隙,信号可以被分发到线程。
使 用sigwait的好处是它能简化信号处理,通过允许我们用同步方式处理异步产生的信号。我们可以避免信号中断信号,通过把它们加到每个线程的信号掩码 里。然后我们可以指定特定的线程来处理信号。这些被指定的线程可以进行函数调用,而不必担心哪些函数可以在一个信号处理机里安全地调用,因为它们从普通的 线程上下文里被调用,而不是传统的中断一个普通线程执行的信号处理机。
如果多线程在sigwait调用时阻塞在同一个信号上,那么当信号被 分发时只有一个线程从sigwait返回。如果信号被捕获(例如进程使用sigaction建立了一个信号处理机)而线程正在sigwait调用里等待相 同的信号,那依赖于实现来决定如何分发信号。在这种情况下,信号可能允许sigwait返回或调用信号处理机,但不会同时。
为了发送信号给一个进程,我们调用kill(10.9)。为了发送一个信号给一个线程,我们调用pthread_kill。
我们可以传递值为0的signo来检查线程的存在性。如果一个线程的默认动作是终止这个进程,那么发送信号给一个线程仍然会杀死整个进程。
注意闹钟计时器是一个进程资源,而所有线程共享同一个闹钟集。因此,进程里的多线程无法在不影响(或合作)另一个线程的情况下使用闹钟计时器。
回想第10.16节,我们等待一个信号处理机来设置一个表示主程序应该退出的标志。仅有的可以运行的线程控制是主线程和信号处理机,所以阻塞信号足以避免标志改变的丢失。有了线程,我们需要使用一个互斥体来保护这个标准,如下面的代码所示:
注意我们在主线程开头阻塞了SIGINT和SIGQUIT。当我们创建线程来处理信号时,线程继承了当前的信号掩码。因为sigwait将反阻塞信号,只有一个线程可以收到信号。这让我们在不用担心从这些信号中断的情况下编码我们的主线程。
运行结果为:
^C
interrupt
^C
interrupt
^\$
Linux
实现线程为单独的进程,用clone共享资源。因为如此,Linux上的线程对待信号的行为和其它实现不同。在POSIX.1线程模型里,异步信号被发送
给一个进程,然后进程里的某个线程被选来收到这个信号,基于当前哪个线程没有阻塞这个信号。在Linux上,一个异步信号被发送给一个特定的线程,而因为
每个线程作为单独的进程执行,系统不能选择一个当前没有阻塞该信号的线程。结果是线程可能注意不到信号。因而,像上面的代码当信号由终端驱动产生时可以工
作,它向进程组发送信号,但是当你尝试用kill向进程发送一个信号,在Linux上它不会如预期地工作。(我的ubuntu上的Linux3.0好像可
以用kill发送信号。)