struct sigaction

张开发
2026/6/25 14:47:45 15 分钟阅读
struct sigaction
1 是什么struct sigaction 是 POSIX 标准 提供的用于高级信号处理的核心数据结构。 它配合 sigaction() 系统调用使用允许进程精确、安全地控制收到特定信号时的行为。 相比传统的 signal() 函数它提供了更强的可控性、可移植性和安全性 是现代 C 语言网络服务、守护进程、多进程程序中处理信号的推荐方式。#includesignal.hstructsigaction{void(*sa_handler)(int);// 传统信号处理函数void(*sa_sigaction)(int,siginfo_t*,void*);// 扩展信号处理函数sigset_tsa_mask;// 信号屏蔽字intsa_flags;// 控制行为的标志位void(*sa_restorer)(void);// 内部使用现代编程不应直接操作};2 字段详解#1 sa_handler 函数指针指向返回值 void、参数为 int信号值的函数 这是传统的信号处理方式。当信号到达时内核调用此函数并传入触发的信号编号#2 sa_sigaction 函数指针指向参数列表更丰富的函数void func(int signo, siginfo_t *info, void *context)。 启用条件仅当 sa_flags 中设置了 SA_SIGINFO 标志时此字段才生效。 扩展参数详解 int signo 同 sa_handler 的参数即信号编号。 siginfo_t *info 指向一个包含信号详细上下文信息的结构体指针。 这是 sa_sigaction 的核心优势你可以从中得知 si_signo 信号编号。 si_code 信号来源代码。 例如SI_USER用户调用 kill 发送、 SI_KERNEL内核产生如段错误、 SI_TIMER定时器超时、 SI_QUEUE实时信号带数据发送。 si_pid发送信号的进程 ID。 si_uid发送信号进程的真实用户 ID。 void *context 指向 ucontext_t 的指针需要定义 _GNU_SOURCE 包含了信号发生瞬间的 CPU 寄存器状态和堆栈信息。 这对于实现协程调度、崩溃现场捕获或高级调试器极其关键。 注意由于 sa_handler 和 sa_sigaction 共用存储 不要试图同时给两个字段赋值也不要试图调用错误的函数指针。 只要 SA_SIGINFO 没设置内核就只会访问 sa_handler。#3 sa_mask 作用 定义在执行信号处理函数期间除了当前触发的信号本身之外还需要额外阻塞哪些信号。 工作机制 信号处理函数开始执行前 内核自动将 sa_mask 中指定的信号集加入到进程的阻塞信号集中 也会自动阻塞当前信号本身除非设置了 SA_NODEFER。 处理函数执行期间这些被屏蔽的信号即使递送到进程也不会中断处理函数它们会进入挂起状态。 处理函数正常返回后内核自动恢复原来的信号屏蔽字挂起的信号得以递送。 编程意义 原子性保护。假设你处理 SIGTERM 时需要写一个关键文件 为了防止在写的过程中又收到 SIGINT 导致数据损坏你应该在 sa_mask 中包含 SIGINT。 处理完后自动恢复原屏蔽状态。#4 sa_flags 这是 struct sigaction 中最复杂也最强大的字段它是一个按位或|组合的整型掩码。 常用标志详解如下 SA_SIGINFO 模式切换。 设置此标志后内核将调用 sa_sigaction 字段指向的函数 而非 sa_handler。 这是获取信号详细信息如发送者 PID、内存地址的唯一途径。 SA_RESTART 重启系统调用。 如果信号中断了某个“慢速”系统调用如 read、write、accept、sleep 且该调用返回了 EINTR 错误设置此标志后内核会自动重启该调用 而不是让函数返回错误给应用层。 这极大简化了网络服务程序的处理逻辑。 SA_NODEFER 允许嵌套No Defer。 默认情况下当进入信号处理函数时内核会自动阻塞当前正在处理的信号防止同一信号递归调用导致栈溢出。 设置此标志后内核不再自动阻塞当前信号这意味着如果在处理函数返回前又收到同一个信号 它会嵌套调用处理函数。 不推荐普通场景使用除非明确知道递归安全。 SA_RESETHAND 一次性处理Reset Handler。 在信号处理函数入口处内核会将信号处理方式重置为 SIG_DFL默认行为。 这是为了兼容早期不可靠的 UNIX signal 函数行为。 除非你有特殊的一次性捕获需求且能接受竞态条件风险否则不建议使用。 SA_ONSTACK 使用备用栈。 如果进程使用 sigaltstack() 申请了一块备用内存栈 设置此标志后信号处理函数的栈帧将在备用栈上分配而非当前线程的常规栈。 主要用于处理 SIGSEGV段错误 当程序因栈溢出而触发 SIGSEGV 时常规栈已满无法执行代码 此时必须在备用栈上运行处理逻辑才能捕获错误并安全退出。 SA_NOCLDSTOP 子进程专用SIGCHLD。 仅对 SIGCHLD 信号有效。 如果设置当子进程停止SIGSTOP或恢复SIGCONT时 不会向父进程发送 SIGCHLD 通知。 仅当子进程退出时才发送通知。 SA_NOCLDWAIT 子进程专用SIGCHLD。 如果设置当子进程退出时不会产生僵尸进程。 父进程无需调用 wait() 或 waitpid() 来清理资源内核会自动回收。 如果此时 sa_handler 设置为了 SIG_DFL该标志确保不产生僵尸进程 如果 sa_handler 为自定义函数行为略有差异通常用于防止僵尸进程堆积。#5 sa_restorer 供底层 C 库恢复执行上下文的内部函数指针 应用程序切勿手动赋值应置为 NULL 或忽略总结对比表 以下是对四个核心字段作用的概括 sa_handler 执行层 收到信号时调用哪段代码 sa_sigaction 执行层 高级模式 不仅调用代码还要告诉我谁发的、为什么发、当时的寄存器长什么样 sa_mask 安全层 在处理这段代码的过程中禁止哪些信号来捣乱 sa_flags 策略层 处理完后要不要重置被中断的 read 要不要接着读这段代码的栈是放在原地还是备用空间3 sigactionintsigaction(intsignum,conststructsigaction*act,structsigaction*oldact);signum要配置的信号编号如 SIGINT, SIGTERM, SIGCHLD 等 act新的信号处理配置。传 NULL 表示不修改仅用于查询 oldact用于保存旧的配置。传 NULL 表示不保存 成功返回 0失败返回 -1 并设置 errno

更多文章