struct sigaction
是 Linux/Unix 系统中用于设置信号处理的关键数据结构,其定义和用法直接影响信号的行为。以下是详细解析:
1. struct sigaction
结构体定义
#include <signal.h>
struct sigaction {
void (*sa_handler)(int); // 传统信号处理函数(如 SIG_DFL/SIG_IGN)
void (*sa_sigaction)(int, siginfo_t *, void *); // 扩展信号处理函数
sigset_t sa_mask; // 执行处理函数时要阻塞的信号集
int sa_flags; // 控制信号行为的标志位
};
关键字段说明:
字段 | 作用 |
---|---|
sa_handler |
简单信号处理函数(如 SIG_DFL 、SIG_IGN 或自定义函数) |
sa_sigaction |
支持附加信息的信号处理函数(需设置 SA_SIGINFO ) |
sa_mask |
在执行当前处理函数时自动阻塞的信号集(防止重入) |
sa_flags |
控制信号行为(如 SA_RESTART 、SA_SIGINFO ) |
2. sa_flags
常用标志
标志 | 作用 |
---|---|
SA_RESTART |
自动重启被中断的系统调用(避免慢速调用因信号返回错误) |
SA_SIGINFO |
使用 sa_sigaction() (而非 sa_handler() ),可获取更多信息 |
SA_NOCLDSTOP |
子进程停止时不发送 SIGCHLD |
SA_NODEFER/SA_NOMASK |
不阻塞当前正在处理的信号(默认会阻塞,防止重入) |
3. 典型用法示例
(1) 基本用法:捕获 SIGINT (Ctrl+C)
#include <signal.h>
#include <stdio.h>
void handler(int sig) {
printf("Received SIGINT\n");
}
int main() {
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask); // 不额外阻塞其他信号
sa.sa_flags = SA_RESTART; // 重启被中断的系统调用
if (sigaction(SIGINT, &sa, NULL) == -1) {
perror("sigaction");
return -1;
}
while(1); // Keep running to test signal
}
(2) 高级用法:获取信号的详细信息 (siginfo_t
)
void handler(int sig, siginfo_t *info, void *ucontext) {
printf("Signal %d from PID %d\n", info->si_signo, info->si_pid);
}
int main() {
struct sigaction sa;
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO; // Enable extended signal handling
if (sigaction(SIGTERM, &sa, NULL) == -1) {
perror("sigaction");
return -1;
}
}
4. system()
中的特殊处理
在标准库的 system()
实现中:
- 临时忽略 SIGINT/SIGQUIT:
防止用户在终端按 Ctrl+C (SIGINT
)终止父进程。 - 阻塞 SIGCHLD:
避免子进程退出时与父进程的其他部分产生竞争条件。
// system()内部伪代码片段:
struct sigaction ignore_action;
ignore_action.sa_handler = SIG_IGN;
sigemptyset(&ignore_action.sa_mask);
ignore_action.sa_flags = SA_RESTART;
// Ignore SIGINT and SIGQUIT during command execution
sigaction(SIGINT, &ignore_action, &old_int);
sigaction(SIGQUIT, &ignore_action, &old_quit);
// Block SIGCHLD to prevent race conditions
sigprocmask(SIG_BLOCK, &chld_set, &old_mask);
// ... fork() + exec() ...
// Restore original handlers and mask after execution completes.
5. FAQ
(1) Why block signals in sa_mask
?
- 防止重入问题:若在处理某个信号时,同一信号再次触发可能导致递归崩溃。
- Example:
若正在处理SIGHUP
,默认情况下新到达的SIGHUP
会被阻塞,直到当前处理完成。
(2)SA_RESTART vs SA_NODEFER
:
- ✅ Use
SA_RESTART
:
适用于需要稳定运行的网络服务等场景。 - ❌ Avoid for time-critical operations:
某些系统调用不应被重启(如定时器相关)。
(3)siginfo_t *info
: What’s inside?
typedef struct {
int si_signo; /* Signal number */
pid_t si_pid; /* Sending process ID */
void *si_addr; /* Faulting memory address (e.g., for SIGSEGV)*/
} siginfo_t;
完整定义见 man sigaction(2)
.
6. Debugging Tips
(1) Check current signal handlers:
gdb -p <PID>
(gdb) p *(struct sigaction*)0x7ffd12345678 # Examine a saved struct
# Or via /proc:
cat /proc/<PID>/status | grep SigCgt # Hex mask of caught signals
(2) Trace signal modifications:
strace -e trace=rt_sig* ./your_program # Monitor all signal syscalls
掌握这些细节后,可以更精准地控制程序对信号的响应行为!
内容由零声教学AI助手提供,问题来源于学员提问