struct sigaction
是在 UNIX 和类 UNIX 操作系统(如 Linux)中用于信号处理的一个重要结构体。信号是操作系统和进程之间的一种通信机制,可以用来通知进程发生了某种事件,例如用户按下了中断键(通常是 Ctrl+C),某个进程试图访问非法内存区域等。
在 Linux 系统中,每一个信号都有一个默认的处理方式,但有时候程序开发者希望改变信号的默认处理行为,例如,在收到某个特定信号时执行一个特定的函数。为了实现这一需求,提供了 struct sigaction
结构体通过 sigaction()
系统调用为信号设置信号处理程序。
在 POSIX.1 标准中,struct sigaction
通常被定义在 <signal.h>
头文件中,定义可能因操作系统不同而略有差异。但通常,该结构体包括以下成员:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
sa_handler:
int
类型参数,即信号的编号。SIG_IGN
,表示忽略该信号。SIG_DFL
,则表示使用该信号的默认处理方式。sa_sigaction:
sa_handler
互斥。它可以提供信号的更多信息。例如,信号的来源等。sa_sigaction
需要同时设置 sa_flags
的 SA_SIGINFO
标志。sa_mask:
sa_flags:
SA_RESTART
: 使被中断的系统调用自动重新启动。SA_NOCLDSTOP
: 子进程停止时不产生 SIGCHLD
信号。SA_NOCLDWAIT
: 子进程退出时不创建僵尸进程。SA_NODEFER
: 允许在处理信号时递归处理该信号。SA_SIGINFO
: 使用 sa_sigaction
而不是 sa_handler
作为信号处理程序。sa_restorer:
为了设置信号处理程序,需要使用 sigaction()
系统调用,函数原型如下:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
signum
: 信号编号,例如 SIGINT
。act
: 指向一个 struct sigaction
的指针,描述如何处理该信号。oldact
: 如果不为 NULL
,则用于保存先前的信号处理设置信息。以下是一个简单的使用 sigaction
设置信号处理程序的示例:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
void handler(int signum) {
printf("Received signal %d\n", signum);
}
int main() {
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = handler;
act.sa_flags = SA_RESTART; // 自动重新启动被中断的系统调用
if (sigaction(SIGINT, &act, NULL) < 0) {
perror("sigaction");
exit(EXIT_FAILURE);
}
printf("Press Ctrl+C to trigger SIGINT...\n");
while (1) {
pause(); // 等待信号
}
return 0;
}
在该示例中,信号处理程序 handler
将会在 SIGINT 信号(如 Ctrl+C)发送到进程时被调用。信号处理程序只是简单地打印接收到的信号编号。
很多初学者会疑惑 sigaction
与传统的 signal
函数的区别,signal
是一种更简单直观的 API,但 sigaction
提供了更强大和灵活的信号处理功能,特别是在控制信号屏蔽、支持多个信号处理模式(通过标志选项)以及避免信号处理中的竞态条件等方面。
综上所述,struct sigaction
和 sigaction()
系统调用构成了 UNIX 系统中信号处理的基石,可以帮助开发者细粒度地控制信号处理,使应用程序更稳定和安全。这也反映了 UNIX 哲学中给用户提供原动力和灵活性的设计理念。