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;
}
总结一下通常步骤:
- 定义一个信号集,把需要屏蔽的信号加进去。
- 设置好sigaction结构体,把处理函数和屏蔽函数加进去。
- 注册捕获信号的函数。
- 开始执行。
以上。