当线程被创建时,它会继承进程的信号掩码,这个掩码就会变成线程私有的,所以我们可以设置进程的信号掩码,使其在当前进程创建的线程都会屏蔽信号。多个线程是共享进程的地址空间,每个线程对信号的处理函数是相同的,即如果某个线程修改了与某个信号相关的处理函数后,所在进程中的所有线程都必须共享这个处理函数的改变。这样如果一个线程选择忽略某个信号,而其他的线程可以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择,即后来线程的处理设置会覆盖前者线程的处理设置。
每个信号只会被传递给一个线程,即进程中的信号是传递到单个线程的,传递给哪个线程是不确定的。如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去。但是alarm 定时器是所有线程共享的资源,所以在多个线程中同时使用alarm 还是会互相干扰。
在进程中可以调用 sigprocmask 来阻止信号发送,但在多线程的进程中它的行为并没有定义,它可以不做任何事情。在主线程中调用pthread_sigmask 使得所有线程都阻塞某个信号,也可以在某个线程中调用它来设置自己的掩码。
/* 线程与信号 */
/*
* 函数功能:设置线程的信号屏蔽字;
* 返回值:若成功则返回0,否则返回错误编码;
* 函数原型:
*/
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
/*
* 说明:
* 该函数的功能基本上与前面介绍的在进程中设置信号屏蔽字的函数sigprocmask相同;
*/
/*
* 函数功能:等待一个或多个信号发生;
* 返回值:若成功则返回0,否则返回错误编码;
* 函数原型:
*/
int sigwait(const sigset_t *set, int *signop);
/*
* 说明:
* set参数指出线程等待的信号集,signop指向的整数将作为返回值,表明发送信号的数量;
*/
/*
* 函数功能:给线程发送信号;
* 返回值:若成功则返回0,否则返回错误编码;
* 函数原型:
*/
int pthread_kill(pthread_t thread, int signo);
/*
* 说明:
* signo可以是0来检查线程是否存在,若信号的默认处理动作是终止整个进程,那么把信号传递给某个线程仍然会杀死整个进程;
*/
如果信号集中的某个信号在 sigwait 调用的时候处于未决状态,那么 sigwait 将立即无阻塞的返回,在返回之前, sigwait 将从进程中移除那些处于未决状态的信号。为了避免错误动作的发生,线程在调用 sigwait 之前,必须阻塞那些它正在等待的信号。 sigwait 函数会自动取消信号集的阻塞状态,直到新的信号被递送。在返回之前, sigwait 将恢复线程的信号屏蔽字。
测试程序:
#include "apue.h"
#include <pthread.h>
#include <signal.h>
int quitflags;
sigset_t mask;
//初始化互斥量、条件变量
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t wait = PTHREAD_COND_INITIALIZER;
void *thr_fun(void *arg);
int main(void)
{
int err;
sigset_t oldmask;
pthread_t tid;
//初始化信号集,添加两个信号SIGINT、SIGQUIT
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
//在主线程设置信号屏蔽字,使得所以线程都阻塞信号集的信号
err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask);
if(err != 0)
err_quit("SIG_BLOCK error: %s\n", strerror(err));
//创建新的线程
err = pthread_create(&tid, NULL, thr_fun, 0);
if(err != 0)
err_quit("can't create thread: %s\n", strerror(err));
//对主线程进行加锁
pthread_mutex_lock(&lock);
//等待条件变量为真
while(quitflags == 0)
pthread_cond_wait(&wait, &lock);
//对主线程解锁操作
pthread_mutex_unlock(&lock);
quitflags = 0;
//打开信号屏蔽字
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
exit(0);
}
void *thr_fun(void *arg)
{
int err, signo;
for(; ;)
{
//在新建线程中等待信号发生
err = sigwait(&mask, &signo);
if(err != 0)
err_quit("sigwait error: %s\n", strerror(err));
switch(signo)
{
case SIGINT:
printf("\ninterrupt\n");
break;
case SIGQUIT:
pthread_mutex_lock(&lock);
quitflags = 1;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&wait);
return(0);
default:
printf("unexpected signal %d\n", signo);
exit(1);
}
}
}
输出结果:
^C
interrupt
^C
interrupt
^C
interrupt
^\
参考资料:
《UNIX高级环境编程》