在 C 语言的 Linux 环境中,注册异常处理函数通常是通过 signal () 或 sigaction () 系统调用来完成的。这些函数可以捕获特定的信号,并指定当信号发生时应该执行的处理函数。
使用 signal () 注册信号处理函数
使用 sigaction ()
注册信号处理函数
相比 signal ()
,sigaction ()
是更推荐的方式,因为它提供了更多的控制和功能(例如可以重新定义信号处理行为,并保证信号的可靠传递)。
两种方法的区别
signal ()
是传统的方法,使用起来简单,但行为在不同的平台和情况下可能不一致。
sigaction ()
提供了更强的控制能力和一致性,特别是在处理复杂信号时。比如,sigaction ()
可以指定额外的信息(如 siginfo_t)并提供更多的处理选项。
在上述例子中,当进程收到 SIGSYS 信号时,信号处理函数将被调用。
sigaction()
是一个功能强大的函数,用于在 Linux/Unix 系统中处理信号。与传统的 signal()
相比,sigaction()
提供了更多的功能和更精确的控制。
sigaction()
函数原型
signum
:要捕获的信号编号,如SIGINT
、SIGTERM
、SIGSYS
等。act
:指向包含新信号处理动作的sigaction
结构体的指针。oldact
:如果不为NULL
,则存储旧的信号处理动作,允许恢复原有的信号处理函数。
sigaction
结构体
sa_handler
:指向信号处理函数的指针。当信号触发时,会调用这个函数。它只接受信号编号作为参数。sa_sigaction
:指向带扩展信息的信号处理函数,它允许接收更多参数,例如信号的来源和上下文信息。要使用这个字段,需要设置sa_flags
标志为SA_SIGINFO
。sa_mask
:定义在处理当前信号时,哪些其他信号将被阻塞。sa_flags
:用于设置信号处理的一些行为,常用的标志有:SA_SIGINFO
:表示使用sa_sigaction
代替sa_handler
。SA_RESTART
:表示被信号中断的系统调用应自动重启。SA_NODEFER
:在信号处理期间,不自动阻塞正在处理的信号。SA_NOCLDWAIT
:当处理SIGCHLD
时,子进程终止后不产生僵尸进程。
示例:使用 sigaction()
捕获信号
代码详解
-
memset(&sa, 0, sizeof(sa));
:首先清空sigaction
结构体,确保所有字段初始化为零。 -
sa.sa_sigaction = signal_handler;
:将sa_sigaction
设置为自定义的信号处理函数signal_handler
,以便在接收到信号时调用。 -
sa.sa_flags = SA_SIGINFO;
:使用SA_SIGINFO
标志,以便sigaction()
调用sa_sigaction
而不是sa_handler
。通过这种方式,信号处理函数可以接收更多的上下文信息(例如,哪个进程发送了信号)。 -
sigaction(SIGINT, &sa, NULL);
:调用sigaction()
注册信号处理程序,捕获SIGINT
信号(当用户按下 Ctrl+C 时会触发)。 -
while(1)
:程序进入一个无限循环,等待信号。每次收到信号时,会调用signal_handler
并打印相关信息。
siginfo_t
结构体
当使用 SA_SIGINFO
标志时,信号处理函数的第二个参数是指向 siginfo_t
结构体的指针,包含了关于信号的详细信息:
常用标志和行为
-
SA_RESTART
:让被信号中断的系统调用自动重启,而不是返回错误。这对于保持程序流畅运行非常有用。 -
SA_NODEFER
:默认情况下,当信号处理函数运行时,处理该信号的信号会被阻塞。设置此标志可以让信号处理函数在运行期间接受相同的信号。 -
SA_NOCLDWAIT
:如果进程捕获SIGCHLD
信号,此标志会禁止生成僵尸进程,而不需要调用wait()
。
使用 sigaction()
的优点
-
更强的控制:通过
sa_mask
可以定义在处理特定信号时应该阻塞哪些其他信号。 -
可靠性:与
signal()
相比,sigaction()
在某些复杂情况下更加可靠,不会因为内核版本或平台不同而行为不一致。 -
支持信号信息:通过
siginfo_t
提供更多关于信号的详细信息。
总结
sigaction()
是一个功能强大且灵活的信号处理机制,允许你在捕获信号时做更多的事情,如访问发送信号的进程信息、阻塞其他信号、自动重启被中断的系统调用等。