0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

在Linux上什么是段错误?如何获得一个核心转储?

Linux爱好者 来源:未知 作者:李倩 2018-07-16 16:53 次阅读

本周工作中,我花了整整一周的时间来尝试调试一个段错误。我以前从来没有这样做过,我花了很长时间才弄清楚其中涉及的一些基本事情(获得核心转储、找到导致段错误的行号)。于是便有了这篇博客来解释如何做那些事情!

在看完这篇博客后,你应该知道如何从“哦,我的程序出现段错误,但我不知道正在发生什么”到“我知道它出现段错误时的堆栈、行号了! ”。

什么是段错误?

“段错误segmentation fault”是指你的程序尝试访问不允许访问的内存地址的情况。这可能是由于:

试图解引用空指针(你不被允许访问内存地址0);

试图解引用其他一些不在你内存(LCTT 译注:指不在合法的内存地址区间内)中的指针;

一个已被破坏并且指向错误的地方的 C++ 虚表指针C++ vtable pointer,这导致程序尝试执行没有执行权限的内存中的指令;

其他一些我不明白的事情,比如我认为访问未对齐的内存地址也可能会导致段错误(LCTT 译注:在要求自然边界对齐的体系结构,如 MIPS、ARM 中更容易因非对齐访问产生段错误)。

这个“C++ 虚表指针”是我的程序发生段错误的情况。我可能会在未来的博客中解释这个,因为我最初并不知道任何关于 C++ 的知识,并且这种虚表查找导致程序段错误的情况也是我所不了解的。

但是!这篇博客后不是关于 C++ 问题的。让我们谈论的基本的东西,比如,我们如何得到一个核心转储?

运行 valgrind

我发现找出为什么我的程序出现段错误的最简单的方式是使用valgrind:我运行

valgrind -vyour-program

这给了我一个故障时的堆栈调用序列。 简洁!

但我想也希望做一个更深入调查,并找出些valgrind没告诉我的信息! 所以我想获得一个核心转储并探索它。

如何获得一个核心转储

核心转储core dump是您的程序内存的一个副本,并且当您试图调试您的有问题的程序哪里出错的时候它非常有用。

当您的程序出现段错误,Linux 的内核有时会把一个核心转储写到磁盘。 当我最初试图获得一个核心转储时,我很长一段时间非常沮丧,因为 – Linux 没有生成核心转储!我的核心转储在哪里?

这就是我最终做的事情:

在启动我的程序之前运行ulimit -c unlimited

运行sudo sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t

ulimit:设置核心转储的最大尺寸

ulimit -c设置核心转储的最大尺寸。 它往往设置为 0,这意味着内核根本不会写核心转储。 它以千字节为单位。ulimit是按每个进程分别设置的 —— 你可以通过运行cat /proc/PID/limit看到一个进程的各种资源限制。

例如这些是我的系统上一个随便一个 Firefox 进程的资源限制:

内核在决定写入多大的核心转储文件时使用软限制soft limit(在这种情况下,max core file size = 0)。 您可以使用 shell 内置命令 ulimit(ulimit -c unlimited) 将软限制增加到硬限制hard limit。

kernel.core_pattern:核心转储保存在哪里

kernel.core_pattern 是一个内核参数,或者叫 “sysctl 设置”,它控制 Linux 内核将核心转储文件写到磁盘的哪里。

内核参数是一种设定您的系统全局设置的方法。您可以通过运行 sysctl -a 得到一个包含每个内核参数的列表,或使用 sysctl kernel.core_pattern 来专门查看 kernel.core_pattern 设置。

所以 sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t 将核心转储保存到目录 /tmp 下,并以 core 加上一系列能够标识(出故障的)进程的参数构成的后缀为文件名。

如果你想知道这些形如 %e、%p 的参数都表示什么,请参考 man core。

有一点很重要,kernel.core_pattern 是一个全局设置 —— 修改它的时候最好小心一点,因为有可能其它系统功能依赖于把它被设置为一个特定的方式(才能正常工作)。

kernel.core_pattern 和 Ubuntu

默认情况下在 ubuntu 系统中,kernel.core_pattern 被设置为下面的值:

$sysctl kernel.core_pattern

kernel.core_pattern = |/usr/share/apport/apport %p %s %c %d %P

这引起了我的迷惑(这 apport 是干什么的,它对我的核心转储做了什么?)。以下关于这个我了解到的:

Ubuntu 使用一种叫做 apport 的系统来报告 apt 包有关的崩溃信息。

设定kernel.core_pattern=|/usr/share/apport/apport %p %s %c %d %P意味着核心转储将被通过管道送给apport程序。

apport 的日志保存在文件/var/log/apport.log中。

apport 默认会忽略来自不属于 Ubuntu 软件包一部分的二进制文件的崩溃信息

我最终只是跳过了 apport,并把kernel.core_pattern重新设置为sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t,因为我在一台开发机上,我不在乎 apport 是否工作,我也不想尝试让 apport 把我的核心转储留在磁盘上。

现在你有了核心转储,接下来干什么?

好的,现在我们了解了ulimit和kernel.core_pattern,并且实际上在磁盘的/tmp目录中有了一个核心转储文件。太好了!接下来干什么?我们仍然不知道该程序为什么会出现段错误!

下一步将使用gdb打开核心转储文件并获取堆栈调用序列。

从 gdb 中得到堆栈调用序列

你可以像这样用gdb打开一个核心转储文件:

$gdb -cmy_core_file

接下来,我们想知道程序崩溃时的堆栈是什么样的。在 gdb 提示符下运行 bt 会给你一个调用序列backtrace。在我的例子里,gdb 没有为二进制文件加载符号信息,所以这些函数名就像 “??????”。幸运的是,(我们通过)加载符号修复了它。

下面是如何加载调试符号。

symbol-file /path/to/my/binary

sharedlibrary

这从二进制文件及其引用的任何共享库中加载符号。一旦我这样做了,当我执行 bt 时,gdb 给了我一个带有行号的漂亮的堆栈跟踪!

如果你想它能工作,二进制文件应该以带有调试符号信息的方式被编译。在试图找出程序崩溃的原因时,堆栈跟踪中的行号非常有帮助。:)

查看每个线程的堆栈

通过以下方式在 gdb 中获取每个线程的调用栈!

thread apply all bt full

gdb + 核心转储 = 惊喜

如果你有一个带调试符号的核心转储以及gdb,那太棒了!您可以上下查看调用堆栈(LCTT 译注:指跳进调用序列不同的函数中以便于查看局部变量),打印变量,并查看内存来得知发生了什么。这是最好的。

如果您仍然正在基于 gdb 向导来工作上,只打印出栈跟踪与bt也可以。 :)

ASAN

另一种搞清楚您的段错误的方法是使用 AddressSanitizer 选项编译程序(“ASAN”,即$CC -fsanitize=address)然后运行它。 本文中我不准备讨论那个,因为本文已经相当长了,并且在我的例子中打开 ASAN 后段错误消失了,可能是因为 ASAN 使用了一个不同的内存分配器(系统内存分配器,而不是 tcmalloc)。

在未来如果我能让 ASAN 工作,我可能会多写点有关它的东西。(LCTT 译注:这里指使用 ASAN 也能复现段错误)

从一个核心转储得到一个堆栈跟踪真的很亲切!

这个博客听起来很多,当我做这些的时候很困惑,但说真的,从一个段错误的程序中获得一个堆栈调用序列不需要那么多步骤:

试试用valgrind

如果那没用,或者你想要拿到一个核心转储来调查:

确保二进制文件编译时带有调试符号信息;

正确的设置ulimit和kernel.core_pattern;

运行程序;

一旦你用gdb调试核心转储了,加载符号并运行bt;

尝试找出发生了什么!

我可以使用gdb弄清楚有个 C++ 的虚表条目指向一些被破坏的内存,这有点帮助,并且使我感觉好像更懂了 C++ 一点。也许有一天我们会更多地讨论如何使用gdb来查找问题!

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11219

    浏览量

    208879
  • C++
    C++
    +关注

    关注

    22

    文章

    2104

    浏览量

    73480
  • 线程
    +关注

    关注

    0

    文章

    504

    浏览量

    19646

原文标题:在 Linux 上如何得到一个段错误的核心转储

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C程序中10与内存有关的常见错误

    与内存有关的错误,属于那种最令人惊恐的错误时间和空间,经常在距离错误一段距离之后才表现出
    发表于 06-20 10:41 670次阅读

    有没有办法UART禁用引导加载程序启动信息?

    有没有办法 UART 禁用引导加载程序启动信息?我应该改用 UART1 吗? 我将创建
    发表于 07-12 13:20

    安装Z-Stack_Linux_Gateway_1_0_1_installer.run出错,请问是系统不支持吗?

    本帖最后由 只耳朵怪 于 2018-5-24 11:28 编辑 我ubuntu12.04 32位的系统安装Z-Stack_Linux_Gateway_1_0_1_insta
    发表于 05-22 08:38

    Linux上升级到ISE 11.3出现总线错误

    在下载11.3的tar文件后,我linux 32位机器从ISE 11.2更新了ISE 11.3。虽然更新成功,但是当我运行bin / lin / ise时它在终端上给出了以下内容:总线错误
    发表于 11-19 14:41

    Vivado 2014.1SLES10安装失败

    问题的框架:#C [ld-linux-x86-64.so .2 + 0x882f] do_lookup_x + 0xcf ##无法写入核心核心
    发表于 12-07 11:18

    位文件后合成设计更改

    合成设计时,所有FDCE端口都正确连接。但是将代码到目标之后,我得到的输出是错误的。但是,如果我第二次
    发表于 04-16 07:46

    用于VHDL代码的saif文件

    一直在使用xilinx vivado来测试些代码。这些代码通常用于无矢量功率估计。但是,当我尝试从模拟设置saif文件时,我收到以下错误
    发表于 04-20 10:14

    Vivado 2016.2错误核心访问失败的原因?

    你好我有设计合成和实现没有任何计时错误。但是当我比特流后触发ILA时,出现了这个
    发表于 06-04 16:32

    有什么方法可以追溯地将加密标志应用于现有加密设备核心分区?

    我有生产固件的设备。我们启用了闪存加密,并启用了核心。不幸的是,我们没有文档中的任何地
    发表于 03-02 08:01

    Ubuntu 16.04系统中调试Apollo项目核心文件的方法

    核心对于程序员调试程序非常有益,因为有些程序错误是很难重现的,例如指针异常,而核心
    的头像 发表于 03-23 09:30 6965次阅读

    什么是错误

    。 这个内存区要么是不存在的,要么是受到系统保护的,还有可能是缺少文件或者文件损坏。 二、错误产生的原因下面是些典型的错误的原因: 非
    的头像 发表于 09-11 14:04 1.7w次阅读

    内核的设置

    当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在文件中,这种行为就叫做 Core Dump(中文有的翻译成“核心
    的头像 发表于 12-19 13:42 687次阅读

    教你Linux进度条小程序

    Linux 写下一个简易的进度条小程序。
    的头像 发表于 01-29 11:37 1260次阅读

    错误是什么意思?是何原因引起的?

    刚接触指针的时候,经常会遇到错误。所谓错误,就是访问了不能访问的内存。
    的头像 发表于 02-13 09:41 3190次阅读

    Linux内核Linux系统中到底处于什么样的地位

    Linux操作系统是当今世界最为广泛使用的开源操作系统之,内核则是操作系统的核心和灵魂所
    的头像 发表于 07-06 11:48 1166次阅读
    <b class='flag-5'>Linux</b>内核<b class='flag-5'>在</b><b class='flag-5'>Linux</b>系统中到底处于<b class='flag-5'>一</b><b class='flag-5'>个</b>什么样的地位