Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1800368
  • 博文数量: 438
  • 博客积分: 9799
  • 博客等级: 中将
  • 技术积分: 6092
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-25 17:25
文章分类

全部博文(438)

文章存档

2019年(1)

2013年(8)

2012年(429)

分类: 系统运维

2012-03-31 23:18:53

处理信号甚至可以用一个基于进程的范型来完成。把线程引入到系统让事情变得更复杂。


每个线程有它自己的信号掩码,但是信号布署被进程 里的所有线程共享。这意味着个体线程可以阻塞信号,但是当一个线程修改了一个给定信号的相关动作时,所有的线程都共享这个动作。因而,如果一个线程选择忽 略一个给定信号,那么另一个线程可以撤消那个选择,通过恢复默认布署或为信号安装一个信号处理机。


信号被分发给进程里的单个线程。如果信号和一个硬件错误或过期的计时器相关,那么信号被发送给其动作导致这个事件的线程。另一方面,其它信号被分发给任意一个线程。


在10.12节,我们讨论了进程如何使用sigprocmask来阻塞信号的分发。sigprocmask的行为在多线程下是无定义的。线程必须使用pthread_sigmask来替代。



  1. #include <siganl.h>

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

  3. 成功返回0,失败返回错误号。


pthread_sigmask函数和sigprocmask相同,除了pthread_sigmask和线程工作,并在失败时返回一个错误码而不是设置errno并返回-1。


一个线程可以等待一个或多个信号的发生,通过调用sigwait。



  1. #include <signal.h>

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

  3. 成功返回0,失败返回错误号。


set参数指定了线程正在等待的信号集。在返回时,signop指向的整型将包含被分发的信号号。


如 果set里指定的某个信号在sigwait被调用时正待定,那么sigwait会返回而不被阻塞。在返回前,sigwait从进程的待定信号集里删除这个 信号。为了避免错误行为,一个线程必须在调用sigwait之前阻塞信号。sigwait函数将自动阻塞信号并等待某个被分发。在返回前,sigwait 会恢复线程的信号掩码。如果信号在sigwait被调用时没有被阻塞,那么在它线程完成它的sigwait调用前,会有时间间隙,信号可以被分发到线程。


使 用sigwait的好处是它能简化信号处理,通过允许我们用同步方式处理异步产生的信号。我们可以避免信号中断信号,通过把它们加到每个线程的信号掩码 里。然后我们可以指定特定的线程来处理信号。这些被指定的线程可以进行函数调用,而不必担心哪些函数可以在一个信号处理机里安全地调用,因为它们从普通的 线程上下文里被调用,而不是传统的中断一个普通线程执行的信号处理机。


如果多线程在sigwait调用时阻塞在同一个信号上,那么当信号被 分发时只有一个线程从sigwait返回。如果信号被捕获(例如进程使用sigaction建立了一个信号处理机)而线程正在sigwait调用里等待相 同的信号,那依赖于实现来决定如何分发信号。在这种情况下,信号可能允许sigwait返回或调用信号处理机,但不会同时。


为了发送信号给一个进程,我们调用kill(10.9)。为了发送一个信号给一个线程,我们调用pthread_kill。



  1. #include <signal.h>

  2. int pthread_kill(pthread_t thread, int signo);

  3. 成功返回0,失败返回错误码。


我们可以传递值为0的signo来检查线程的存在性。如果一个线程的默认动作是终止这个进程,那么发送信号给一个线程仍然会杀死整个进程。


注意闹钟计时器是一个进程资源,而所有线程共享同一个闹钟集。因此,进程里的多线程无法在不影响(或合作)另一个线程的情况下使用闹钟计时器。


回想第10.16节,我们等待一个信号处理机来设置一个表示主程序应该退出的标志。仅有的可以运行的线程控制是主线程和信号处理机,所以阻塞信号足以避免标志改变的丢失。有了线程,我们需要使用一个互斥体来保护这个标准,如下面的代码所示:



  1. #include <pthread.h>
  2. #include <signal.h>

  3. int quitflag; /* set nonzero by thread */
  4. sigset_t mask;

  5. pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
  6. pthread_cond_t wait = PTHREAD_COND_INITIALIZER;

  7. void *
  8. thr_fn(void *arg)
  9. {
  10.     int err, signo;

  11.     for (;;) {
  12.         err = sigwait(&mask, &signo);
  13.         if (err != 0) {
  14.             printf("sigwait failed: %s\n", strerror(err));
  15.             exit(1);
  16.         }
  17.         switch (signo) {
  18.         case SIGINT:
  19.             printf("\ninterrupt\n");
  20.             break;

  21.         case SIGQUIT:
  22.             pthread_mutex_lock(&lock);
  23.             quitflag = 1;
  24.             pthread_mutex_unlock(&lock);
  25.             pthread_cond_signal(&wait);
  26.             return(0);

  27.         default:
  28.             printf("unexpected signal %d\n", signo);
  29.             exit(1);
  30.         }
  31.     }
  32. }

  33. int
  34. main(void)
  35. {
  36.     int err;
  37.     sigset_t oldmask;
  38.     pthread_t tid;

  39.     sigemptyset(&mask);
  40.     sigaddset(&mask, SIGINT);
  41.     sigaddset(&mask, SIGQUIT);
  42.     if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0) {
  43.         printf("SIG_BLOCK error: %s\n", strerror(err));
  44.         exit(1);
  45.     }

  46.     err = pthread_create(&tid, NULL, thr_fn, 0);
  47.     if (err != 0) {
  48.         printf("can't create thread: %s\n", strerror(err));
  49.         exit(1);
  50.     }

  51.     pthread_mutex_lock(&lock);
  52.     while (quitflag == 0)
  53.         pthread_cond_wait(&wait, &lock);
  54.     pthread_mutex_unlock(&lock);

  55.     /* SIGQUIT has been caught and is now blocked; do whatever */
  56.     quitflag = 0;

  57.     /* reset signal mask which unblocks SIGQUIT */
  58.     if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
  59.         printf("SIG_SETMASK error: %s\n", strerror(err));
  60.         exit(1);
  61.     }
  62.     exit(0);
  63. }

我们用一个单独的线程控制来处理信号,而不是依赖于一个中断主线程控制的信号处理机。我们在一个互斥体的保护下改变quitflag的值,以 便主线程控制不会错过当我们调用pthred_cond_signal时生成的唤醒调用。我们在主线程控制里使用相同的互斥体来检查标志的值,并自动释放 这个互斥体以及等待这个条件。

注意我们在主线程开头阻塞了SIGINT和SIGQUIT。当我们创建线程来处理信号时,线程继承了当前的信号掩码。因为sigwait将反阻塞信号,只有一个线程可以收到信号。这让我们在不用担心从这些信号中断的情况下编码我们的主线程。


运行结果为:
^C
interrupt
^C
interrupt
^\$ 


Linux 实现线程为单独的进程,用clone共享资源。因为如此,Linux上的线程对待信号的行为和其它实现不同。在POSIX.1线程模型里,异步信号被发送 给一个进程,然后进程里的某个线程被选来收到这个信号,基于当前哪个线程没有阻塞这个信号。在Linux上,一个异步信号被发送给一个特定的线程,而因为 每个线程作为单独的进程执行,系统不能选择一个当前没有阻塞该信号的线程。结果是线程可能注意不到信号。因而,像上面的代码当信号由终端驱动产生时可以工 作,它向进程组发送信号,但是当你尝试用kill向进程发送一个信号,在Linux上它不会如预期地工作。(我的ubuntu上的Linux3.0好像可 以用kill发送信号。)

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