sigaction 自定义信号处理函数的“修复”

在对嵌入式 Linux 系统程序开发中的进程间通信进行学习时,涉及到了信号集函数组,书中给出的示例如下:

该实例首先把 SIGQUIT、SIGINT 两个信号加入信号集,然后将该信号集设为阻塞状态,
并在该状态下使程序暂停 5 秒。接下来再将信号集设置为非阻塞状态,再对这两个信号分别
操作,其中 SIGQUIT 执行默认操作,而 SIGINT 执行用户自定义函数的操作。源代码如下
所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void my_func(int signum)
{
printf("If you want to quit,please try SIGQUIT\n");
}
int main()
{
sigset_t set,pendset;
struct sigaction action1,action2;
if(sigemptyset(&set)<0)
perror("sigemptyset");
if(sigaddset(&set,SIGQUIT)<0)
perror("sigaddset");
if(sigaddset(&set,SIGINT)<0)
perror("sigaddset");
if(sigprocmask(SIG_BLOCK,&set,NULL)<0)
perror("sigprocmask");
else
{
printf("blocked\n");
sleep(5);
}
if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)
perror("sigprocmask");
else
printf("unblock\n");
while(1){
if(sigismember(&set,SIGINT)){
sigemptyset(&action1.sa_mask);
action1.sa_handler=my_func;
sigaction(SIGINT,&action1,NULL);
}else if(sigismember(&set,SIGQUIT)){
sigemptyset(&action2.sa_mask);
action2.sa_handler = SIG_DFL;
sigaction(SIGTERM,&action2,NULL);
}
}
}

理论上,按说明所述及代码的编写,使用 Ctrl-C 传递 SIGINT 信号后,会转到自定义处理函数 my_func,但在实际操作中,在进入自定义处理函数后,程序直接退出。猜测是由于在进行自定义处理函数后,系统仍然对信号做了默认的处理。

在请教老师后,给出了一种解决方案,对 sigaction 结构体中的 sa_flags 进行赋值,这里先贴出该结构体。

1
2
3
4
5
6
struct sigaction {
void (*sa_handler)(int signo);
sigset_t sa_mask;
int sa_flags;
void (*sa_restore)(void);
}

sa_handler 是一个函数指针,指定信号关联函数,这里除可以是用户自定义的处理函数
外,还可以为 SIG_DFL(采用缺省的处理方式)或 SIG_IGN(忽略信号)。它的处理函数只
有一个参数,即信号值。
sa_mask 是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被阻塞,
在调用信号捕获函数之前,该信号集要加入到信号的信号屏蔽字中。
sa_flags 中包含了许多标志位,是对信号进行处理的各个选择项。它的常见可选值如下:

选项 含义
SA_NODEFER/SA_NOMASK 当捕捉到此信号时,在执行其信号捕捉函数时,系统不会自动阻塞此信号
SA_NOCLDSTOP 进程忽略子进程产生的任何 SIGSTOP、SIGTSTP、SIGTTIN 和 SIGTTOU 信号
SA_RESTART 可让重启的系统调用重新起作用
SA_ONESHOT/SA_RESETHAND 自定义信号只执行一次,在执行完毕后恢复信号的系统默认动作

sigaction 结构体中的 sa_flags 成员赋值为 0,修改后的部分代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
while(1){
if(sigismember(&set,SIGINT)){
sigemptyset(&action1.sa_mask);
action1.sa_handler=my_func;
action1.sa_flags=0; //添加此行
sigaction(SIGINT,&action1,NULL);
}else if(sigismember(&set,SIGQUIT)){
sigemptyset(&action2.sa_mask);
action2.sa_handler = SIG_DFL;
sigaction(SIGTERM,&action2,NULL);
}
}

这样修改后,程序确实正常运行了。但是根据该书所述和在网上查询,始终未能理解为何这样操作,而且之后尝试赋其他值,依然正常运行。网上国内的氛围就是博客间互相复制互相抄袭,找不到多少主观的分析,所以一直未能解决,仅在此做下记录。