嵌入式与Linux那些事

电子技术应用专栏作家——嵌入式与Linux那些事。关注嵌入式与Linux的校招社招,本人整理了《嵌入式软件工程师笔试面试指南》PDF,平时发布嵌入式与Linux相关的实用技术文章

内核转储的设置

0
阅读(1472)

  简介

  当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做 Core Dump(中文有的翻译成“核心转储”)。

  我们可以认为 core dump 是“内存快照”,但实际上,除了内存信息之外,还有些关键的程序运行状态也会同时 dump 下来,例如寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。

  core dump 对于编程人员诊断和调试程序是非常有帮助的,因为对于有些程序错误是很难重现的,例如指针异常,而 core dump 文件可以再现程序出错时的情景。

  核心转储如何产生

  上面说当程序运行过程中异常终止或崩溃时会发生 core dump,但还没说到什么具体的情景程序会发生异常终止或崩溃。

  例如我们使用 kill -9 命令杀死一个进程会发生 core dump 吗?实验证明是不能的,那么什么情况会产生呢?

  Linux 中信号是一种异步事件处理的机制,每种信号都有其对应的默认操作,你可以在 signal(7) 查看 Linux 系统提供的信号以及默认处理。

  默认操作主要包括:终止进程(Term)、忽略该信号(Ing)、终止进程并发生核心转储(Core)、暂停进程(Stop)、继续运行被暂停的进程(Cont)。

  如果我们信号均是采用默认操作,那么,以下列出的几种信号,它们在发生时会产生 core dump:

 image.png

  这就是为什么我们使用 Ctrl+z 来挂起一个进程或者 Ctrl+C 结束一个进程均不会产生 core dump。

  因为前者会向进程发出 SIGTSTP 信号,该信号的默认操作为暂停进程(Stop Process);后者会向进程发出SIGINT 信号,该信号默认操作为终止进程(Terminate Process)。

  同样,上面提到的 kill -9 命令会发出 SIGKILL 命令,该命令默认为终止进程。而如果我们使用 Ctrl+\ 来终止一个进程,会向进程发出 SIGQUIT 信号,默认是会产生 core dump 的。

  还有其它情景会产生 core dump, 如:程序调用 abort() 函数、访存错误、非法指令等等。

  不会生成core dump文件的情况

  进程没有写入核心文件的权限。(默认情况下,核心文件称为 core 或 core.pid,其中 pid 是转储核心的进程的 ID,并在当前工作目录中创建。有关命名的详细信息,请参见下文。)如果出现以下情况,则写入核心文件失败:要创建的目录不可写,或者如果存在同名文件且不可写或不是常规文件(例如,它是目录或符号链接)。

  一个(可写的、常规的)文件与用于核心转储的同名文件已经存在,但有多个硬链接到该文件。

  将创建核心转储文件的文件系统已满;或已用完 inode;或以只读方式安装;或者用户已达到文件系统的配额。

  要创建核心转储文件的目录不存在。

  进程的 RLIMIT_CORE(核心文件大小)或 RLIMIT_FSIZE(文件大小)资源限制设置为零;请参阅 getrlimit(2) 和 shell 的 ulimit 命令的文档(csh(1) 中的限制)。

  进程正在执行的二进制文件没有启用读取权限。(这是一种安全措施,可确保内容不可读的可执行文件不会产生可能可读的核心转储,其中包含可执行文件的映像。)

  进程正在执行一个set-user-ID(set-group-ID)程序,该程序被除进程的真实用户(组)ID之外的用户(组)拥有,或者进程正在执行具有文件能力(capabilities)的程序(请参阅 capabilities(7))。(但是,请参阅 prctl(2) PR_SET_DUMPABLE 操作的说明,以及 proc(5) 中 /proc/sys/fs/suid_dumpable 文件的说明)

  /proc/sys/kernel/core_pattern 为空且 /proc/sys/kernel/core_uses_pid 包含值 0。请注意,如果 /proc/sys/kernel/core_pattern 为空且 /proc/ sys/kernel/core_uses_pid 包含值 1,核心转储文件将具有 .pid 形式的名称,除非使用 ls(1) -a 选项,否则此类文件将被隐藏。

  (自 Linux 3.7 起)内核配置时没有配置 CONFIG_COREDUMP 选项。

  此外,如果使用了 madvise(2) MADV_DONTDUMP 标志,则核心转储可能会排除进程的部分地址空间。

  启用内核转储

  使用ulimit命令可以查看当前的内核转储功能是否生效。-c表示内核转储文件的大小限制,0表示内核转储无效。

 image.png

  将生成的可执行程序拷贝到开发板上。

  image.png

  可以看到,在GDB启动后,已经打印出test.c的第6行收到了SIGSEGV信号,产生了段错误。使用list命令可以查看附近的源代码。

  在专用目录生成内核转储

  core文件默认会在当前目录生成,大多数时候,我们希望固定core文件的生成位置。

  内核转储保存位置可以通过sysctl变量kernel.core_pattern设置。例如,在/etc/sysctl.conf中做如下设置。

 image.png

  压缩转储文件

  kernel.core_pattern也支持管道,可以在kernel.core_pattern 后加入管道符自动压缩内核转储文件。

  image.png

  启用整个系统的内核转储功能

  在终端通过命令行只是临时修改,重启后无效 ,要想永久修改有三种方式:

  在/etc/rc.local 中增加一行 ulimit -c unlimited

  在/etc/security/limits.conf最后增加如下两行记录:

image.png

  利用内核转储掩码排除共享内存

  大型应用程序,通常会跑多个进程。如果所有进程的共享内存全部转存储的话,会对磁盘造成压力,转储过程也会加重系统的负担,甚至会由于转储时间过长导致服务停止时间过长。

  由于共享内存的进程中,共享内存的内容是相同的,所以可以只在某个进程中转储共享内存,无需全部转储。

 image.png

  如果要跳过所有共享内存区域,应将掩码值设置为1.

原文链接:https://mp.weixin.qq.com/s/Z-5Z5D_21JnJFG4uvrQ5Gg

微信图片_20220708145705.jpg

电子技术应用专栏作家 嵌入式与Linux那些事