信号集函数

POSIX信号集操作

#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);

sigset_t 是信号集类型。信号集实际上就是一个位图,每个信号表示对应位。

顾名思义:

  • "empty":清空信号集的每一位(置0)。
  • "fill": 填充信号集的每一位(置1)。
  • "add": 加入一个信号到信号集中(置1)。
  • "del": 从信号集中移除一个信号(置0)。
  • "ismember:" 判断是否在信号集中。

参数解释

  • set: 我们所操作的信号集。
  • **signum: ** 操作的信号。

返回值

  • sigismember返回值:
    • 在信号集内返回1。
    • 不在信号集内返回0。
  • 其余:
    • 成功返回0.
    • 失败返回-1,设置errno.

sigprocmask函数

来屏蔽信号、解除屏蔽也使用该函数。其本质,读取或修改进程的信号屏蔽字(PCB中)。

注意:

  • 屏蔽信号:只是将信号处理延后执行(延至解除屏蔽)+
  • 忽略信号:表示将信号丢弃。

函数原型:

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

参数解释

  • how: 设置方式。
    • SIG_BLOCK: 设置为阻塞。
    • SIG_UNBLOCK: 设置为非阻塞。
    • SIG_SETMASK:设置一个新的屏蔽集合为set。
  • set: 新设置的信号集。
  • oldset: 返回上一次信号集。保护现场。

返回值

  • 成功调用返回0.
  • 失败调用返回-1,设置errno.

sigpending函数

函数原型:

#include <signal.h>
int sigpending(sigset_t *set);
  • set是传出参数。
  • 此函数将传入的set填充为当前的信号集,可以看到未决信号。通过sigismember函数调用可以知道指定的信号是否未决。

sigaction函数

注册需要捕获的信号的函数。

我们以后最常用的就是sigaction函数,和signal一样它也是一个捕获信号执行自定义行为的函数,同样用来防止进程的意外死亡。但是它提供更加丰富的机制。

函数原型

#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

其中,struct sigaction结构体为:

struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;
    void     (*sa_restorer)(void);
};

struct sigaction结构体成员:

  • (*sa_restorer)(void): 不需要设置。
  • sa_flags:
    • 一般设置为0,捕获信号后会执行第一个成员函数sa_handler.
    • 如果指定为SA_SIGINFO,则捕获后执行第二个成员函数sa_sigaction。
  • sa_mask: 在处理函数执行期间,原本被屏蔽的信号集不再是PCB中所屏蔽的。此时需要指定在处理函数期间需要屏蔽的信号集 (如果处理函数需要执行很久,那么设置这个信号集很有必要)。是临时性的设置,出了处理函数之后将无效。

参数解释

  • signum: 注册的信号。
  • act: 上述已经解释,根据实际情况设置好。
  • oldact: 传出参数,返回上一次设置的act。用以保护现场。

返回值

  • 成功调用返回0.
  • 失败调用返回-1,且设置好errno.

Note

信号捕捉的特性

  • 进程正常运行时,默认PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由☆来指定。而是用sa_mask来指定。调用完信号处理函数,再恢复为☆。
  • XXX信号捕捉函数执行期间,XXX信号自动被屏蔽。
  • 阻塞的常规信号不支持排队,产生多次只记录一次。(后32个实时信号支持排队)。

实例

最后来个实例吧。

void catchSignal(int signal)
{   
    printf("catch signal:%d
", signal);
    //模拟捕捉执行时间长的时候,新来的同样信号和屏蔽信号会怎样?
    //结论是会被阻塞,在未决信号集中置1,此函数执行完会去捕捉。
    //但是多个信号过来,只会捕捉一次。
    sleep(2);   
    printf("cat end.
");
}

int main()
{
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGQUIT);
    
    //设置阻塞,执行处理函数期间能够正确捕捉到一个SIGQUIT等待处理
    sigprocmask(SIG_BLOCK, &sigset, NULL);
    
    // 初始化sigaction结构体
    struct sigaction sigac;
    sigac.sa_flags = 0;
    sigac.sa_handler = catchSignal;
    
    //信号捕捉期间阻塞SIGQUIT
    sigac.sa_mask = sigset;
    //注册捕捉函数
    sigaction(SIGINT, &sigac, NULL);
    while(1)
    {
      printf("wait for signal...
");
      sleep(1);
    }
    return 0;
}

总结一下通常步骤:

  1. 定义一个信号集,把需要屏蔽的信号加进去。
  2. 设置好sigaction结构体,把处理函数和屏蔽函数加进去。
  3. 注册捕获信号的函数。
  4. 开始执行。

以上。

原文地址:https://www.cnblogs.com/love-jelly-pig/p/10069566.html