下面讲一个同步信号处理的程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.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;
while(1) {
err = sigwait(&mask, &signo);
if (err != 0) {
printf("sigwait failed: %s\n", strerror(err));
exit(1);
}
switch (signo) {
case SIGINT:
printf("\ninterrupt\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()
{
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("SIG_BLOCK error: %s\n", strerror(err));
exit(1);
}
err = pthread_create(&tid, 0, thr_fn, 0);
if (err != 0) {
printf("can't create thread: %s\n", strerror(err));
exit(1);
}
pthread_mutex_lock(&lock);
while (quitflag == 0)
pthread_cond_wait(&wait, &lock);
pthread_mutex_unlock(&lock);
quitflag = 0;
if (sigprocmask(SIG_SETMASK, &oldmask, 0) < 0) {
printf("SIG_SETMASK error: %s\n", strerror(err));
exit(1);
}
exit(0);
}
这里并不让信号处理程序中断主控线程,而是由专门的独立控制线程进行信号处理。改动 quitflag 的值是在互斥量的保护下进行的,这样主控线程不会在调用 pthread_cond_signal 时错失唤醒调用。在主控线程中使用相同的互斥量来检查标志的值,并且原子地释放互斥量,等待条件的发生。
注意在主线程开始时阻塞 SIGINT 和 SIGQUIT,当创建线程进行信号处理时,新建线程继承了现有的信号屏蔽字。因为 sigwait 会解除信号的阻塞状态,所以只有一个线程可以用于信号的接收。这使得对主线程进行编码时不必担心来自这些信号的中断。
下面是在终端下运行的效果:
下面是终端下发送 kill 信号时的效果:
(第一个终端运行程序,处于等待状态)
(第二个终端发送 kill 信号,首先找到程序的进程号,然后发信号)
(第一个终端的结果,三个中断信号和一个终止信号)