Chinaunix首页 | 论坛 | 博客
  • 博客访问: 993217
  • 博文数量: 200
  • 博客积分: 5011
  • 博客等级: 大校
  • 技术积分: 2479
  • 用 户 组: 普通用户
  • 注册时间: 2008-06-27 15:07
文章分类

全部博文(200)

文章存档

2009年(12)

2008年(190)

我的朋友

分类:

2008-12-16 10:22:50

12.8 thread and signals

(一)基础

Signal本身使进程较为复杂,在进城中加入了thread后,就更复杂。

1.每个thread都有自己的signal mask,这是从main thread继承过来的。如果main thread将一些信号给block了,那么之后生成的thread也会将这些信号给block。个人测试得出:在thread里,调用pthread_sigmasksigprocmask的效果是一样的。在一个thread里面更改signal mask只会影响本thread

2Thread之间共享signalhandler,即他们没有各自的handler。一个thread修改了一个signalhandler,会影响到别的threadhandler

3.在我的suse机子上,各个threadpid是一样的。

(二)信号分配原则:

1.如果一个thread触发了hardware fault,或者expired timersignal会发送给这个thread,否则其他的信号就发给其进程,在进程的所有thread中选择一个thread来接受该信号。

2.在thread之间进行选择的时候,会选择当前没有将该信号blockthread。如果有多个thread没有block该信号,那么取决于implementation

3.书上说,linux的各个thread是通过clone实现的,其本质是独立的进程,只不过共享了数据,所以 各个threadgetpid的返回结果也不一样。因此你如果靠kill发送一个信号给一个进程,妄图在该进程的各个thread中选择是不行的,因为你仅仅是将信号发给了该进程,其他进程并没有受到这个信号, linux不能在其他进程里选择一个进程来处理该信号。但如果你是使用controllin terminal发送的信号,由于它会将信号发送给一个process group,所以接受进程及其threads(其实也是进程)都会受到信号,那么就可以依据哪个thread将信号unblock了而选择哪个thread来处理该信号。

然而实际上,在我的测试环境里:一个进程建立的threadpid与其父进程是一样的。并且,所以,我只能向该进程的pid,发送信号,而且,我使用kill -USR1 pid来发送信号,给该进程,该进程可以在它的若干个thread里面选择一个来对本信号进行响应,注意,只选择一个thread响应一次。这样看来,我现在使用的linux版本以及pthread库已经使他的threadsignal的语义与标准的posix语义保持了一致了。

下面是我使用的测试代码(使用sigprocmask/pthread_sigmask)是等效的:

#include

#include

#include

#include

#include

 

void handler( int signo )

{

       pthread_t t = pthread_self();

       printf("Handler called in thread %d\n", (unsigned long)t);

}

 

void* thread( void* para )

{

       pthread_t t = pthread_self();

       printf("thread %d, pid:%d \n", (unsigned long)t, getpid() );

       printf("para == %d\n", *((int*)para));

 

       //我们靠参数para取值的不同来讲其中一个threadSIGUSR1unblock掉。

       if( *((int*)para) == 1 )

       {

              sigset_t newset, oldset;

              sigemptyset(&newset);

              sigaddset(&newset, SIGUSR1);

              printf("thread %d unblocks SIGUSR1\n", t);

              pthread_sigmask( SIG_UNBLOCK, &newset, NULL );

              sigprocmask( SIG_UNBLOCK, &newset, NULL );

 

       }

      

       sleep(30);

       printf("thread %d waked up\n", (unsigned long)t);

       pthread_exit(0);

 

}

 

int main()

{

       pid_t pid = getpid();

       printf("main thread pid:%d\n", pid );

      

       struct sigaction oldact, newact;

       newact.sa_handler = handler;

       if( sigaction( SIGUSR1, &newact, &oldact )<0 )

              exit(1);

      

       //main thread block SIGUSR1

       sigset_t newset, oldset;

       sigemptyset(&newset);

       sigaddset(&newset, SIGUSR1);

       printf("main thread will block SIGUSR1\n");

//     sigprocmask( SIG_BLOCK, &newset, &oldset);

       pthread_sigmask( SIG_BLOCK, &newset, &oldset);

      

       //create threads

       pthread_t t1,t2;

       void *st1, *st2;

       int para0 = 0, para1 = 1;

       if( pthread_create(&t1, NULL, thread, (void*)¶0)<0 )

       {

              puts("thread create error");

              exit(1);

       }

       if( pthread_create( &t2, NULL, thread, (void*)(¶1))<0 )

       {

              puts("thread create error");

              exit(1);

       }

       if( pthread_join( t1, &st1 )<0 )

       {

              puts("thread 1 wait error");

              exit(1);

       }

       if( pthread_join( t2, &st2 )<0 )

       {

              puts("thread 2 wait error");

              exit(1);

       }

       printf("thread 1 returns %d\n", st1 );

       printf("thread 2 returns %d\n", st2 );

//     sigprocmask( SIG_SETMASK, &oldset, NULL );

       pthread_sigmask( SIG_SETMASK, &oldset, NULL );

 

       return 0;

}

结果正如我们预料:

[shaoting@serverbj6:~]$ main thread pid:26790

main thread will block SIGUSR1

thread 1084229952, pid:26790

para == 0

thread 1094719808, pid:26790

para == 1

thread 1094719808 unblocks SIGUSR1

kill -USR1 26790

[shaoting@serverbj6:~]$ Handler called in thread 1094719808

thread 1094719808 waked up

thread 1084229952 waked up

thread 1 returns 0

thread 2 returns 0

 

[1]+  Done                    ./a.out

1.我们的3threadpid都是26790

2.首先,main threadSIGUSR1 block了,然后thread 1084229952同样也是继承了main thread的特性,默认将其block, thread 1094719808手动unblockSIGUSR1

3. 我们发送SIGUSR1信号到该进程,导致了thread 1094719808被打断,handler被调用,在handler内部调用pthread_self得到的结果是1094719808

 

(三)sigwait函数

这个函数可以让你的thread以指定的signal mask进入block状态,等待某些信号的发生,当该信号发生后,会将该信号值得到,并且该信号的handler并不被调用。即signal handler并不会打断我们的操作,并且我们可以捕获到该信号。其意义在于将异步的信号捕捉转换为同步的捕捉。我们可以建立一堆threads, 其他工作thread将一些信号全都给block,只让一个thread 将他们unblock,这样就只有这一个thread能够接受到该信号,这个thread使用sigwait函数等待信号的到来,等到来之后,在执行一些操作,我们调用这些操作的时候,并不是在signal handler里面,所以不会有async-signal safe的问题,所以我们只需要保证thread safe就可以了。很好。

原文:

The advantage to using sigwait is that it can simplify signal handling by allowing us to treat asynchronously-generated signals in a synchronous manner. We can prevent the signals from interrupting the threads by adding them to each thread's signal mask. Then we can dedicate specific threads to handling the signals. These dedicated threads can make function calls without having to worry about which functions are safe to call from a signal handler, because they are being called from normal thread context, not from a traditional signal handler interrupting a normal thread's execution.

下面就是书中使用这种机制的代码:

Figure 12.16. Synchronous signal handling

#include "apue.h"

#include

 

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

        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)

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

}

 

 

#include

 

int sigwait(const sigset_t *restrict set, int

 *restrict signop);

 

Returns: 0 if OK, error number on failure

 

#include

 

int pthread_sigmask(int how, const sigset_t

 *restrict set,

                    sigset_t *restrict oset);

 

Returns: 0 if OK, error number on failure

 

#include

 

int pthread_kill(pthread_t thread, int signo);

 

Returns: 0 if OK, error number on failure

用这个函数可以向一个thread发送信号,当然只是在一个进程内部。

 

阅读(1376) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~