《unix高级环境编程》线程控制——线程与信号

        当线程被创建时,它会继承进程的信号掩码,这个掩码就会变成线程私有的,所以我们可以设置进程的信号掩码,使其在当前进程创建的线程都会屏蔽信号。多个线程是共享进程的地址空间,每个线程对信号的处理函数是相同的,即如果某个线程修改了与某个信号相关的处理函数后,所在进程中的所有线程都必须共享这个处理函数的改变。这样如果一个线程选择忽略某个信号,而其他的线程可以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择,即后来线程的处理设置会覆盖前者线程的处理设置。

        每个信号只会被传递给一个线程,即进程中的信号是传递到单个线程的,传递给哪个线程是不确定的。如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去。但是alarm 定时器是所有线程共享的资源,所以在多个线程中同时使用alarm 还是会互相干扰。

        在进程中可以调用 sigprocmask 来阻止信号发送,但在多线程的进程中它的行为并没有定义,它可以不做任何事情。在主线程中调用pthread_sigmask 使得所有线程都阻塞某个信号,也可以在某个线程中调用它来设置自己的掩码。

/* 线程与信号 */

/*
 * 函数功能:设置线程的信号屏蔽字;
 * 返回值:若成功则返回0,否则返回错误编码;
 * 函数原型:
 */
#include <signal.h>

int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
/*
 * 说明:
 * 该函数的功能基本上与前面介绍的在进程中设置信号屏蔽字的函数sigprocmask相同;
 */

/*
 * 函数功能:等待一个或多个信号发生;
 * 返回值:若成功则返回0,否则返回错误编码;
 * 函数原型:
 */
int sigwait(const sigset_t *set, int *signop);
/*
 * 说明:
 * set参数指出线程等待的信号集,signop指向的整数将作为返回值,表明发送信号的数量;
 */

/*
 * 函数功能:给线程发送信号;
 * 返回值:若成功则返回0,否则返回错误编码;
 * 函数原型:
 */
int pthread_kill(pthread_t thread, int signo);
/*
 * 说明:
 * signo可以是0来检查线程是否存在,若信号的默认处理动作是终止整个进程,那么把信号传递给某个线程仍然会杀死整个进程;
 */

        如果信号集中的某个信号在 sigwait 调用的时候处于未决状态,那么 sigwait 将立即无阻塞的返回,在返回之前, sigwait 将从进程中移除那些处于未决状态的信号。为了避免错误动作的发生,线程在调用 sigwait 之前,必须阻塞那些它正在等待的信号。 sigwait 函数会自动取消信号集的阻塞状态,直到新的信号被递送。在返回之前, sigwait 将恢复线程的信号屏蔽字。

测试程序:

#include "apue.h"
#include <pthread.h>
#include <signal.h>

int quitflags;
sigset_t mask;

//初始化互斥量、条件变量
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t wait = PTHREAD_COND_INITIALIZER;

void *thr_fun(void *arg);

int main(void)
{
    int err;
    sigset_t oldmask;
    pthread_t tid;

    //初始化信号集,添加两个信号SIGINT、SIGQUIT
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGQUIT);
    //在主线程设置信号屏蔽字,使得所以线程都阻塞信号集的信号
    err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask);
    if(err != 0)
        err_quit("SIG_BLOCK error: %s\n", strerror(err));
    //创建新的线程
    err = pthread_create(&tid, NULL, thr_fun, 0);
    if(err != 0)
        err_quit("can't create thread: %s\n", strerror(err));

    //对主线程进行加锁
    pthread_mutex_lock(&lock);
    //等待条件变量为真
    while(quitflags == 0)
        pthread_cond_wait(&wait, &lock);
    //对主线程解锁操作
    pthread_mutex_unlock(&lock);

    quitflags = 0;

    //打开信号屏蔽字
    if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
    exit(0);
}

void *thr_fun(void *arg)
{
    int err, signo;

    for(; ;)
    {
        //在新建线程中等待信号发生
        err = sigwait(&mask, &signo);
        if(err != 0)
            err_quit("sigwait error: %s\n", strerror(err));

        switch(signo)
        {
            case SIGINT:
                printf("\ninterrupt\n");
                break;
            case SIGQUIT:
                pthread_mutex_lock(&lock);
                quitflags = 1;
                pthread_mutex_unlock(&lock);
                pthread_cond_signal(&wait);
                return(0);
            default:
                printf("unexpected signal %d\n", signo);
                exit(1);
        }
    }
}

输出结果:

^C
interrupt
^C
interrupt
^C
interrupt
^\

参考资料:

《UNIX高级环境编程》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值