我们在线程基础中说过,每个线程都有自己的signal mask(信号屏蔽字),也就是单个线程可以筛选某些信号。
thread中的信号处理very similar to 进程中的信号处理,比如:
(1)pthread_sigmask vs sigprocmask
不过需要注意的是,pthread_sigmask失败时直接返回错误码;而sigprocmask失败时设置errno并返回-1;
(2)pthread_kill vs kill
还记得吗?“kill is not kill”,发送信号。
(3)sigwait vs sigsuspend
这两个看起来很突兀,对应到书上是12-16(线程)与10-23(进程)。sigsuspend用在process中,是suspend一个进程并等待一个信号的到来;sigwait用在线程中,在12-16中,我们不用signal handler中断main thread(10-23中使用其中断进程的),而是有专门的thread来处理信号。关于signalwait的使用细节,可以看这里:【http://man7.org/linux/man-pages/man3/sigwait.3.html】【http://blog.youkuaiyun.com/wozaiwogu/article/details/4361456】。
好啦,我们看代码以详细了解线程的信号处理:
#include "apue.h"
#include <pthread.h>
int quitflag;
sigset_t mask;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t waitloc = PTHREAD_COND_INITIALIZER;
void *
thr_fn(void *arg)
{
int err, signo;
for(; ;)
{
err = sigwait(&mask, &signo);
if(err != 0)
err_exit(err, "sigwait failed!");
switch(signo)
{
case SIGINT:
printf("\n interrupt\n");
break;
case SIGQUIT:
pthread_mutex_lock(&lock);
quitflag = 1;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&waitloc);
return(0);
default:
printf("unexpected signal\n");
exit(1);
}
}
}
int
main(void)
{
int err;
sigset_t oldmask;
pthread_t tid;
/* Set the signal set */
sigemptyset(&oldmask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
if((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
err_exit(err, "SIG_BLOCK error");
/* Create a new thread */
err = pthread_create(&tid, NULL, thr_fn, 0);
if(err !=0)
err_exit(err, "can't create thread");
/* using mutex and condition variables to handle a signal */
pthread_mutex_lock(&lock);
while(quitflag == 0)
pthread_cond_wait(&waitloc, &lock);
pthread_mutex_unlock(&lock);
/* SIGQUIT has been caught and is now blocked; do whatever */
quitflag = 0;
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
exit(0);
}
本程序中使用了条件变量来进行同步信号处理,处理的信号包括SIGINT(ctrl+c)和SIGQUIT(ctrl+\);接到SIGINT时输出提示,直到接到SIGQUIT时退出。我们主要看main函数,注意以下几点:
(1)signal set 和 signal mask的使用
以前说过,有两个信号是不能被忽略的:SIGSTOP和SIGKILL,原因是它们给kernel或superuser提供了可靠的停止或终止方法;
(2)还记不记得condition variable的使用“模板”?
case SIGQUIT 和 main中的交互处理方式;
(3)sigwait、pthread_sigmask与sigprocmask的使用。
下面是长长的分割线~
————————————————————————————————————————
好啦,关于线程,我们就讲那么多,下面看deamon!