sigwait

信号的生成来自内核,让内核生成信号的请求来自3个地方:
  1. 用户:用户能够通过输入CTRL+c、Ctrl+\,或者是终端驱动程序分配给信号控制字符的其他任何键来请求内核产生信号;
  2. 内核:当进程执行出错时,内核会给进程发送一个信号,例如非法段存取(内存访问违规)、浮点数溢出等;
  3. 进程:一个进程可以通过系统调用kill给另一个进程发送信号,一个进程可以通过信号和另外一个进程进行通信。
由进程的某个操作产生的信号称为同步信号(synchronous signals),例如除0;由象用户击键这样的进程外部事件产生的信号叫做异步信号。(asynchronous signals)。

进程接收到信号以后,可以有如下3种选择进行处理:
  1. 接收默认处理:接收默认处理的进程通常会导致进程本身消亡。例如连接到终端的进程,用户按下CTRL+c,将导致内核向进程发送一个SIGINT的信号,进程如果不对该信号做特殊的处理,系统将采用默认的方式处理该信号,即终止进程的执行;
  2. 忽略信号:进程可以通过代码,显示地忽略某个信号的处理,例如:signal(SIGINT,SIGDEF);但是某些信号是不能被忽略的,
  3. 捕捉信号并处理:进程可以事先注册信号处理函数,当接收到信号时,由信号处理函数自动捕捉并且处理信号。

 

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
有两个信号既不能被忽略也不能被捕捉,它们是SIGKILL和SIGSTOP。即进程接收到这两个信号后,只能接受系统的默认处理,即终止线程。

可靠性

在早期的UNIX中信号是不可靠的,不可靠在这里指的是:信号可能丢失,一个信号发生了,但进程却可能一直不知道这一点。
现在Linux 在SIGRTMIN实时信号之前的都叫不可靠信号,这里的不可靠主要是不支持信号队列,就是当多个信号发生在进程中的时候(收到信号的速度超过进程处理的速度的时候),这些没来的及处理的信号就会被丢掉,仅仅留下一个信号。
可靠信号是多个信号发送到进程的时候(收到信号的速度超过进程处理信号的速度的时候),这些没来的及处理的信号就会排入进程的队列。等进程有机会来处理的时候,依次再处理,信号不丢失。



 -------------------------------------------------------------------------------------------

在多线程代码中,总是使用sigwait或者sigwaitinfo或者sigtimedwait等函数来处理信号。
而不是signal或者sigaction等函数。因为在一个线程中调用signal或者sigaction等函数会改变所有线程中的
信号处理函数。而不是仅仅改变调用signal/sigaction的那个线程的信号处理函数。

每个线程均有自己的信号屏蔽集(信号掩码),可以使用pthread_sigmask函数来屏蔽某个线程对某些信号的
响应处理,仅留下需要处理该信号的线程来处理指定的信号。实现方式是:利用线程信号屏蔽集的继承关系
(在主进程中对sigmask进行设置后,主进程创建出来的线程将继承主进程的掩码)

int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
调用sigwait同步等待的信号必须在调用线程中被屏蔽,并且通常应该在所有的线程中被屏蔽(这样可以保证信号绝不会被送到除了调用sigwait的任何其它线程),这是通过利用信号掩码的继承关系来达到的。

signal或者sigaction是进程里面的概念,他们是针对进程进行控制的,是所有线程共享的。

sigwait()则是针对线程的概念,它的设置仅仅针对当前线程有效,同时最好在其它所有线程block指定的信号集。



下面是通过在主线程中阻塞一些信号,其它的线程会继承信号掩码,然后专门用一个线程使用sigwait函数来同步的处理信号,使其它的线程不受到信号的影响。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

#define handle_error_en(en, msg) \
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

static void * sig_thread(void *arg)
{
      sigset_t *set = (sigset_t *) arg;
      int s, sig;

      for (;;) {
            s = sigwait(set, &sig);
            if (s != 0)
                  handle_error_en(s, "sigwait");
            printf("Signal handling thread got signal %d\n", sig);
      }
}

int main(int argc, char *argv[])
{
      pthread_t thread;
      sigset_t set;
      int s;

      /* 
         Block SIGINT; other threads created by main() will inherit
         a copy of the signal mask. 
       */
      sigemptyset(&set);
      sigaddset(&set, SIGQUIT);
      sigaddset(&set, SIGUSR1);
      s = pthread_sigmask(SIG_BLOCK, &set, NULL);
      if (s != 0)
            handle_error_en(s, "pthread_sigmask");
      s = pthread_create(&thread, NULL, &sig_thread, (void *) &set);
      if (s != 0)
            handle_error_en(s, "pthread_create");
      /* 
        Main thread carries on to create other threads and/or do
        other work 
       */
      pause(); /* Dummy pause so we can test program */
      return 0;
}

 

posted @ 2016-03-19 20:48  牧 天  阅读(2934)  评论(0)    收藏  举报