《UNIX环境高级编程》笔记-线程和信号

每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。这意味着尽管单个线程可以阻止某些信号,

但当线程修改了与某个信号相关的处理行为以后,所有的线程都必须共享这个处理行为的改变。这样如果一个线程选择忽略某

个信号,而其他的线程可以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择。

进程中的信号是递送到单个线程的。如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去,

而其他的信号则被发送到任意一个线程。

线程使用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会接触信号阻塞状态,所以只有一个线程可以

用于信号的接收。这使得对主线程进行编码时不必但系来自这些信号的中断。

《UNIX环境高级编程》笔记-线程和信号

上一篇:云上太空舱:在可信的环境中,让数据安全流通


下一篇:模板优化一加入场次