内核转储-coredump简介

背景

在liunx下c语言开发程序,最近遇到程序崩溃的现象,由于现场看不到任何崩溃的信息,很难定位问题。此时,内核转储(coredump)就派上用场了。

通常情况下coredmp包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息等,在设置妥当的情况下,该coredump文件在程序出错时自动生成。

coredump文件简介

Coredump文件,是Unix/Linux操作系统的一种机制,对于线上服务而言,出现Core的过程意味着服务暂时不能正常响应,需要恢复,并且随着吐Core进程的内存空间越大,此过程可能持续很长一段时间(例如当进程占用60G+以上内存时,完整Core文件需要15分钟才能完全写到磁盘上),这期间产生的流量损失,不可估量。

另一方面,OS在出Core的同时,虽然会终止掉当前进程,但是也会保留下第一手的现场数据,OS仿佛是一架被按下快门的相机,而照片就是产出的Core文件。里面含有当进程被终止时内存、CPU寄存器等信息,可以供后续开发人员进行调试。

简单的说,在一个程序崩溃时,系统会在指定目录下生成一个core文件。core文件主要是用来调试的。

打开或关闭core文件的生成

以下命令阻止系统生成core文件
ulimit -c 0
检查生成core文件的选项是否打开:
[john@localhost ~]$ ulimit -c
0
[john@localhost ~]$ 

-c 表示内核转储文件的大小限制,如果显示为零,则未打开。

打开core转储文件
ulimit -c 1073741824 #改为1G
ulimit -c unlimited #改为无限制
使设置永久生效的办法

上面所述的方法,只是在当前shell中生效,重启后丢失。永久生效的办法是在profile中添加:

#vi /etc/profile
ulimit -c 1073741824 #注意,若将产生的转储文件大小大于该数字时,将不会产生转储文

或者,

ulimit -c unlimited

这样重启机器后就生效了。也可以使用source命令使之马上生效。

source /etc/profile

指定内核转储的文件名和目录

缺省情况下,内核在coredump时所产生的core文件与该程序在相同的目录中,并且文件名固定为core。

这时,如果有多个程序产生core文件,或者同一个程序多次崩溃,就会重复覆盖同一个core文件。

可以通过修改kernel的参数,指定内核转储所生成的core文件的路径和文件名。在/etc/sysctl.conf中,设置sysctl变量kernel.core_pattern的值。

#vi /etc/sysctl.conf
kernel.core_pattern = /var/core/core_%e_%p #指定生成coredump文件的路径和文件名
kernel.core_uses_pid = 0

需要说明的是,如果/proc/sys/kernel/core_uses_pid的内容被配置成1,即使core_pattern中没有设置%p,最后生成的core dump文件名仍会加上进程ID。

其中,%e, %p分别表示:

%c 转储文件的大小上限
%e 所dump的文件名
%g 所dump的进程的实际组ID
%h 主机名
%p 所dump的进程PID
%s 导致本次coredump的信号
%t 转储时刻(由1970年1月1日起计的秒数)
%u 所dump进程的实际用户ID

可以使用以下命令,使修改结果马上生效。

sysctl –p /etc/sysctl.conf

强制某个进程产生coredump

有些bug是不会导致程序crash的,比如死锁,这时程序已经不正常了,可是却没有coredump产生。如果环境又不允许gdb调试,就可以尝试强制该进程产生coredump文件。

一般情况下,可以利用 watchdog监控进程,当发现这些进程很长时间没有更新其heartbeat时,可以给这些进程发送可以导致其产生coredump的信号。根据 linux的信号默认的处理行为,SIGQUIT,SIGABRT, SIGFPE和SIGSEGV都可以让该进程产生coredump文件。这样我们可以通过coredump来得知死锁是否发生。 当然, 如果进程添加了这些信号的处理函数,那么就不会产生coredump了。

还有一种情况,进程并没有死锁或者block在某个位置,但是我们需要在某个指定位置进行调试,获取某些变量或者其它信息。但是,有可能是客户环境或者生产环境,不允许我们进行长时间的检测。那么,我们就需要通 过coredump来获得进程在运行到该点时的快照。 这个时候,可以利用gdb来手工产生coredump。在attach上这个进程时,在指定位置打上断点,当断点触发时,使用gdb的命令gcore,可以立即产生一个coredump。

使用core dump进行调试

使用gdb调试

  1. 输入命令:gdb 可执行文件 coredump文件,启动gdb调试程序,
  2. 输入命令:gdb [-c] coredump文件,然后在gdb提示符下输入:file 可执行程序

这时就可以用backtrace/thread等命令查看当时的错误了,就像程序在本地执行到崩溃点一样,或者用where回车, 也可以显示程序在哪一行当掉的

一个例子

#include 
int main(void)
{
    int *a = NULL;
    *a = 0x1;

    return 0;
}

使用gcc -g a.c编译生成可执行文件a.out,运行就会显示:Segmentation fault(core dump),这表示在当前目录下, 已经生成了a.out对应的内核转储文件。
使用以下方式启动GDB调试:

#gdb -c ./*.core ./a.out
GNU gdb (GDB) 7.1-Ubuntu
...
Core was generated by './a.out'.
Program terminated with signal 11, Segmentation fault.
#0 0x00000000004004dc in main() at a.c:6
6 *a =0x1;

a.c的第6行收到了11号信号。用GDB的list命令可以查看附近的源代码。

相关参考

查看系统默认参数设置ulimit -a
[john@localhost ~]$ ulimit -a
core file size          (blocks, -c) 0 #此时默认不会生成core文件
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7168
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 4096
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
[john@localhost ~]$
ulimit的介绍
ulimit [-SHacdflmnpstuv [limit]]
              在支持它的系统上,对  shell 和它启动的进程,提供对可用资源的控制。 选项 -H 和 -S 指定为所给资源设定的硬性和柔性限额。
              硬性限额在设置后不能增加;柔性限额可以增加,直到与硬性限额相等。        如果没有给出         -H         或         -S
              选项,将同时设置硬性和柔性限额。 limit 的值可以是一个数字,单位是指定资源的单元值,或者是特殊值 hard, soft, 或 unlim‐
              ited                     之一,意思分别是当前硬性限额,当前柔性限额和没有限额。如果忽略了                     limit,
              将打印出当前对资源的柔性限额值,除非给出了                           -H                          选项。当指定多于一个
              资源时,限额名称和单位将在值之前打印出来。其他选项按照如下意义解释:
              -a     报告所有当前限额
              -c     core 文件的最大值
              -d     进程数据段的最大值
              -f     shell 创建的文件的最大值
              -l     内存中可以锁定的最大值
              -m     常驻内存的最大值
              -n     打开的文件描述符最大个数 (大多数系统不允许设置这个值)
              -p     管道大小,以 512 字节的块为单位 (这个值可能不能设置)
              -s     栈的最大值
              -t     cpu 时间总数的最大值,以秒计
              -u     用户可以运行的最大进程数
              -v     shell 可用的虚拟内存总量的最大值

              如果给出了 limit, 它将是指定资源的新限额 (选项 -a 只显示它们)。如果没有给出选项,则假设有 -f。  值的递增间隔是  1024
              字节,除了    -t    单位是    秒,    -p    单位是    512    字节的块个数,    -n    和    -u    是不可调节的值。返回
              0,除非给出了非法的选项或参数,或者在设置新的限额时发生了错误。

你可能感兴趣的:(Linux基础学习)