> 文章列表 > 用sigaction编写自己的信号处理函数

用sigaction编写自己的信号处理函数

用sigaction编写自己的信号处理函数

重写signal handler在debug的时候非常有用,比如发生了segment fault,如果想知道内存访问出错的地址,就可以通过写自己的信号处理函数,打印出错地址。

首先看sigaction函数。通过man sigaction可以看到详细信息。

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

第一个参数是指要处理的信号,第二个参数是sigaction 结构体,包含具体的handler信息,以下:

struct sigaction {void     (*sa_handler)(int);void     (*sa_sigaction)(int, siginfo_t *, void *);sigset_t   sa_mask;int        sa_flags;void     (*sa_restorer)(void);};

第一个和第二个参数都是handle,但是只能同时指定一个。第二个可以得到更多的信息。其他参数可以查看man。我们再重点介绍以下siginfo_t这个结构。

siginfo_t {int      si_signo;     /* Signal number */int      si_code;      /* Signal code */pid_t    si_pid;       /* Sending process ID */uid_t    si_uid;       /* Real user ID of sending process */int      si_status;    /* Exit value or signal */sigval_t si_value;     /* Signal value */void    *si_addr;      /* Memory location which caused fault */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 violationoccurred (since Linux 3.19) */void    *si_upper;     /* Upper bound when address violationoccurred (since Linux 3.19) */int      si_pkey;      /* Protection key on PTE that causedfault (since Linux 4.6) */
...
}

这里列举了部分成员,对debug很重要的是si_addr 和si_code。前者说明了出错的地址,后者提供出错码,结合信号可以查到具体的原因。 

example

#include <signal.h>
#include "stdio.h"void
handler(int sig, siginfo_t *info, void *ucontext)
{printf("si_signo: %d, si_code: %d, si_pid: %d, si_value: %d, si_addr: %p\\n", info->si_signo, info->si_code, info->si_pid, info->si_value, info->si_addr);signal(sig, SIG_DFL);
}int main() {struct sigaction act;act.sa_sigaction = handler;act.sa_flags = SA_SIGINFO;sigaction(SIGSEGV, &act, NULL);int *p=(int *) 10;*p = 123;return 0;
}

编译运行,得到:

si_signo: 11, si_code: 1, si_pid: 10, si_value: 0, si_addr: 0xa

si_addr就是出错地址,可以看到我们确实是因为非法访问了10这个地址。