相比于进程中的信号处理,在线程中更加复杂,线程中的信号处理有如下特点:
- 每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。这意味着单个线程可以阻止某个信号,但是当某个线程修改了一个信号的处理行为,那么所有线程都会共享这个改变。
- 进程中的信号是递送到单个线程的。如果一个信号和硬件故障相关,那么该信号一般会被发送到引起该事件的线程中去,而其他的信号会被发送到任意的线程中。
屏蔽字修改
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
sigprocmask函数用来修改进程的信号屏蔽字,但是它只能用在进程中,要修改线程的信号屏蔽字应使用函数pthread_sigmask.
pthread_sigmask的接口和使用方式和sigprocmask相同,只不过失败时返回值不像sigprocmask那样放回-1并设置errno,而是返回错误编号。
发送信号
kill将信号发送给进程,而向线程发送信号则使用函数pthread_kill.
int pthread_kill(pthread_t thread, int signo);
可以传一个0值的signo来检查线程是否存在。如果信号的默认处理动作是终止该进程,那么把信号发送给某个线程仍会杀死整个进程。
闹钟
闹钟定时器是进程资源,所有线程共享相同的闹钟。所以,进程中的多个线程不可能互不干扰的使用闹钟定时器。
sigwait函数
#include <signal.h>
int sigwait(const sigset_t *restrict set, int *restrict signop);
功能:
线程可以通过调用sigwait函数等待一个或多个信号的出现。
参数:
set为要等待的信号集,返回时,signop指向的整数表明接收到的信号值。
如果在调用该函数时,信号集set中的某个信号处于挂起状态,那么函数会无阻塞地返回。返回前,sigwait将从进程中移除处于挂起状态的信号。若系统支持排队信号,则只移除该信号,其他信号继续排队。
为避免错误行为发生,线程在调用sigwait之前,必须阻塞要等待的信号。sigwait函数会原子地取消信号集的阻塞状态,直到有新的信号被传递。再返回前,sigwait会恢复线程的信号屏蔽字。
使用sigwait的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理。为防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中。然后可以安排专用线程处理信号。这些线程可以进行函数调用,不需要担心在信号处理程序中调用哪些函数是安全地,因为这些函数调用来自正常的线程上下文,而非会中断线程正常执行的传统信号处理程序。
下面是一个使用专用线程处理信号的例子,参考自《APUE》12.8节。
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
int quitflag;
sigset_t mask;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t waitloc = PTHREAD_COND_INITIALIZER;
void *pth_fn(void *args)
{
int signo;
while(1){
sigwait(&mask, &signo);
switch(signo){
case SIGINT:
printf("\ninterrupt\n");
break;
case SIGQUIT:
pthread_mutex_lock(&lock);
quitflag = 1;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&waitloc);
return 0;
default:
printf("unexpected signal %d\n", signo);
exit(0);
}
}
}
int main()
{
sigset_t oldmask;
pthread_t tid;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
pthread_create(&tid, NULL, pth_fn, NULL);
pthread_mutex_lock(&lock);
while(quitflag == 0)
pthread_cond_wait(&waitloc, &lock);
pthread_mutex_unlock(&lock);
quitflag = 0;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return 0;
}
运行示例
$ ./a.out
^C
interrupt
^C
interrupt
^\$