APUE学习笔记(17)-线程和信号

By:             潘云登

Date:          2009-8-22

Email:         intrepyd@gmail.com

Homepage: http://blog.csdn.net/intrepyd

Copyright: 该文章版权由潘云登所有。可在非商业目的下任意传播和复制。

对于商业目的下对本文的任何行为需经作者同意。


写在前面

1.          本文内容对应《UNIX环境高级编程》(2)》第12章。

2.          总结了如何设置线程的信号屏蔽字,以及如何使用专用线程进行信号处理。

3.          希望本文对您有所帮助,也欢迎您给我提意见和建议。


线程的信号屏蔽字

每个线程都有自己的线程屏蔽字,但是信号处理函数是进程中所有线程共享的。这意味着尽管单个线程可以阻止某些信号,但当线程修改了与某个信号相关的处理行为后,所有的线程都必须共享这个处理行为的改变。

         进程中的信号是递送到单个线程的。如果信号与硬件故障或定时器超时相关,该信号就被发送到引起该事件的线程中去,而其它的信号则被发送到任意一个线程。但要注意,闹钟定时器是进程资源,并且所有的线程共享相同的alarm,所以进程中的多个线程不可能互不干扰地使用闹钟定时器。

         线程必须使用pthread_sigmask函数,替代进程使用的sigprocmask,为自己设置信号屏蔽字。两个函数的区别在于,pthread_sigmask函数在失败时返回错误码,而不像sigprocmask那样设置errno并返回-1

#include <pthread.h>

int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);

                                             Return: 0 if OK, error number on failure


sigwait

 

 

线程可以通过调用sigwait等待一个或多个信号发生。set参数指出了线程等待的信号集,signop指向的整数表明接收到的信号值。如果信号集中的某个信号在sigwait调用的时候处于未决状态,那么sigwait将无阻塞地返回,在返回之前,sigwait将从进程中移除那些处于未决状态的信号。

#include <pthread.h>

int sigwait(const sigset_t *restrict set, int *restrict signop);

                                             Return: 0 if OK, error number on failure

为了避免错误动作发生,线程在调用sigwait之前,必须阻塞那些它正在等待的信号。sigwait函数会自动取消信号集的阻塞状态,直到有新的信号被传递。在返回之前,sigwait将恢复线程的信号屏蔽字,这与sigsuspend函数类似。如果信号在sigwait调用的时候没有被阻塞,在完成对sigwait调用之前会出现一个时间窗,在这个窗口期,某个信号可能在线程完成sigwait调用之前就被递送了。

使用sigwait的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理。为了防止信号中断线程,可以把信号 加到每个线程的信号屏蔽字中,然后安排专用线程作信号处理。这个专用线程可以进行任何函数调用,不需要担心在信号处理函数中调用哪些函数是可重入的,因为这些函数调用来自正常的线程环境,能够知道在何处被中断并继续执行。

         范例程序如下:

#include "apue.h"

#include <pthread.h>

 

int         quitflag;   /* set nonzero by thread */

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);  /*signo表明接收的信号值*/

        if (err != 0)

            err_exit(err, "sigwait failed");

        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(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)

        err_exit(err, "SIG_BLOCK error");                /*设置线程信号屏蔽字*/

 

    err = pthread_create(&tid, NULL, thr_fn, 0); /*创建线程时,新线程继承信号屏蔽字*/

    if (err != 0)

        err_exit(err, "can't create thread");

 

    pthread_mutex_lock(&lock);

    while (quitflag == 0)   /*主线程并不处理SIGQUIT信号,而使用条件变量判断是否退出*/

        pthread_cond_wait(&wait, &lock);

    pthread_mutex_unlock(&lock);

 

    /* SIGQUIT has been caught and is now blocked; do whatever */

    quitflag = 0;

 

    /* reset signal mask which unblocks SIGQUIT */

    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)

        err_sys("SIG_SETMASK error");

    exit(0);

}


给线程发送信号

要把信号发送到进程,可以调用kill。要把信号发送到线程,可以调用pthread_kill。可以传一个0值的signo来检查线程是否存在。如果信号的默认处理动作是终止该进程,那么把信号传递给某个线程仍然会杀掉整个进程。

#include <pthread.h>

int pthread_kill(pthread_t thread, int signo);

                                             Return: 0 if OK, error number on failure

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值