20.1 使用 SIGTERM 和核心转储

核心转储介绍

GDB 可以加载核心转储以复现程序终止时的内存,也可以为给定的进程创建核心转储。

核心转储是可以通过发送信号实现的。调用 abort() 时,会向进程自身发送 SIGABRT 信号,该信号的默认行为是终止进程并为其生成核心转储。在终端中,SIGQUIT(ctrl + /)和 SIGINT(ctrl + c)相比,也多出来了生成核心转储的行为。可以从以下尝试中体会出两个信号的差异:

(py310) xxx /data/apue $ ulimit -c unlimited
(py310) xxx /data/apue $ ./build/main 
^C
# 返回码 130
(py310) xxx /data/apue $ ./build/main 
^\Quit (core dumped)
# 返回码 131

可以检查核心转储文件的路径。既可以通过 /proc 文件系统查看路径,也可以通过 sysctl 命令查看路径。在 WSL 中打印出来的结果见下方:

(py310) xxx $ cat /proc/sys/kernel/core_pattern
/mnt/wslg/dumps/core.%e
(py310) xxx $ sysctl kernel.core_pattern
kernel.core_pattern = /mnt/wslg/dumps/core.%e

尝试使用核心转储

gcc -g 编译以下 C 语言程序:

#include <stdlib.h>

int main() {
    int a = 5;
    abort();
}

然后在 WSL 中运行:

(py310) xxx /data/tlpi $ /data/tlpi/build/main
Aborted
(py310) [134] xxx /data/tlpi $ ls /mnt/wslg/dumps/
(py310) xxx /data/tlpi $ ulimit -c unlimited
(py310) xxx /data/tlpi $ /data/tlpi/build/main
Aborted (core dumped)
(py310) [134] xxx /data/tlpi $ ls /mnt/wslg/dumps/
core.main

ulimit -c unlimited 设置了核心转储文件的大小为无限制,如果不加这个就没有核心转储生成(因为这个限制的默认大小是 0)。这条命令只在当前的终端中生效。

接下来用 gdb 加载核心转储,能用 bt 看到栈信息:

(py310) xxx /data/tlpi $ gdb /data/tlpi/build/main /mnt/wslg/dumps/core.main 
GNU gdb (Debian 13.1-3) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /data/tlpi/build/main...
[New LWP 286206]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `/data/tlpi/build/main'.
Program terminated with signal SIGABRT, Aborted.
#0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
44      ./nptl/pthread_kill.c: No such file or directory.
(gdb) bt
#0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
#1  0x00007f4eab33fe8f in __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
#2  0x00007f4eab2f0fb2 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#3  0x00007f4eab2db472 in __GI_abort () at ./stdlib/abort.c:79
#4  0x000055a332a0214d in main () at /data/tlpi/main.c:12
(gdb) 

也能使用 coredumpctl list 来发现所有的核心转储,但这需要首先安装 systemd-coredump 这个 Debian 包。安装这个包之后,核心转储的存储位置就发生变化了,和 WSL 的默认位置不一样:

(py310) xxx /data/tlpi $ cat /proc/sys/kernel/core_pattern
|/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %h

但是安装这个包之后我找不到生成的核心转储,用 coredumpctl list 也显示没有转储。回答 https://unix.stackexchange.com/a/65111/ 提到 systemd 会将转储记录在 journal 中,所以我先用 journalctl -f 来追踪日志,然后再在另外一个 shell 中运行以上程序,发现了新日志:

Jun 05 23:36:03 xxx systemd-coredump[3854]: Failed to connect to coredump service: No such file or directory

始终解决不了这个问题……可能是 WSL 的实现问题,还是卸载 systemd-coredump 算了。卸载完成之后,cat /proc/sys/kernel/core_pattern 的结果又变成了 core……!(重启 WSL 之后会恢复成原来的那个。)