用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 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) */
...
}

这里列举了部分成员,对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这个地址。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
信号处理程序是在应用程序中处理操作系统发送的信号的函数。异步安全函数是指在多线程或者信号处理上下文中可以安全调用的函数。在信号处理程序中,由于信号的突发性和不可预测性,需要使用异步安全函数来保证程序的正确性和稳定性。 异步安全函数具有以下特点: 1. 不会修改全局状态或者共享数据:由于信号处理程序可能在任何时间点中断应用程序的正常执行,因此异步安全函数应该避免修改全局状态或者共享数据,以免引发竞态条件或者数据不一致性。 2. 不会调用不可重入函数:不可重入函数是指在执行过程中使用了全局或者静态变量的函数,由于信号处理程序可能在同一时间点被多个线程调用,因此异步安全函数应该避免调用不可重入函数,以免产生竞态条件或者数据不一致性。 3. 只使用异步信号安全的函数:异步信号安全的函数是指能够在信号处理上下文中安全调用的函数,这些函数通常是线程安全的,不会引发竞态条件或者数据不一致性。 常见的异步安全函数包括: - signal():用于设置信号处理程序。 - sigaction():用于设置信号处理程序,并提供更加灵活和可靠的信号处理方式。 - sigprocmask():用于操作进程的信号屏蔽字,可以设置在信号处理程序执行期间需要被屏蔽的信号集合。 - sigsuspend():用于挂起进程的执行,直到收到某个特定信号为止。 需要注意的是,在信号处理程序中只能调用异步安全函数,而不能调用非异步安全函数,否则可能会导致不可预料的行为或者错误。因此,在编写信号处理程序时,需要仔细选择调用的函数,并确保它们是异步安全的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值