1. 概述
在本教程中,我们将了解如何管理和配置coredump。我们将研究kernel.core_pattern
然后我们将继续使用coredumpctl
。
2. 简介
coredump是程序崩溃后由 Linux 内核自动生成的文件。该文件包含应用程序崩溃时的内存、寄存器值和调用堆栈。
3. 使用信号生成coredump
在本节中,我们将学习如何终止程序并强制其生成coredump。为此,我们将使用kill
命令,它使用信号来终止应用程序。这些信号会产生coredump。
举个例子,让我们使用sleep
作为无限期运行的程序:
bash
$ sleep 500
[1] 5464
$ kill -s SIGTRAP $(pgrep sleep)
[1]+ Trace/breakpoint trap (core dumped) sleep 500
我们可以看到"core dumped"的信息表明coredump成功 。我们还注意到"Trace/breakpoint trap"表示SIGTRAP信号。
现在我们已经有了这个框架,让我们看看如何配置coredump。
4. 配置coredump
有两种方法可以配置coredump。一种是通过管道传递coredump,另一种是将其存储在文件中。
主要配置参数是kernel.core_pattern 。这适用于基于文件和基于管道的coredump。除了此配置参数之外,基于文件的转储还有大小限制。我们可以使用ulimit配置此大小。
我们将在以下部分中介绍这两种配置类型。
4.1. 将coredump重定向到管道
让我们看看如何配置我们的系统以通过管道生成coredump。首先,我们需要一个示例程序来从管道中提取coredump。之后,我们将配置内核以提供程序名称作为参数并将coredump提供给我们的程序。
让我们编写一个程序,如果崩溃进程处于睡眠状态,则该程序只会生成coredump:
python
#!/usr/bin/python2.7
# Filename: /tmp/core_dump_example.py
import sys
# Expect sys.argv to have %e configured in kernel.core_pattern
process_filename = sys.argv[1]
if process_filename == "sleep":
with open("/tmp/sleep_core_dump", "wb") as core_dump:
core_contents = bytearray(sys.stdin.read())
core_dump.write(core_contents)
在这里,我们注意到程序检查第一个参数,并且仅在包含sleep时输出coredump。让我们将其存储在/tmp/core_dump_example.py下并为其授予可执行权限。
现在,我们希望操作系统在生成coredump时调用我们的脚本。通过阅读core的手册页,我们可以通过 使用sysctl配置kernel.core_patttern 属性 来实现这一点:
ini
$ sudo sysctl -w kernel.core_pattern="|/tmp/core_dump_example.py %e"
模式开头的管道指示操作系统应通过 stdin 将coredump的内容传递到我们的脚本。
注意末尾的%e 。%e_是一个扩展为崩溃应用程序的进程名称的模板。还有更多可用的模板,在核心手册页中进行了描述。
让我们尝试创建一个coredump:
bash
$ sleep 500 &
[1] 8828
$ kill -s SIGTRAP $(pgrep sleep)
[1]+ Trace/breakpoint trap (core dumped) sleep 500
让我们检查一下使用 python 脚本创建的文件的签名:
yaml
$ file /tmp/sleep_core_dump
/tmp/sleep_core_dump: ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from 'sleep 500', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: '/usr/bin/sleep', platform: 'x86_64'
通过使用coredump上的文件,我们可以立即看到崩溃的程序是*/usr/bin/sleep。它还向我们展示了其他信息,例如启动此进程的 UID。
4.2. 将coredump重定向到文件
接下来,让我们配置我们的系统以生成coredump文件。为此, 我们将_kernel.core_pattern_设置为我们所需的文件名 。使用核心手册页中的模板,我们可以修饰coredump文件名。
首先,让我们设置coredump文件名:
ini
$ sudo sysctl -w kernel.core_pattern="/tmp/%e_core_dump.%p"
当_睡眠应用程序崩溃时,我们期望__/tmp_下出现一个具有_sleep_core_dump.pid_模式的文件。其中_%e_是程序名称,_%p_是程序的 PID。
请注意,我们可以给出文件名,而不是绝对路径。这将在崩溃进程的当前工作目录中创建一个coredump文件。
接下来,我们需要检查使用ulimit施加的任何限制。默认情况下,coredump文件有一个限制设置 。ulimit****设置的这些限制不会影响基于管道的coredump处理程序。
coredump大小的单位是_块_。让我们看看每个块有多少字节:
shell
$ stat -fc %s .
4096
使用_每块 4096 字节_,我们将限制设置为 5 MB,因为我们不希望示例生成大于 5 MB 的coredump。这可以计算为_nblocks =desired_limit / block_size_,其中desired_limit 和 block_size 均以字节为单位。5 MB 相当于 1280 个块 = (5 * 1024 * 1024) / 4096。
默认情况下,coredump的硬限制设置为_0_。要设置限制,我们必须将以下两行添加到_/etc/security/limits.conf_:
yaml
baeldung_user hard core 1280
baeldung_user soft core 1280
硬限制是系统范围的限制,软限制是基于用户的限制。软限制应小于相应的硬限制。此后我们需要重新启动。
让我们检查一下重启后coredump文件的大小限制:
shell
$ ulimit -c
1280
太好了,已经生效了。让我们尝试创建一个coredump:
bash
$ sleep 500 &
[1] 9183
$ kill -s SIGTRAP $(pgrep sleep)
[1]+ Trace/breakpoint trap (core dumped) sleep 500
$ ls /tmp/*_core_*
-rw------- 1 user user 372K Jun 26 23:31 /tmp/sleep_core_dump.1780
我们已经创建了具有所需模式的coredump文件。
5. 为正在运行的进程生成coredump
有时,为正在运行的进程生成coredump可能很有用。GDB可以捕获正在运行的进程的coredump,但它还附带一个名为\gcore的实用程序。gcore是一个命令行实用程序,可以捕获正在运行的进程的coredump。
让我们尝试使用gcore捕获coredump:
bash
$ sleep 500 &
[1] 3000
$ sudo gcore -o sleep 3000
0x00007f975eee630e in clock_nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
warning: target file /proc/3000/cmdline contained unexpected null characters
warning: Memory read failed for corefile section, 4096 bytes at 0xffffffffff600000.
Saved corefile sleep.3000
[Inferior 1 (process 3000) detached]
我们可以看到,启动了一个 PID 为 3000 的_sleep进程。随后,gcore被启动并附加到sleep进程中。结果,gcore随后生成了sleep.3000的coredump文件并自行分离。当gcore与进程分离后,进程将愉快地继续运行而不受影响。
注意:gcore_需要sudo才能附加到进程。我们可以使用sysctl将kernel.yama.ptrace_scope设置为0。这将允许gcore附加到没有sudo 的进程。但是,请注意,应谨慎使用,因为这存在安全风险。任何进程都可以使用ptrace系统调用并检查任何程序内部。
6.coredumpctl_简介
在本节中,我们将介绍一个名为coredumpctl的实用程序。与手动配置coredump相反,coredumpctl自动管理coredump。coredumpctl记录coredump本身并维护崩溃历史记录。
在以下部分中,我们假设系统上已安装coredumpctl 。
6.1. 配置coredumpctl
coredumpctl附带一个名为systemd-coredump 的服务。这是一项获取coredump,然后对其进行处理以从中提取元数据的服务。然后它将这些信息存储在/var/lib/systemd/coredump/ 下。
我们可以通过检查kernel.core_pattern来检查是否配置了该服务:
perl
$ sysctl -n kernel.core_pattern
|/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %h
我们已确认kernel.core_pattern_设置为使用_systemd-coredump。 这告诉内核将与coredump相关的任何信息传递给systemd-coredump。
要尝试coredumpctl,我们首先需要生成一个新的coredump:
bash
$ sleep 500 &
[1] 2826
$ kill -s SIGTRAP $(pgrep sleep)
[1]+ Trace/breakpoint trap (core dumped) sleep 500
$ coredumpctl
TIME PID UID GID SIG COREFILE EXE
Sun 2020-06-28 18:52:59 BST 2826 1000 1000 5 present /usr/bin/sleep
这真的很酷。通过尝试coredumpctl,我们可以看到我们有崩溃历史!
要提取特定崩溃的coredump文件,我们可以使用 PID、可执行文件的名称或崩溃时间。作为示例,让我们尝试使用 PID保存sleep的coredump:
yaml
$ coredumpctl dump 2826 --output=core.dump
PID: 2826 (sleep)
UID: 1000 (user)
...
Stack trace of thread 2826:
#0 0x00007f7ec62f730e __GI___clock_nanosleep (libc.so.6 + 0xe030e)
#1 0x00007f7ec62fceb7 __GI___nanosleep (libc.so.6 + 0xe5eb7)
...
除了coredump文件之外,我们还可以看到简短的摘要和堆栈跟踪。这来自systemd-coredump对coredump的预处理。
6.3. 使用coredumpctl运行调试会话
让我们看看如何使用_debug_命令启动调试会话:
vbnet
$ coredumpctl debug 2826
...
Reading symbols from /usr/bin/sleep...
(No debugging symbols found in /usr/bin/sleep)
[New LWP 2959]
Core was generated by `sleep 500'.
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
...
(gdb)
请注意gdb如何在加载coredump文件的情况下自动打开。
要检查崩溃情况,我们输入"disassemble"。结果,我们可以看到以下反汇编结果:
ini
Dump of assembler code for function __GI___clock_nanosleep:
<__GI___clock_nanosleep+80>
0x00007fb71b22c307 <+39>: mov eax,0xe6
0x00007fb71b22c30c <+44>: syscall
=> 0x00007fb71b22c30e <+46>: mov edx,eax
我们可以看到,有" mov Wax,0xe6"后面跟着一条系统调用指令。查看系统调用列表,似乎 230 (0xe6) 是clock_nanosleep 系统调用。这是捕获coredump的点。
七、结论
在本教程中,我们探讨了如何配置coredump。后来,我们探索了实用程序oredumpctl,它使管理coredump变得更加容易。