每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。这意味着尽管单个线程可以阻止某些信号,
但当线程修改了与某个信号相关的处理行为以后,所有的线程都必须共享这个处理行为的改变。这样如果一个线程选择忽略某
个信号,而其他的线程可以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择。
进程中的信号是递送到单个线程的。如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去,
而其他的信号则被发送到任意一个线程。
线程使用pthread_sigmask来阻止信号发送。
#include <signal.h> int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t restrict oset); //成功则返回0,否则返回错误编号。线程可以通过调用sigwait等待一个或多个信号发生。
#include <signal.h> int sigwait(const sigset_t *restrict set, int *restrict signop); //成功则返回0,否则返回错误编号。set参数指出了线程等待的信号集,signop指向的整数将作为返回值,表明发送的信号。
如果信号集中的某个信号在sigwait调用的时候处于未决状态,那么sigwait将无阻塞地返回,在返回之前,sigwait将从
进程中移除那些处于未决状态的信号。为了避免错误动作的发生,线程在调用sigwait之前,必须阻塞那些它正在等待的信号。
sigwait函数会自动取消信号集的阻塞状态,直到新的信号被递送。在返回之前,sigwait将恢复线程的信号屏蔽字。
使用sigwait的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理。为了防止信号中断线程,可以把
信号加到每个线程的信号屏蔽字中,然后安排专用线程作信号处理。
要把信号发送到进程,可以调用kill;要把信号发送到线程,可以调用pthread_kill。
#include<signal.h> int pthread_kill(pthread_t thread, int signo); //成功则返回0,否则返回错误编号。
注意闹钟定时器是进程资源,并且所有的线程共享相同的alarm,所有进程中的多个线程不可能互不干扰地使用闹钟定时器。
实践:
#include<stdio.h> #include<pthread.h> #include<string.h> #include<signal.h> int quitflag; sigset_t mask; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t wait = PTHREAD_COND_INITIALIZER; void *thr_fn(void *arg){ int err,signo; for(;;){ err = sigwait(&mask, &signo); if(err != 0){ exit(-1); } switch(signo){ case SIGINT: printf("interrupt.\n"); break; case SIGQUIT: pthread_mutex_lock(&lock); quitflag = 1; pthread_mutex_unlock(&lock); pthread_cond_signal(&wait); return 0; default: printf("unexpected signal %d\n",signo); exit(-1); } } } int main(void){ int err; sigset_t oldmask; pthread_t tid; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); if((err = pthread_sigmask(SIG_BLOCK,&mask,&oldmask)) != 0){ printf("pthread_sigmask:%s.\n",strerror(err)); return -1; } err = pthread_create(&tid, NULL, thr_fn, 0); if(err != 0){ printf("pthread_create:%s.\n",strerror(err)); return -1; } pthread_mutex_lock(&lock); while(quitflag == 0){ pthread_cond_wait(&wait, &lock); } pthread_mutex_unlock(&lock); if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0){ perror("sigprocmask"); return -1; } return 0; }运行结果:
yan@yan-vm:~/apue$ ./a.out
^Cinterrupt.
^Cinterrupt.
^Cinterrupt.
^Cinterrupt.
^Cinterrupt.
^Cinterrupt.
^\yan@yan-vm:~/apue$
这里并不让信号处理程序中断主控线程,而是由专门的独立控制线程进行信号处理。主线程开始时阻塞SIGINT和SIGQUIT。
当创建线程进行信号处理时,新建线程继承了现有的信号屏蔽字。因为sigwait会接触信号阻塞状态,所以只有一个线程可以
用于信号的接收。这使得对主线程进行编码时不必但系来自这些信号的中断。