用sigaction编写自己的信号处理函数
信号处理函数在调试中的重要性可不亚于程序员杯中的咖啡。当你遇到段错误(Segment Fault)时,通过重写信号处理函数,不仅能捕获错误地址,还能像侦探一样顺藤摸瓜,找到问题的根源。比如,sigaction函数就是你手中的“放大镜”,它允许你自定义信号处理逻辑,帮助你深入挖掘问题。
那么,问题来了:为什么我们不直接用默认的信号处理函数呢?答案是,默认处理函数就像是个“沉默的目击者”,只告诉你出事了,但不会告诉你出了什么事。通过自定义处理函数,你不仅能知道出错的地址(si_addr),还能得到出错码(si_code),简直是调试的一把利器。
举个例子,当你想访问一个非法内存地址时,sigaction会通过si_addr告诉你:“喂,你访问的地址是非法的!”这样你就能迅速定位问题。就像你在代码中访问了地址0xA,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这个地址。