在Linux系统编程中,信号(signal)是一种重要的进程间通信机制,用于通知进程发生了某种事件,例如终止请求、定时器触发、非法操作等。传统的信号处理函数如 signal() 可以注册简单的信号处理程序,但在实际开发中存在一些局限性,比如信号中断、无法携带额外信息、对可重入函数的限制等。为了解决这些问题,Linux提供了功能更强大的 sigaction 函数,使开发者能够精细控制信号行为,确保程序的稳定性与可预测性。

首先,sigaction 的基本作用是为指定信号注册处理程序。它的函数原型如下:
其中,signum 表示信号编号,act 指向新的信号处理设定,oldact 用于保存之前的设定。通过 sigaction,开发者可以指定信号的处理方式,例如调用自定义函数处理、忽略信号或恢复默认行为。相比 signal(),sigaction 提供了更多控制选项,例如阻塞其他信号、指定信号处理函数执行时的附加信息等。
struct sigaction 是 sigaction 的核心结构体,主要成员包括:
sa_handler / sa_sigaction:指向信号处理函数的指针。sa_handler 适用于简单处理函数,而 sa_sigaction 可以接收三个参数,包括信号编号、信号附加信息以及上下文指针,适合复杂场景。
sa_mask:定义在处理当前信号时需阻塞的其他信号集合。通过合理设置,可以避免处理函数被其他信号打断,从而保证关键操作的原子性。
sa_flags:用于设置处理行为,如 SA_SIGINFO 表示使用 sa_sigaction,SA_RESTART 可以自动重启被信号中断的系统调用,SA_NOCLDSTOP 可避免子进程停止或继续时产生 SIGCHLD 信号等。
通过这些成员,开发者可以实现灵活而安全的信号处理。例如,在需要获取发送信号的进程PID或附加数据时,可以使用 SA_SIGINFO 配合 siginfo_t 结构体,实现更丰富的信号处理逻辑。
应用场景方面,sigaction 常用于以下几类任务:
守护进程和后台服务:在守护进程中,利用 sigaction 捕捉 SIGTERM 或 SIGINT 信号,实现平滑关闭、资源释放和日志记录。
定时任务与信号驱动IO:结合 SIGALRM 或实时信号(SIGRTMIN+n),开发者可以实现高精度定时触发或异步事件处理。
子进程管理:通过捕获 SIGCHLD 信号,可以在子进程结束时及时回收资源,避免僵尸进程产生。
错误处理与安全控制:捕获 SIGSEGV、SIGFPE 等信号,可以记录异常信息、生成核心转储,辅助调试和安全防护。
与传统 signal() 函数相比,sigaction 提供了原子性和可重入性保障。传统 signal() 在处理某些信号时可能被同类信号打断,导致竞态条件,而 sigaction 的 sa_mask 能有效阻塞相关信号,保证处理函数的完整执行。此外,SA_RESTART 选项在系统调用被信号打断时可自动重试,提升程序鲁棒性。
值得注意的是,信号处理函数本身有一定限制。由于信号可能在任意时间打断程序运行,因此处理函数中应尽量避免调用非可重入函数(如 printf、malloc 等),以防出现死锁或数据破坏。通常,处理函数中只进行简单操作,如设置标志位、写入管道或调用可重入函数,然后在主循环中处理实际业务逻辑。
综上所述,Linux sigaction 函数是信号处理的强大工具,它不仅替代了传统 signal() 的功能,更提供了灵活、可控、安全的信号管理手段。无论是守护进程、异步事件处理,还是子进程管理和异常捕获,sigaction 都能够保证程序在面对信号干扰时依然稳定运行。
掌握 sigaction 的使用,不仅是Linux系统编程的基础技能,也是开发高可靠、高性能软件的关键环节。