• UNIX环境编程学习笔记(24)——信号处理进阶学习之信号集和进程信号屏蔽字


    lienhua34
    2014-11-03

    1 信号传递过程

    信号源为目标进程产生了一个信号,然后由内核来决定是否要将该信号传递给目标进程。从信号产生到传递给目标进程的流程图如图 1 所示,

    图 1: 信号产生、传递到处理的流程图

    进程可以阻塞信号的传递。当信号源为目标进程产生了一个信号之后,内核会执行依次执行下面操作,

    1. 如果目标进程设置了忽略该信号,则内核直接将该信号丢弃。

    2. 如果目标进程没有阻塞该信号,则内核将该信号传递给目标进程,由目标进程执行相对应操作。

    3. 如果目标进程设置阻塞该信号,则内核将该信号放到目标进程的阻塞信号列表中,等待目标进程对该类型信号的下一步设置。若目标进程后续设置忽略该信号,则内核将该信号从目标进程的阻塞信号列表中移除并丢弃。若目标进程对该信号解除了阻塞,内核将该信号传递给目标进程进行相对应的操作。

    在信号产生到信号传递给目标进程之间的时间间隔内,我们称该信号为未决的(pending)。

    每个进程都有一个信号屏蔽字(signal mask),它规定了当前要阻塞传递给该进程的信号集。对于每种可能的信号,信号屏蔽字中都有一位与之对应。

    2 信号集及其操作

    POSIX.1 定义了一个数据类型sigset_t,用于表示信号集。另外,头文件 signal.h 提供了下列五个处理信号集的函数。

    函数 sigemptyset 初始化由 set 指向的信号集,清除其中所有信号。

    int sigemptyset(sigset_t *set);

    返回值:若成功则返回0,若出错则返回-1

    函数 sigfillset 初始化由 set 指向的信号集,使其包含所有信号。

    int sigfillset(sigset_t *set);
    返回值:若成功则返回0,若出错则返回-1

    函数 sigaddset 将一个信号 signo 添加到现有信号集 set 中。

    int sigaddset(sigset_t *set, int signo);
    返回值:若成功则返回0,若出错则返回-1

    函数 sigdelset 将一个信号 signo 从信号集 set 中删除。

    int sigdelset(sigset_t *set, int signo);
    返回值:若成功则返回0,若出错则返回-1

    函数 sigismember 判断指定信号 signo 是否在信号集 set 中。

    int sigismember(const sigset_t *set, int signo);
    返回值:若真则返回1,若假则返回0,若出错则返回-1

    3. sigprocmask 检 或设置进程的信号屏蔽字

    调用 sigprocmask 函数可以检测或者设置进程的信号屏蔽字。

    #include <signal.h>
    int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
    返回值:若成功则返回0,若出错则返回-1

    若 oset 参数是一个非空指针,则进程的当前信号屏蔽字将通过 oset 返回。若 set 参数是一个非空指针,则参数 how 将指示如何修改当前信号屏蔽字。how 的可选值如表 1 所示,

    表 1: sigprocmask 函数 how 参数可选值
    how 说明
    SIG_BLOCK 该进程新的信号屏蔽字是其当前信号屏蔽字和 set 指向信号集的并集。
    SIG_UNBLOCK 该进程的信号屏蔽字是当前信号屏蔽字和 set 所指向信号集补给的交集。set 包含了我们希望解除阻塞的信号。
    SIG_SETMASK 该进程新的信号屏蔽字设置为 set 所指向的信号集。

    下面我们来看一个例子。在下面的程序文件中先调用 sigprocmask 设置阻塞信号 SIGALRM,然后调用 alarm(2) 设置一个两秒钟的闹钟(两秒钟之后将向当前进程产生一个 SIGALRM 信号)。在睡眠 4 秒钟之后(此时应该已经产生了 SIGALRM 信号),调用 sigprocmask 函数解除对信号SIGALRM 的阻塞。

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <signal.h>
    
    static void
    sig_alrm(int signo)
    {
      printf("received SIGALRM
    ");
    }
    
    int
    main(void)
    {
      sigset_t sigset;
    
      sigemptyset(&sigset);
      sigaddset(&sigset, SIGALRM);
      if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0) {
        printf("sigprocmask error: %s
    ", strerror(errno));
        exit(-1);
      }
    
      if (signal(SIGALRM, sig_alrm) < 0) {
        printf("signal error: %s
    ", strerror(errno));
        exit(-1);
      }
    
      alarm(2);
      sleep(4);
      printf("before unblock sigprocmask
    ");
      if (sigprocmask(SIG_UNBLOCK, &sigset, NULL) < 0) {
        printf("sigprocmask SIG_UNBLOCK error: %s
    ", strerror(errno));
        exit(-1);
      }
      printf("before exit
    ");
      exit(0);
    }
    sigprocmaskdemo.c

    编译该程序文件 sigprocmaskdemo.c,生成并执行文件 sigprocmaskdemo,

    lienhua34:demo$ gcc -o sigprocmaskdemo sigprocmaskdemo.c
    lienhua34:demo$ ./sigprocmaskdemo
    before unblock sigprocmask
    received SIGALRM
    before exit

    从上面的执行输出,我们看到信号 SIGALRM 是在调用 sigprocmask函 数 执 行 unblock 之 后 才 被 传 递 给 当 前 进 程 进 行 处 理 的。 如 果 我 们 将sigprocmaskdemo.c 中的sigprocemask(SIG_BLOCK, &sigset, NULL) 注释掉,编译执行,生成如下结果,

    lienhua34:demo$ ./sigprocmaskdemo
    received SIGALRM
    before unblock sigprocmask
    before exit

    4 sigpending 获取进程未决的信号集

    函数 sigpending 获取当前进程所有未决的信号。通过其 set 参数返回未决的信号集。

    #include <signal.h>
    int sigpending(sigset_t *set);
    返回值:若成功则返回0,若出错则返回-1

    下面我们来看一个例子,

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <signal.h>
    
    void
    alrm_is_pending(const char *str)
    {
      sigset_t pendingsigset;
    
      printf("%s: ", str);
      if (sigpending(&pendingsigset) < 0) {
        printf("sigpending error: %s
    ", strerror(errno));
        exit(-1);
      }
      if (sigismember(&pendingsigset, SIGALRM)) {
        printf("SIGALRM is pending
    ");
      } else {
        printf("SIGALRM is not pending
    ");
      }
    }
    
    int
    main(void)
    {
      sigset_t sigset;
    
      sigemptyset(&sigset);
      sigaddset(&sigset, SIGALRM);
      if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0) {
        printf("sigprocmask error: %s
    ", strerror(errno));
        exit(-1);
      }
      alrm_is_pending("before alarm");
      alarm(2);
      sleep(4);
      alrm_is_pending("after alarm");
      exit(0);
    }
    sigpendingdemo.c

    编译该程序 sigpendingdemo.c,生成并执行文件 sigpendingdemo。从下面的运行结果,我们看到调用 alarm 函数产生信号 SIGALRM 之后,该信号在 sigpending 函数的 set 参数指向的信号集中。

    lienhua34:demo$ gcc -o sigpendingdemo sigpendingdemo.c
    lienhua34:demo$ ./sigpendingdemo
    before alarm: SIGALRM is not pending
    after alarm: SIGALRM is pending

    (done)

  • 相关阅读:
    hdu_5718_Oracle(大数模拟)
    hdu_2222_Keywords Search(AC自动机板子)
    hdu_5616_Jam's balance(暴力枚举子集||母函数)
    hdu_2255_奔小康赚大钱(KM带权二分匹配板子)
    hdu_2544_最短路(spfa版子)
    hdu_2457_DNA repair(AC自动机+DP)
    hdu_5555_Immortality of Frog(状压DP)
    hdu_2159_FATE(完全背包)
    [USACO2002][poj1944]Fiber Communications(枚举)
    [AHOI2013]打地鼠(网络流)
  • 原文地址:https://www.cnblogs.com/lienhua34/p/4072417.html
Copyright © 2020-2023  润新知