信号 概念

来源:互联网 发布:tv版电影软件 编辑:程序博客网 时间:2024/05/15 13:07

7.1  信号

  • 概念
  • 沿革
  • 可重入性, 中断系统调用
  • Linux 中的信号
  • Linux 的信号实现

7.1.1  概念

  • 信号是异步事件
    ===============================================================================    * 异步/同步: 不可预见/可预见    * 一般的 UNIX 系统有 30 或 31 个信号    * 信号有自己的名称和对应的编号    * 信号和异常处理机制===============================================================================
  • 信号的产生条件
    ===============================================================================    * 用户在按下特定的键之后, 将向该终端上的前台进程组发送信号. 比如, Ctrl+C.    * 硬件异常会产生信号: 比如被 0 除, 无效内存引用等.    * kill(2) 系统调用可允许进程向其他进程或进程组发送任意信号.    * kill(1) 命令允许用户向进程发送任意信号.    * 软件设置的条件, 比如 SIGALARM.===============================================================================
  • 信号的关联动作 (action) 或信号的处置 (disposition)
    ===============================================================================   * 忽略信号. 有两个信号永远不能忽略: SIGKILL 和 SIGSTOP, 它们为超级用户提供了     杀死和停止进程的必要方法. 忽略硬件异常产生的信号会出现什么样的问题?   * 捕获信号. 告诉内核在出现信号时调用我们自己定义的处理函数. 比如可以在处理     SIGCHLD 信号时利用 waitpid 获得子进程的退出状态, 以避免生成僵尸进程.   * 使用默认动作. 每个信号有其默认动作.-------------------------------------------------------------------------------   * 某些信号不能被忽略, 也不能被捕获.===============================================================================
  • POSIX 定义的信号
    ===============================================================================       信号         值      动作     注释       -------------------------------------------------------------------------       SIGHUP        1        A      Hangup detected on controlling terminal                                     or death of controlling process       SIGINT        2        A      Interrupt from keyboard       SIGQUIT       3        A      Quit from keyboard       SIGILL        4        A      Illegal Instruction       SIGABRT       6        C      Abort signal from abort(3)       SIGFPE        8        C      Floating point exception       SIGKILL       9       AEF     Kill signal       SIGSEGV      11        C      Invalid memory reference       SIGPIPE      13        A      Broken pipe: write to pipe with no readers       SIGALRM      14        A      Timer signal from alarm(2)       SIGTERM      15        A      Termination signal       SIGUSR1   30,10,16     A      User-defined signal 1       SIGUSR2   31,12,17     A      User-defined signal 2       SIGCHLD   20,17,18     B      Child stopped or terminated       SIGCONT   19,18,25            Continue if stopped       SIGSTOP   17,19,23    DEF     Stop process       SIGTSTP   18,20,24     D      Stop typed at tty       SIGTTIN   21,21,26     D      tty input for background process       SIGTTOU   22,22,27     D      tty output for background process-------------------------------------------------------------------------------    * 信号值是 Linux 系统在不同平台上的取值, 第一个数对 Alpha 和 Sparc 有效, 中      间的数对 i386 和 PowerPC 有效, 最后一个数对 MIPS 有效. "-" 表现缺少该信号.    * A: 默认动作是终止进程    * B: 默认动作是忽略信号    * C: 默认动作是 core 转储    * D: 默认动作是停止进程    * E: 信号不能被捕获    * F: 信号不能被忽略===============================================================================

7.1.2  沿革

  • 信号出现在 UNIX 的早期版本中
  • 但早期的信号模型是不可靠的, 信号可能被丢失, 也很难处理关键段
  • UNIX 的两个重要分支 BSD 和 System V 分别对早期的信号进行了扩展
  • 但这两个系统的扩展并不兼容
  • POSIX 统一了这两种实现, 最终提供了可靠的信号模型

7.1.3  可重入性, 中断系统调用

  • 可重入性的概念. 调用某个函数的过程中出现信号, 如果信号处理函数中再次调用该函数, 会出现什么样的后果?
    ===============================================================================可重入函数:     int foo1 (int a) {         return a*2;     }-------------------------------------------------------------------------------不可重入函数:     void foo2 (int a) {         static int integer [20];         static int i;         if (i > 19 )             return;         integer [i] = a;         i ++;     }===============================================================================
  • 由于 errno 的特殊性, 如果信号处理函数调用了可能会修改 errno 的函数, 则应该保存并恢复 errno 的值
  • 中断系统调用 (Interrupted System Call)
  • 进程调用 "慢" 系统调用时, 如果发生信号, 内核会终止系统调用, 以便让进程有机会处理信号
  • 慢系统调用: 可能会永远阻塞的系统调用
    ===============================================================================慢系统调用的分类:    * 从终端设备, 管道和网络设备上的文件读取.    * 向上述文件写入.    * 某些设备上的文件打开.    * pause 和 wait 系统调用.    * 某些 ioctl 操作.    * 某些进程间通讯函数.===============================================================================
  • 发生中断系统调用时, 被中断的系统调用返回错误值, 而 errno 被设置为 EINTR
  • BSD/Linux 对某些系统调用在发生中断系统调用时, 可自动重新启动该系统调用

7.1.4  Linux 中的信号

  • Linux 除支持上述 POSIX 定义的信号外, 还支持下面的信号
    ===============================================================================       Signal       Value     Action   Comment       ---------------------------------------------------------------------       SIGTRAP        5         CG     Trace/breakpoint trap       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT       SIGEMT       7,-,7       G       SIGBUS      10,7,10      AG     Bus error       SIGSYS      12,-,12      G      Bad argument to routine (SVID)       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)       SIGPOLL                  AG     A synonym for SIGIO (System V)       SIGCLD       -,-,18      G      A synonym for SIGCHLD       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)       SIGPROF     27,27,29     AG     Profile alarm clock       SIGPWR      29,30,19     AG     Power failure (System V)       SIGINFO      29,-,-      G      A synonym for SIGPWR       SIGLOST      -,-,-       AG     File lock lost       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)       SIGUNUSED    -,31,-      AG     Unused signal-------------------------------------------------------------------------------    * 信号值是 Linux 系统在不同平台上的取值, 第一个数对 Alpha 和 Sparc 有效, 中      间的数对 i386 和 PowerPC 有效, 最后一个数对 MIPS 有效. "-" 表现缺少该信号.    * A: 默认动作是终止进程    * B: 默认动作是忽略信号    * C: 默认动作是 core 转储    * D: 默认动作是停止进程    * E: 信号不能被捕获    * F: 信号不能被忽略    * G: 非 POSIX 信号===============================================================================

7.1.5  Linux 的信号实现

  • Linux 中没有信号的优先级
  • Linux 中的信号不排队
  • i386 Linux 的每个进程有两个 32 位字, 分别表示当前挂起的信号以及阻塞信号
  • 32 位字的每一位对应一个信号
  • 如果产生信号, 且该信号不是 SIGKILL 或 SIGSTOP, 则当该信号没有被阻塞时才会被处理
  • 否则就是挂起的 (pending) 信号
  • Linux 用 sigaction 结构表示每个信号的处理方式
  • 信号发生时并不会立即得到处理, Linux 在如下几种情况下才有可能处理进程的信号.
    ===============================================================================   * 每次进程从系统调用中退出时.   * 内核在调度程序中选择执行该进程时.===============================================================================
  • 如果有任何一个未被阻塞的信号发出,内核就根据 sigaction 结构数组中的信息进行处理
    ===============================================================================    1.检查对应的 sigaction 结构,如果该信号不是 SIGKILL 或 SIGSTOP 信号,且被忽略,       则不处理该信号。    2.如果该信号利用默认的处理程序处理,则由内核处理该信号,否则转向第 3 步。    3.该信号由进程自己的处理程序处理,内核将修改当前进程的调用堆栈帧,并将进程的程       序计数寄存器修改为信号处理程序的入口地址。此后,指令将跳转到信号处理程序,当从信       号处理程序中返回时,实际就返回了进程的用户模式部分。

    7.2  不可靠信号

    • signal 系统调用
    • 不可靠信号的问题

    7.2.1  signal 系统调用

    • 利用 signal 函数可设置某信号的关联动作: 捕获, 忽略和默认处理
      ===============================================================================       #include <signal.h>       void (*signal(int signum, void (*handler)(int)))(int);-------------------------------------------------------------------------------handler 可取如下值:    * 用户定义的信号处理器    * SIG_IGN: 忽略信号    * SIG_DFL: 重置为信号的默认处理-------------------------------------------------------------------------------返回值为先前的设置, 若发生错误, 则返回 SIG_ERR.-------------------------------------------------------------------------------Linux 的 signal 函数有如下特点:    * 在可靠信号系统调用的基础上实现, 是库函数.    * 默认的原语类似 System V, 即信号发生时, 信号的处置重置为默认值.    * 如果包含 <bsd/signal.h>, 则具有 BSD signal 的原语, 即发生信号时,       信号处置的设置值不变.===============================================================================
    • SIG_IGN, SIG_DEF 和 SIG_ERR 的定义
      ===============================================================================    #define SIG_ERR (void (*)()) -1    #define SIG_DFL (void (*)()) 0    #define SIG_IGN (void (*)()) 1===============================================================================

    7.2.2  不可靠信号的问题

    • 发生信号时, 信号的关联动作被重置为默认设置. 这可能导致信号丢失
      ===============================================================================        void sig_int ();               /* my signal handling function */        ...        signal (SIGINT, sig_int);      /* establish handler */        ...    sig_int ()    {        signal (SIGINT, sig_int);                                     /* reestablish handler for next occurrence */        ...                           /* process the signal ... */    }-------------------------------------------------------------------------------    * 在进入 sig_int 与再次调用 signal 之间发生的 SIGINT 信号将不会被捕获,      而由于默认设置将最终导致进程终止.===============================================================================
    • 无法暂时关闭信号 (即阻塞信号), 而只能忽略信号, 这同样会导致信号丢失
      ===============================================================================    int sig_int_flag;                  /* set nonzero when signal occure */    main ()    {        void sig_int ();               /* my signal handling function */        ...        signal (SIGINT, sig_int);      /* establish handler */        ...        while (sig_int_flag == 0)            pause ();                  /* go to sleep, waiting for signal */        ...    }    sig_int ()    {        signal (SIGINT, sig_int);                                     /* reestablish handler for next occurrence */       sig_int_flag = 1;       /* set flag for main loop to examine */    }-------------------------------------------------------------------------------    * 正常情况下, 上述程序将在发生 SIGINT 信号时继续运行.    * 但在测试 sig_int_flag 的值为零, 然后在调用 pause 之前发生了 SIGINT 信号,      则 pause 函数将导致进程永远休眠.===============================================================================

      7.3  可靠信号

      • 术语和原语
      • 信号集及其操作
      • 可靠信号系统调用
      • sigsetjmp 和 siglongjmp
      • 作业控制信号

      7.3.1  术语和原语

      • 信号产生, 即发送给进程: 当导致信号的事件发生时
      • 信号递达: 当信号的动作发生时
      • 进程可阻塞信号的递达
        ===============================================================================产生被进程阻塞的信号时, 如果信号的动作是默认动作和捕获信号, 则下列情形发生之前, 信号保持挂起:    * 进程解开信号的阻塞    * 改变动作为忽略===============================================================================
      • 系统在信号递达时才考虑如何处理阻塞信号, 而不是信号产生时
      • 如果阻塞信号再次产生, 则系统可选择多次递达信号, 或只递达一次.
      • 前者称为排队信号.
      • 常见系统对信号不进行排队. Linux?
      • 每个进程有一个定义当前阻塞信号集的信号掩码
      • 信号集和 select 函数中的文件描述符集一样, 是 POSIX 引人的新数据类型, 用来定义信号集合

      7.3.2  信号集及其操作

      • 信号集的操作函数, sigsetops
        ===============================================================================       #include <signal.h>       int sigemptyset (sigset_t *set);       int sigfillset (sigset_t *set);       int sigaddset (sigset_t *set, int signum);       int sigdelset (sigset_t *set, int signum);       int sigismember (const sigset_t *set, int signum);-------------------------------------------------------------------------------上述函数的一种实现:       #define sigemptyset (ptr)       ( *(ptr) = 0 )       #define sigfillset (ptr)        ( *(ptr) = ~(sigset_t)0, 0 )                      /* C 语言的逗号运算符: 返回右边的值作为表达式的返回值 */       #include <signal.h>       #include <errno.h>       #define SIGBAD (signo)  ((signo) <=0 || (signo) >= NSIG)       int sigaddset (sigset_t *set, int signo)       {             if (SIGBAD (signo)) {errno = EINVAL; return -1;}             *set != 1 << (signo -1);             return 0;       }       int sigdelset (sigset_t *set, int signo)       {             if (SIGBAD (signo)) {errno = EINVAL; return -1;}             *set &= ~(1 << (signo -1));             return 0;       }       int sigismember (sigset_t *set, int signo)       {             if (SIGBAD (signo)) {errno = EINVAL; return -1;}             return ((*set & (1<< (signo - 1))) != 0);       }===============================================================================

      7.3.3  可靠信号系统调用

      • sigaction, sigprocmask, sigpending, sigsuspend
        ===============================================================================       #include <signal.h>       int sigaction (int signum,  const  struct  sigaction  *act, struct sigaction *oldact);       int sigprocmask (int  how,  const  sigset_t *set, sigset_t *oldset);       int sigpending (sigset_t *set);       int sigsuspend (const sigset_t *mask);-------------------------------------------------------------------------------              struct sigaction {                  void (*sa_handler) (int);                  void (*sa_sigaction) (int, siginfo_t *, void *);                  sigset_t sa_mask;                  int sa_flags;                  void (*sa_restorer)(void);     // 废弃的元素, 不应使用              }-------------------------------------------------------------------------------sigaction:    * sigaction 用于改变进程在接收到信号时的动作.    * sigaction 可指定除 SIGKILL 和 SIGSTOP 之外的信号的动作 (act->sa_handler),      可取 SIG_DFL, SIG_IGN 和用户定义函数.    * sigaction 可指定在处理 signum 信号时, 应该阻塞的信号集 (act->sa_mask).    * sigaction 可指定信号处理过程中的行为 (act->sa_flags), 下述标志 "或" 的结果:        o SA_NOCLDSTOP: 子进程停止时不接收 SIGCHLD.        o SA_ONESHOT 或 SA_RESETHAND: 重置信号动作为默认值.        o SA_RESTART: 如果该信号中断慢系统调用, 则重新启动系统调用.        o SA_NOMASK 或 SA_NODEFER: 不要避免在信号处理函数中接收同一信号.        o SA_SIGINFO: 信号处理函数接受三个参数. 这时, 必须设置 act->sa_sigaction          元素, 其中 siginfo_t 是内核传递到信号处理函数中的发生信号时进程的状态          信息. 详细信息, 可参阅 sigaction(2) 手册页.    * oldact 非空时, 可返回先前的设置.    * 利用 SA_SIGINFO 和 siginfo_t 实现有效的存储管理.-------------------------------------------------------------------------------sigprocmask:    * sigprocmask 用于改变进程的当前阻塞信号集.    * how 可取 SIG_BLOCK, SIG_UNBLOCK 和 SIG_MASKSET. 前两个动作分别在当前阻塞      信号集中添加或删除由 set 指定的信号集, SIG_MASK 用于完全设置阻塞信号集.    * oldset 非空时, 可返回先前的设置.-------------------------------------------------------------------------------sigpending:    * sigpending 用于检验挂起的信号.-------------------------------------------------------------------------------sigsuspend:    * sigsuspend 用于在接收到某个信号之前, 临时用 mask 替换进程的信号掩码, 并      暂停进程执行.    * sigsuspend 返回后将恢复调用之前的信号掩码.    * 该系统调用始终返回 -1, 并将 errno 设置为 EINTR.    * 该系统调用实际是阻塞并暂停两个动作的原子操作.===============================================================================
      • 保护关键代码段示例
        ===============================================================================    #include <signal.h>    #include <stdio.h>    #include <errno.h>    #include <sys/types.h>    #include <signal.h>    #include <unistd.h>         static void sig_int (int);    void err_sys (const char* info)    {        perror (info);        exit (1);    }    void pr_mask (const char* str)    {        sigset_t   sigset;        int        errno_save;        errno_save = errno;    /* this function may be called by signal handler */        if (sigprocmask (0, NULL, &sigset) < 0)            err_sys ("sigprocmask error");                printf ("%s", str);        if (sigismember (&sigset, SIGINT))  printf ("SIGINT  ");        if (sigismember (&sigset, SIGQUIT)) printf ("SIGQUIT  ");        if (sigismember (&sigset, SIGUSR1)) printf ("SIGUSR1  ");        if (sigismember (&sigset, SIGALRM)) printf ("SIGALRM  ");        printf ("/n");        errno = errno_save;    }        int main (void)    {        sigset_t  newmask, oldmask, zeromask;        if (signal (SIGINT, sig_int) == SIG_ERR)            err_sys ("signal (SIGINT) error");                sigemptyset (&zeromask);                sigemptyset (&newmask);        sigaddset (&newmask, SIGINT);        /* block SIGINT and save current signal mask */        if (sigprocmask (SIG_BLOCK, &newmask, &oldmask) < 0)            err_sys ("SIG_BLOCK error");                /* critical region of code */        pr_mask ("in critical region: ");        /* allow all signals and pause */        if (sigsuspend (&zeromask) != -1)            err_sys ("sigsuspend error");        pr_mask ("after return from sigsuspend: ");        /* reset signal mask whick unblocks SIGINT */        if (sigprocmask (SIG_SETMASK, &oldmask, NULL) < 0)            err_sys ("SIG_SETMASK error");        /* and continue processing ... */        exit (0);    }    static void sig_int (int signo)    {        pr_mask ("/nin sig_int: ");        return;    }-------------------------------------------------------------------------------    * 在 Linux 下, 必须包含头文件:    #include <bsd/signal.h>      以便使用可靠的 signal 函数. 或者使用 sigaction 函数.===============================================================================

      7.3.4  sigsetjmp 和 siglongjmp

      • sigsetjmp 和 siglongjmp 专用于从信号处理函数中进行远跳转
      • 并能够明确指定是否保存信号掩码
        ===============================================================================       #include <setjmp.h>       int sigsetjmp (sigjmp_buf env, int savesigs);       void siglongjmp (sigjmp_buf env, int val);-------------------------------------------------------------------------------    * 如果 savesigs 非零, 则 siglongjmp 在跳转之后将恢复保存的信号掩码.===============================================================================

      7.3.5  作业控制信号

      • #自学#

      7.4  与信号相关的函数

      • kill 和 raise
      • alarm 和 pause
      • abort, system 和 sleep

      7.4.1  kill 和 raise

      • kill: 向其它进程发送信号
      • raise: 向当前进程发送信号
        ===============================================================================       #include <sys/types.h>       #include <signal.h>       int kill (pid_t pid, int sig);       int raise (int sig);-------------------------------------------------------------------------------kill 的 pid 参数的四种条件:    * pid > 0: 信号发送到 PID 为 pid 的进程.    * pid == 0: 信号发送到与发送进程处于同一进程组的进程.    * pid < -1: 信号发送到进程组 ID 等于 -pid 的所有进程.    * pid == -1: POSIX 未指定. Linux 发送到进程表中所有的进程, 除第一个进程之外.    * sig == 0 时, 不会发送任何信号, 但仍然执行错误检查, 因此可用来检查是否有      向指定进程发送信号的许可.===============================================================================
      • raise 等价于 kill (getpid (), sig)

      7.4.2  alarm 和 pause

      • alarm 可设置定时器. 定时器到期时, 将产生 SIGALRM 信号
      • setitimer 是另一个设置间隔定时器的函数, 更加灵活
        ===============================================================================       #include <unistd.h>       unsigned int alarm (unsigned int seconds);-------------------------------------------------------------------------------       #include <sys/time.h>       int getitimer (int which, struct itimerval *value);       int setitimer (int which, const struct itimerval *value, struct itimerval *ovalue);-------------------------------------------------------------------------------    * alarm 的返回值为先前尚未过期的定时器所剩余的时间.    * alarm 和 setitimer 使用同一个定时器, 因此会互相影响.    * setitimer 定时精度要比 alarm 高, 同时可以按照进程的执行时间 (用户态时间,      核心态时间, 或两者) 进行计时.-------------------------------------------------------------------------------EXAMPLE:      /* MiniGUI 的定时器 */      void sig_handler (int v)      {         int sem_value;         timer_counter++;         // alert desktop         DskMsgs.dwState |= 0x01;         sem_getvalue (&DskMsgs.wait, &sem_value);         if (sem_value == 0)            sem_post(&DskMsgs.wait);      }      BOOL InitTimer ()      {         struct itimerval timerv;         struct sigaction siga;                  siga.sa_handler = sig_handler;         siga.sa_flags = 0;                  memset (&siga.sa_mask, 0, sizeof (sigset_t));         sigaction (SIGALRM, &siga, &old_alarm_handler);         timerv.it_interval.tv_sec = 0;         timerv.it_interval.tv_usec = 10000;         timerv.it_value = timerv.it_interval;         if (setitimer (ITIMER_REAL, &timerv, &old_timer)) {            fprintf(stderr, "TIMER: setitimer call failed!/n");            perror("setitimer");            return FALSE;         }         timer_counter = 0;         return TRUE;      }      BOOL TerminateTimer ()      {         int i;         if (setitimer (ITIMER_REAL, &old_timer, 0) == -1) {            fprintf( stderr, "Timer: setitimer call failed!/n");            perror("setitimer");            return 0;         }         if (sigaction (SIGALRM, &old_alarm_handler, NULL) == -1) {            fprintf( stderr, "Timer: sigaction call failed!/n");            perror("sigaction");            return 0;         }         for (i=0; i < MAX_TIMERS; i++) {            if (timerstr[i] != NULL)               free ( timerstr[i] );            timerstr[i] = NULL;         }         return 1;      }===============================================================================
      • pause 暂停调用进程并等待信号
        ===============================================================================       #include <unistd.h>       int pause (void);===============================================================================

      7.4.3  abort, system 和 sleep

      • abort 函数向进程发送 SIGABRT 信号, 从而可导致程序非正常终止
        ===============================================================================       #include <stdlib.h>       void abort (void);-------------------------------------------------------------------------------    * 如果捕获 SIGABRT 信号, 且信号处理器不返回, 则 abort 不能终止进程.    * abort 终止进程时, 所有打开的流均会被刷新并关闭.    * 如果 SIGABRT 被阻塞或忽略, abort 将覆盖这种设置.===============================================================================
      • system: 执行系统命令
        ===============================================================================       #include <stdlib.h>       int system (const char * string);-------------------------------------------------------------------------------     * 在执行命令的过程中, SIGCHLD 被阻塞, 而 SIGINT 和 SIGQUIT 被忽略.-------------------------------------------------------------------------------EXAMPLE:     /* POSIX.2 implementation of system function */      #include <sys/types.h>      #include <sys/wait.h>      #include <errno.h>      #include <signal.h>      #include <unistd.h>      int system (const char *cmdstring)  /* with appropriate signal handling */      {         pid_t                  pid;         int                    status;         struct sigaction       ignore, saveintr, savequit;         sigset_t               childmask, savemask;         if (cmdstring == NULL)            return (1);        /* always a command processor with UNIX */                  ignore.sa_handler = SIG_IGN;    /* ignore SIGINT and SIGQUIT */         sigemptyset (&ignore.sa_mask);         ignore.sa_flags = 0;         if (sigaction (SIGINT, &ignore, &saveintr) < 0)            return (-1);         if (sigaction (SIGQUIT, &ignore, &savequit) < 0)            return (-1);                  sigemptyset (&childmask);        /* now block SIGCHLD */         sigaddset (&childmask, SIGCHLD);         if (sigprocmask (SIG_BLOCK, &childmask, &savemask) < 0)            return (-1);                  if ( (pid = fork ()) < 0) {            status = -1;    /* probably out of processes */                  }         else if (pid == 0) {            /* child */            /* restore previous signal actions & rest signal mask */            sigaction (SIGINT, &saveintr, NULL);            sigaction (SIGQUIT, &savequit, NULL);            sigprocmask (SIG_SETMASK, &savemask, NULL);            execl ("/bin/sh", "sh", "-c", cmdstring, (char *)0);            _exit (127);               /* exec error */         }         else {                   /* parent */            while (waitpid (pid, &status, 0) < 0)               if (errno != EINTR) {                  status = -1;   /* errno other than EINTR form waitpid() */                  break;               }         }         /* restore previous signal actions & reset signal mask */         if (sigaction (SIGINT, &saveintr, NULL) < 0)            return (-1);         if (sigaction (SIGQUIT, &savequit, NULL) < 0)            return (-1);         if (sigprocmask (SIG_SETMASK, &savemask, NULL) == 0)            return (-1);         return (status);      }===============================================================================
      • sleep: 休眠指定的秒数
        ===============================================================================       #include <unistd.h>       unsigned int sleep (unsigned int seconds);-------------------------------------------------------------------------------     * 经过指定的时间之后, sleep 返回.     * 在这之前, 如果接收到信号, 则 sleep 返回剩余的秒数.--------------------------------------------------------------------------------------------------------------------------------------------------------------EXAMPLE:     /* POSIX.2 implementation of sleep function */        #include <sys/types.h>        #include <stddef.h>        #include <signal.h>        #include <unistd.h>        static void sig_alrm (void)        {            return; /* nothing to do, just returning wakes up sigsuspend() */        }        unsigned int sleep (unsigned int nsecs)        {            struct sigaction        newact, oldact;            sigset_t                newmask, oldmask, suspmask;            unsigned int            unslept;            newact.sa_handler = sig_alrm;            sigemptyset (&newact.sa_mask);            newact.sa_flags = 0;            sigaction (SIGALRM, &newact, &oldact);                                     /* set our handler, save previous information */            sigemptyset (&newmask);            sigaddset (&newmask, SIGALRM);                                     /* block SIGALRM and save current signal mask */            sigprocmask (SIG_BLOCK, &newmask, &oldmask);            alarm (nsecs);            suspmask = oldmask;            sigdelset (&suspmask, SIGALRM);      /* make sure SIGALRM isn't blocked */                        sigsuspend (&suspmask);              /* wait for any signal to be caught */            /* some signal has been caught, SIGALRM is now blocked */            unslept = alarm (0);            sigaction (SIGALRM, &oldact, NULL);  /* reset previous action */            sigprocmask (SIG_SETMASK, &oldmask, NULL);            return (unslept);        }

        7.5  线程中的信号处理

        • 概念
        • pthread_sigmask
        • pthread_kill 和 sigwait

        7.5.1  概念

        • 按照 POSIX, 异步 (外部) 信号发送到整个进程.
        • 所有线程共享同一个设置, 即通过 sigaction 设置的线程处置方法.
        • 每个线程有自己的信号掩码, 线程库根据该掩码决定将信号发送到哪个线程.
        • 由于 Linux 线程实现上的独特性, 外部信号始终发送到特定的线程.

        7.5.2  pthread_sigmask

        • pthread_sigmask 用来定义线程的信号掩码
        • 其接口与 sigprocmask 一样
          ===============================================================================       #include <pthread.h>       #include <signal.h>       int pthread_sigmask (int how, const sigset_t *newmask, sigset_t *oldmask);===============================================================================

        7.5.3  pthread_kill 和 sigwait

        ===============================================================================       #include <pthread.h>       #include <signal.h>       int pthread_kill (pthread_t thread, int signo);       int sigwait (const sigset_t *set, int *sig);===============================================================================
        • pthread_kill 向特定的线程发送信号.
        • sigwait 暂定调用线程, 直到 set 中定义的某个信号递达调用线程.
        • sigwait 返回时, sig 中保存的是接收到的信号编号.
        • sigwait 所等待的信号必须在所有线程中阻塞, 而不仅仅是调用线程.
原创粉丝点击