每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。这意味着尽管单个线程可以阻止某些信号,
但当线程修改了与某个信号相关的处理行为以后,所有的线程都必须共享这个处理行为的改变。这样如果一个线程选择忽略某
个信号,而其他的线程可以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择。
进程中的信号是递送到单个线程的。如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去,
而其他的信号则被发送到任意一个线程。
线程使用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会接触信号阻塞状态,所以只有一个线程可以
用于信号的接收。这使得对主线程进行编码时不必但系来自这些信号的中断。