Chinaunix首页 | 论坛 | 博客
  • 博客访问: 39194
  • 博文数量: 29
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 285
  • 用 户 组: 普通用户
  • 注册时间: 2014-12-08 13:03
个人简介

海纳百川有容乃大,壁立千仞无欲则刚。

文章分类
文章存档

2015年(17)

2014年(12)

我的朋友

分类: LINUX

2015-03-03 09:36:15

问题一:在多线程环境,如果对一个进程发送信号,信号由哪个线程处理?其他线程的工作是否会被信号处理函数中断?
答:进程中的信号是递送到单个线程的。在POSIX.1线程模型中,异步信号被发送到进程以后,进程中当前没有阻塞该信号的某个线程被选中接受信号。在信号处理函数执行过程中,只有接受信号的线程被中断,其他线程不受影响。见代码:

  1. #include <signal.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <unistd.h>

  6. typedef void Sigfunc(int);

  7. sigset_t mask;

  8. Sigfunc *
  9. signal(int signo, Sigfunc *func){
  10.         struct sigaction act, oact;

  11.         act.sa_handler = func;
  12.         sigemptyset(&act.sa_mask);
  13.         act.sa_flags = 0;
  14.         if (signo == SIGALRM) {
  15. #ifdef SA_INTERRUPT
  16.                 act.sa_flags |= SA_INTERRUPT;
  17. #endif
  18.         } else {
  19. #ifdef SA_RESTART
  20.                 act.sa_flags |= SA_RESTART;
  21. #endif
  22.         }
  23.         if (sigaction(signo, &act, &oact) < 0)
  24.                 return(SIG_ERR);
  25.         return(oact.sa_handler);
  26. }


  27. static void sig_int(int signo){
  28.     printf("\n\tSIGINT REACH\n");
  29. }

  30. void * thr_fn(void *arg){
  31.     for(;;){
  32.         printf("thread pid %u\n", pthread_self());
  33.         sleep(100000);
  34.         printf("thread(%u) sleep interrupt\n",pthread_self());
  35.     }
  36. }

  37. int main(void){
  38.     sigset_t oldmask;
  39.     int err;
  40.     void *tret;
  41.     pthread_t tid;
  42.     pthread_t tid2;

  43.     sigemptyset(&mask);
  44.     sigaddset(&mask, SIGINT);

  45.     if(signal(SIGINT,sig_int) == SIG_ERR)
  46.         perror("signal(SIGINT) error");

  47.     err = pthread_create(&tid, NULL, thr_fn, 0);
  48.     if(err != 0)
  49.         perror("Can't create thread");
  50.     
  51.     err = pthread_create(&tid2, NULL,thr_fn, 0);
  52.     if(err != 0)
  53.         perror("Can't create thread");
  54.     pthread_sigmask(SIG_BLOCK,&mask,&oldmask);
  55.     sleep(1000000);
  56.     printf("main thread sleep interrupt\n");
  57.     sleep(1000000);
  58.     printf("main thread sleep interrupt\n");
  59.     return 0;
  60. }
在Solaris10上执行的结果如下:
bash-3.2# ./thread_sig
thread pid 3
thread pid 2
^C
        SIGINT REACH
thread(2) sleep interrupt
thread pid 2

我们知道,当执行sleep的过程中,一旦接收到信号,sleep函数将被中断。以上代码,由于主线程阻塞了SIGINT信号。因此,thread pid 2 线程被中断。但是,主线程 和 thread pid 3 仍然正常工作。
注: 如果把 pthread_sigmask(SIG_BLOCK,&mask,&oldmask); 写在创建线程的前面,那么所有的线程都将阻塞 SIGINT信号。

问题二:sigwait之前为什么要阻塞所等待的信号?
书上说,“如果信号在sigwait调用的时候没有被阻塞,在完成对sigwait调用 之前会出现一个时间窗,在这个窗口期,某个信号可能在线程完成sigwait调用之前就被递送了”。但是,我在机器上(CentOS5 & Solaris)测试的结果是——如果不阻塞等待信号,那么信号处理函数将被执行,sigwait继续等待。每次都是如此。如果在sigwait之前阻塞等待信号,则信号处理函数不会被执行,sigwait被唤醒。因为,sigwait函数会自动取消信号集的阻塞状态,只到有新的信号被递送。在返回之前,sigwait将恢复线程的信号屏蔽字。见代码:

  1. #include <signal.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <unistd.h>

  6. typedef void Sigfunc(int);

  7. sigset_t mask;

  8. /* Reliable version of signal(), using POSIX sigaction(). */
  9. Sigfunc *
  10. signal(int signo, Sigfunc *func)
  11. {
  12.         struct sigaction act, oact;

  13.         act.sa_handler = func;
  14.         sigemptyset(&act.sa_mask);
  15.         act.sa_flags = 0;
  16.         if (signo == SIGALRM) {
  17. #ifdef SA_INTERRUPT
  18.                 act.sa_flags |= SA_INTERRUPT;
  19. #endif
  20.         } else {
  21. #ifdef SA_RESTART
  22.                 act.sa_flags |= SA_RESTART;
  23. #endif
  24.         }
  25.         if (sigaction(signo, &act, &oact) < 0)
  26.                 return(SIG_ERR);
  27.         return(oact.sa_handler);
  28. }

  29. static void sig_int(int signo)
  30. {
  31.     printf("\n\tSIGINT REACH\n");
  32. }

  33. void * thr_fn(void *arg)
  34. {
  35.     int err, signo;
  36.     for(;;){
  37.         signo = sigwait(&mask);
  38.         switch (signo){
  39.         case SIGINT:
  40.             printf("\nsigwait wake up\n");
  41.             break;    
  42.         default:
  43.             printf("unexpected signal %d\n",signo);
  44.             exit(1);
  45.         }
  46.     }
  47. }

  48. int main(void)
  49. {
  50.     int err;
  51.     void *tret;
  52.     pthread_t tid;

  53.     sigset_t oldmask;
  54.     sigemptyset(&mask);
  55.     sigaddset(&mask, SIGINT);

  56.     if(signal(SIGINT,sig_int) == SIG_ERR)
  57.         perror("signal(SIGINT) error");
  58. /*
  59.     if((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
  60.         perror("SIG_BLOCK error");
  61. */
  62.     err = pthread_create(&tid, NULL, thr_fn, 0);
  63.     if(err != 0)
  64.         perror("Can't create thread");
  65.     
  66.     err = pthread_join(tid, tret);
  67.     if (err != 0)
  68.         perror("Can't join");

  69.     return 0;
  70. }
结果如下:
bash-3.2# ./thread_sig
^C
        SIGINT REACH
^C
        SIGINT REACH
^C
        SIGINT REACH
^C
        SIGINT REACH
^C
        SIGINT REACH
被杀掉

“使用sigwait的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理,为了防止信号中断线程,可以吧信号加到每个线程的信号屏蔽字中,然后安排专用的线程作信号处理。”

问题三:如果sigwait所等待的信号,注册了信号处理函数,那么信号处理函数被执行,还是sigwait被唤醒,还是两者都发生?
答:“如果信号被捕获,而且线程正在sigwait调用中等待同一信号,那么将由操作系统实现来决定以何种方式递送信号。操作系统实现可以让sigwait返回,也可以激活信号处理程序,但是不可能出现两者皆可的情况”。我在CentOS5 和 Solaris10 上测试的结果是,sigwait被唤醒,信号处理函数不会被执行,见代码:

  1. #include <signal.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <unistd.h>

  6. typedef void Sigfunc(int);

  7. sigset_t mask;

  8. /* Reliable version of signal(), using POSIX sigaction(). */
  9. Sigfunc *
  10. signal(int signo, Sigfunc *func)
  11. {
  12.         struct sigaction act, oact;

  13.         act.sa_handler = func;
  14.         sigemptyset(&act.sa_mask);
  15.         act.sa_flags = 0;
  16.         if (signo == SIGALRM) {
  17. #ifdef SA_INTERRUPT
  18.                 act.sa_flags |= SA_INTERRUPT;
  19. #endif
  20.         } else {
  21. #ifdef SA_RESTART
  22.                 act.sa_flags |= SA_RESTART;
  23. #endif
  24.         }
  25.         if (sigaction(signo, &act, &oact) < 0)
  26.                 return(SIG_ERR);
  27.         return(oact.sa_handler);
  28. }

  29. static void sig_int(int signo)
  30. {
  31.     printf("\n\tSIGINT REACH\n");
  32. }

  33. void * thr_fn(void *arg)
  34. {
  35.     int err, signo;
  36.     for(;;){
  37.         signo = sigwait(&mask);
  38.         switch (signo){
  39.         case SIGINT:
  40.             printf("\nsigwait wake up\n");
  41.             break;    
  42.         default:
  43.             printf("unexpected signal %d\n",signo);
  44.             exit(1);
  45.         }
  46.     }
  47. }

  48. int main(void)
  49. {
  50.     int err;
  51.     void *tret;
  52.     pthread_t tid;

  53.     sigset_t oldmask;
  54.     sigemptyset(&mask);
  55.     sigaddset(&mask, SIGINT);

  56.     if(signal(SIGINT,sig_int) == SIG_ERR)
  57.         perror("signal(SIGINT) error");

  58.     if((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
  59.         perror("SIG_BLOCK error");

  60.     err = pthread_create(&tid, NULL, thr_fn, 0);
  61.     if(err != 0)
  62.         perror("Can't create thread");
  63.     
  64.     err = pthread_join(tid, tret);
  65.     if (err != 0)
  66.         perror("Can't join");

  67.     return 0;
  68. }
结果如下:
bash-3.2# ./thread_sig
^C
sigwait wake up
^C
sigwait wake up
^C
sigwait wake up
^C
sigwait wake up
^C
sigwait wake up
^C
sigwait wake up
^C
阅读(521) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~