如何才能让进程先捕捉SIGSEGV信号,打印出有用的方便定位问题的信息,然后再优雅地退出呢?

如何才能让进程先捕捉SIGSEGV信号,打印出有用的方便定位问题的信息,然后再优雅地退出呢?

程序实操

SIGSEGV针对段错误信号进行处理!代码如下:段错误位置是crash()

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
typedef struct _sig_ucontext
{
  unsigned long uc_flags;
  struct ucontext *uc_link;
  stack_t uc_stack;
  struct sigcontext uc_mcontext;
  sigset_t uc_sigmask;
} sig_ucontext_t;
void crit_err_hdlr(int sig_num, siginfo_t *info, void *ucontext)
{
  void *array[50];
  void *caller_address;
  char **messages;
  int size, i;
  sig_ucontext_t *uc;
  uc = (sig_ucontext_t *)ucontext;
  caller_address = (void *)uc->uc_mcontext.eip;
  fprintf(stderr, "signal %d (%s), address is %p from %p\n",
          sig_num, strsignal(sig_num), info->si_addr,
          (void *)caller_address);
  size = backtrace(array, 50);
  array[1] = caller_address;
  messages = backtrace_symbols(array, size);
  /* 跳过第一个栈帧 */
  for (i = 1; i < size && messages != NULL; ++i)
  {
    fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);
  }
  free(messages);
  exit(EXIT_FAILURE);
}
int crash()
{
  char *p = NULL;
  *p = 0;
  return 0;
}
int foo4()
{
  crash();
  return 0;
}
int foo3()
{
  foo4();
  return 0;
}
int foo2()
{
  foo3();
  return 0;
}
int foo1()
{
  foo2();
  return 0;
}
int main(int argc, char **argv)
{
  struct sigaction sigact;
  sigact.sa_sigaction = crit_err_hdlr;
  sigact.sa_flags = SA_RESTART | SA_SIGINFO;
  if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0)
  {
    fprintf(stderr, "error setting signal handler for %d (%s)\n",
            SIGSEGV, strsignal(SIGSEGV));
    exit(EXIT_FAILURE);
  }
  foo1();
  exit(EXIT_SUCCESS);
}

编译报错:test.c:29:46: error: ‘struct sigcontext’ has no member named ‘rip’
=> 这是因为机器平台架构的硬件寄存器对应不是这个名词,所以需要以下步骤修改:
1. uname -m查看架构或者是清楚目标机器的架构
2. 修改sig_ucontext_t 对应使用的成员变量,如下:

架构对应寄存器
x86_64架构(64位Intel/AMD) rip:在64位模式下,rip寄存器用于存储下一条要执行的指令的地址。
i686架构(32位x86)eip:在32位模式下,eip寄存器存储下一条要执行的指令的地址。这是rip在32位x86架构中的等价寄存器。
ARM架构 pc(Program Counter):在ARM架构中,pc寄存器存储下一条要执行的指令的地址。这相当于x86架构中的rip或eip。
其他架构对于其他处理器架构(如PowerPC、MIPS、RISC-V等),也有各自的指令指针寄存器,通常称为PC(Program Counter)或类似的名称。

所以我将使用的rip改成了eip,编译可以通过了
在这里插入图片描述
3. 定位段错误位置
方法一:objdump
objdump -d a.out | grep 80486c3定位了crash函数
在这里插入图片描述
方法二:rdynamic链接选项
gcc test.c -rdynamic

-rdynamic编译选项
通知链接器在生成可执行文件时,将所有全局符号(包括未导出的)加入到动态符号表中。这使得在运行时,通过dladdr()、backtrace()、backtrace_symbols()等相关函数可以获取到更详细的符号信息,包括函数名称。

在这里插入图片描述

定位sig_ucontext_t

其实sig_ucontext_t的定义之处,也可以查到当前所定义的成员变量名称
/usr/include目录下寻找sigcontext.h和ucontext.h
在这里插入图片描述

关于 struct sigcontext 支持的架构,它广泛应用于支持Linux操作系统的多种架构,包括但不限于:

x86 (i386, i686)
x86_64 (amd64)
ARM (arm, armv7, armv8)
AArch64 (arm64)
PowerPC (ppc, ppc64, ppc64le)
MIPS (mips, mipsel, mips64, mips64el)
RISC-V (rv32, rv64)

总结

不过!!!你会发现程序一般不会对SIGSEGV信号进行处理,那是因为SIGSEGV是不可靠信号(1-31)
不可靠信号无法保证一定会被处理!!!所以在程序崩溃段错误这种情况下,用了也可能是白用。。。。。。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值