Linux 系统调用 sigaction

信号集合(sigset_t)

关于这个数据结构的字义,我在 glibc 的源码中找到了如下的定义:

1
2
3
typedef __sigset_t sigset_t;

typedef unsigned long int __sigset_t;

虽然因为多平台的原因还有其他的定义方式,但这些定义已经足够我们对这个结构有一个感
性的认识了。也可以猜测 Linux 平台下的信号种类不太可能超过 64 个。

信号集合有一组相关的函数如下:

1
2
3
4
5
6
7
8
9
10
11
#include <signal.h>

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signum);

int sigdelset(sigset_t *set, int signum);

int sigismember(const sigset_t *set, int signum);

struct sigaction结构体

man sigaction找到如下定义:

1
2
3
4
5
6
7
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_handlersa_sigaction只有一个有效,这取决于sa_flags中是否设置了
SA_SIGINFO,如果没有设置则使用sa_handler,否则使用sa_sigaction。实际上,我
在 glibc 的源码中看到这两个变量是定义在一个 union 里的,所以对两个都进行设置的话
应该是不行的(并没有测试过)。我找到的源码如下,你可以尝试自己解读:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct sigaction
{
/* Signal handler. */
#ifdef __USE_POSIX199309
union
{
/* Used if SA_SIGINFO is not set. */
__sighandler_t sa_handler;
/* Used if SA_SIGINFO is set. */
void (*sa_sigaction) (int, siginfo_t *, void *);
}
__sigaction_handler;
# define sa_handler __sigaction_handler.sa_handler
# define sa_sigaction __sigaction_handler.sa_sigaction
#else
__sighandler_t sa_handler;
#endif

/* Additional set of signals to be blocked. */
__sigset_t sa_mask;

/* Special flags. */
int sa_flags;
};

注意sa_handlersa_sigaction的函数签名是不一样的,对应了旧的和新的两种信号处
理机制。sa_sigaction的第二个参数可以携带一些信息,它的定义大概是(man sigaction):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count;
POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
long si_band; /* Band event (was int in
glibc 2.3.2 and earlier) */
int si_fd; /* File descriptor */
short si_addr_lsb; /* Least significant bit of address
(since Linux 2.6.32) */
void *si_lower; /* Lower bound when address violation
occurred (since Linux 3.19) */
void *si_upper; /* Upper bound when address violation
occurred (since Linux 3.19) */
int si_pkey; /* Protection key on PTE that caused
fault (since Linux 4.6) */
void *si_call_addr; /* Address of system call instruction
(since Linux 3.5) */
int si_syscall; /* Number of attempted system call
(since Linux 3.5) */
unsigned int si_arch; /* Architecture of attempted system call
(since Linux 3.5) */
};

虽然手册里给出的成员是这么多,但是我尝试的过程中发现编译器报错说某些不存在,我也
不清楚是怎么回事。

sa_sigaction的第三个参数是一个指向ucontext_t的指针,被转换为了void*,一般
这个参数并不使用(手册里说的。。。),关于这个参数更详细的解释可以使用命令man 3 getcontext查看。

sigaction函数

1
2
3
4
#include <signal.h>

int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);

如果第二个参数act不为NULLsignum的处理方式就被设置为act所指定的,如果第三个
参数oldact不为NULL,旧的处理方式就被保存在其中。下面是一个例子,演示基本用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void handler(int signum, siginfo_t *siginfo, void *ucontext);
void init_sigaction(struct sigaction *act);

int main(int argc, char *argv[])
{
struct sigaction act;
init_sigaction(&act);

printf("pid is: [%d]\n", getpid());

if (sigaction(SIGQUIT, &act, NULL) == -1) {
perror("sigaction error");
exit(1);
}
raise(SIGQUIT); /* send SIGQUIT to itself */
printf("exiting...\n");
return 0;
}

void init_sigaction(struct sigaction *act)
{
act->sa_sigaction = handler;
sigemptyset(&act->sa_mask);
act->sa_flags = SA_SIGINFO;
}

void handler(int signum, siginfo_t *siginfo, void *ucontext)
{
printf("handler called\n");
printf("signum(SIGQUIT) is: %d\n", signum);
printf("information in siginfo is:\n");
printf("\tsi_signo: %d\n", siginfo->si_signo );
printf("\tsi_errno: %d\n", siginfo->si_errno );
printf("\tsi_code: %d\n", siginfo->si_code );
/* printf("\tsi_trapno: %d\n", siginfo->si_trapno); */
printf("\tsi_pid: %d\n", siginfo->si_pid );
printf("\tsi_uid: %d\n", siginfo->si_uid );
printf("\tsi_status: %d\n", siginfo->si_status);
printf("\tsi_utime: %ld\n", siginfo->si_utime );
printf("\tsi_stime: %ld\n", siginfo->si_stime );
/* printf("\tsi_value: %d\n", siginfo->si_value ); */
printf("handler exiting...\n");
}

在我电脑上的执行结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ clang sigaction.c
$ ./a.out
pid is: [2972]
handler called
signum(SIGQUIT) is: 3
information in siginfo is:
si_signo: 3
si_errno: 0
si_code: -6
si_pid: 2972
si_uid: 1000
si_status: 0
si_utime: 0
si_stime: 0
handler exiting...
exiting...
0%