结构体 sigaction 详解
在 Unix 和类 Unix 系统(如 Linux)中,信号(signal)是操作系统与进程之间的一种异步通信机制。信号可以用于通知进程某些异步事件的发生,例如按下 Ctrl+C 触发的 SIGINT 信号,或者一个子进程结束时触发的 SIGCHLD 信号。为了处理这些信号,程序需要使用适当的信号处理机制。struct sigaction 是一种现代且灵活的方式来处理信号,与传统的 signal 函数相比,它提供了更高的控制能力。
什么是 struct sigaction
struct sigaction 是用来设置自定义的信号处理函数的结构体,它包含了信号处理函数以及一些控制信号处理行为的选项。它通常与 sigaction() 系统调用一起使用,用于捕捉和处理信号。
struct sigaction 的定义
在头文件 <signal.h> 中,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_handler:- 这是指向信号处理函数的指针。当某个信号发生时,操作系统会调用这个函数。
- 该函数接受一个
int类型的参数,表示信号编号(如SIGINT,SIGTERM等)。 - 可以将
sa_handler设置为以下几种值:- 自定义信号处理函数:程序自定义的函数,用于处理信号。
SIG_DFL:执行该信号的默认处理动作。SIG_IGN:忽略该信号。
示例:
void signal_handler(int signum) { printf("Received signal %d\n", signum); } -
sa_sigaction:- 这是
sa_handler的一个增强版本,适用于需要获取更详细信号信息的情况。 - 当
sa_flags中设置了SA_SIGINFO标志时,sa_sigaction会被调用,而不是sa_handler。 sa_sigaction提供了三个参数:int:表示信号编号。siginfo_t *:提供关于信号的更多详细信息,如信号来源、进程ID等。void *:提供与信号相关的上下文信息(如 CPU 寄存器的状态)。
示例:
void extended_signal_handler(int signum, siginfo_t *info, void *context) { printf("Received signal %d from process %d\n", signum, info->si_pid); } - 这是
-
sa_mask:sa_mask是一个sigset_t类型的信号集,用于指定在处理当前信号时,应该被阻塞的其他信号。- 在信号处理程序运行时,
sa_mask中的信号会被暂时阻塞,以防止它们中断当前的信号处理。 - 可以通过
sigemptyset()清空信号集,或通过sigaddset()添加需要阻塞的信号。
示例:
sigemptyset(&act.sa_mask); // 清空信号掩码 sigaddset(&act.sa_mask, SIGTERM); // 阻塞 SIGTERM 信号 -
sa_flags:sa_flags用于设置信号处理行为的标志位,可以取多个值:SA_RESTART:让被信号中断的系统调用自动重启。SA_SIGINFO:启用sa_sigaction处理信号,而非sa_handler。SA_NOCLDSTOP:如果信号为SIGCHLD,当子进程暂停时,不发送此信号。
示例:
act.sa_flags = SA_RESTART; // 启用系统调用自动重启 -
sa_restorer:- 这是一个过时的字段,通常不需要设置和使用。它曾经用于指定信号处理函数返回时的清理函数,但现在已经被废弃。
使用 struct sigaction 设置信号处理程序
要使用 sigaction 处理信号,首先需要定义一个 struct sigaction 结构体,然后使用 sigaction() 系统调用来注册信号处理程序。
示例代码
以下是一个完整的使用 sigaction 捕捉 SIGINT 信号(通常是按下 Ctrl+C 时触发的信号)的示例:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void signal_handler(int signum) {
printf("Caught signal %d\n", signum);
}
int main() {
struct sigaction act;
// 指定处理函数
act.sa_handler = signal_handler;
// 清空信号掩码,表示不阻塞任何信号
sigemptyset(&act.sa_mask);
// 使用默认标志
act.sa_flags = 0;
// 注册 SIGINT 信号的处理程序
sigaction(SIGINT, &act, NULL);
// 无限循环,等待信号
while (1) {
printf("Waiting for signal...\n");
sleep(1);
}
return 0;
}
输出:
当按下 Ctrl+C 时,程序将输出:
Waiting for signal...
Caught signal 2
其中,2 是 SIGINT 信号的编号。
总结
struct sigaction 提供了灵活且强大的信号处理机制,使程序可以在接收到信号时执行自定义的处理函数。与传统的 signal() 函数相比,它提供了更多的选项,如信号掩码、扩展的 sa_sigaction 和更多的控制标志。通过 sigaction,程序可以在异步事件发生时做出更精细和可靠的处理,广泛应用于系统级编程、网络服务等需要信号管理的领域。