函数原型
#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
signum
:指定信号编号,如SIGINT
、SIGTERM
等。
handler
:是一个函数指针,指向信号处理函数,信号处理函数只有一个int
类型参数,用于接收信号编号。返回值也是一个函数指针,指向旧的信号处理函数。
sigaction
函数原型:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
signum
:同样指定信号编号。
act
:指向一个struct sigaction
结构体,用于设置新的信号处理方式。
oldact
:指向一个struct sigaction
结构体,用于保存旧的信号处理方式,可以为NULL
。struct sigaction
结构体定义如下:
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
:与signal
函数中的handler
类似,是一个指向信号处理函数的指针。
sa_sigaction
:另一种信号处理函数指针,适用于需要更多信号信息的情况,其参数比sa_handler
指向的函数多了siginfo_t *
和void *
。
sa_mask
:在信号处理函数执行期间要阻塞的信号集。
sa_flags
:用于设置信号处理的一些选项,如SA_RESTART
、SA_SIGINFO
等。
信号处理语义
signal
:
- 简单易用,主要用于基本的信号处理。当信号发生时,系统会跳转到
handler
指定的函数执行,执行完毕后通常恢复到信号发生前的执行点继续执行。但是在某些系统上,使用signal
设置信号处理函数后,信号处理函数执行一次后,信号处理方式可能会被重置为默认行为(如在早期的UNIX系统),现在大多数系统已改进此行为,但这仍带来一定不确定性。
- 信号处理函数只能接收信号编号作为参数,无法获取信号的其他详细信息。
sigaction
:
- 功能更强大和灵活。可以通过
sa_mask
指定在信号处理函数执行期间要阻塞的其他信号,防止在处理一个信号时又被其他信号打断,影响处理流程。
sa_flags
提供了丰富的选项,如SA_RESTART
可使被信号中断的系统调用自动重启;SA_SIGINFO
使得可以使用sa_sigaction
指定的信号处理函数,从而获取关于信号的详细信息,如信号发送进程ID、发送信号的用户ID等。
可移植性
signal
:虽然signal
函数在POSIX标准中定义,但由于历史原因,不同UNIX系统对signal
的实现存在差异,尤其是关于信号处理函数执行一次后是否重置为默认行为方面。因此,signal
的可移植性相对较差,在编写跨平台代码时需要格外小心。
sigaction
:sigaction
是POSIX标准推荐使用的函数,其行为在不同POSIX兼容系统上表现一致,具有良好的可移植性,更适合编写可移植的代码。在现代的UNIX和Linux系统开发中,sigaction
被广泛使用。