gdb调试coredump(使用篇)

2023-05-16

什么是coredump

  Coredump叫做核心转储,它是进程运行时在突然崩溃的那一刻的一个内存快照。操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下,会把进程此刻内存、寄存器状态、运行堆栈等信息转储保存在一个文件里。

   该文件也是二进制文件,可以使用gdb、elfdump、objdump或者windows下的windebug、solaris下的mdb进行打开分析里面的具体内容。

 

   注:core是在半导体作为内存材料前的线圈,当时用线圈当做内存材料,线圈叫做core。用线圈做的内存叫做core memory。

 

 

ulimit 

虽然我们知道进程在coredump的时候会产生core文件,但是有时候却发现进程虽然core了,但是我们却找不到core文件。

 

LinuxSolaris下是需要进行设置的。

 

ulimit  -c 可以设置core文件的大小,如果这个值为0.则不会产生core文件,这个值太小,则core文件也不会产生,因为core文件一般都比较大。

 

使用ulimit  -c unlimited来设置无限大,则任意情况下都会产生core文件。

 

WindowsminiDumpFullDump的设置

 

Windows下需要在下面的注册表,

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\WindowsError Reporting]

下面加一项LocalDumps,并做如下项配置:

Value

描述

Type

默认值

DumpFolder

文件保存路径

REG_EXPAND_SZ

%LOCALAPPDATA%CrashDumps

DumpCount

dump文件的最大数目

REG_DWORD

10

DumpType

指定生成的dump类型:
0:Custom dump
1:Mini dump
2:Full dump

REG_DWORD

1

CustomDumpFlags

仅在DumpType0时使用
MINIDUMP_TYPE的组合

REG_DWORD


MiniDumpWithDataSegs|  

MiniDumpWithUnloadedModules|  

MiniDumpWithProcessThreadData  
















上面的配置信息,可以通过打开注册表来手动添加。

首先,打开cmd或者运行程序(微软图标+R


 



如上截图,可以通过图形界面手动添加这些注册表信息,然后windows系统在有进程crash的时候就会保存fulldump的文件。

 

或者通过reg文件的方式来进行注册


Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps]
"DumpFolder"="F:\\study_Test\\Dump"
"DumpCount"=dword:a
"DumpType"=dword:1  

 

如上,通过新建一个fulldump.reg的文件,保存上面内容,双击后,这些信息就会注册到注册表中。

 

gdb 调试coredump的简单示例

 


#include "stdio.h"
#include "stdlib.h"

void dumpCrash()
{
    char *pStr = "test_content";
    free(pStr);
}
int main()
{
    dumpCrash();
    return 0;
}  

 

如上代码,pStr指针指向的是字符串常量,字符串常量是保存在常量区的,free释放常量区的内存肯定会导致coredump

 

首先把上面的代码拷贝到linux机器上,保存为dumpTest.c文件,gcc编译


gcc -o dumpTestdumpTest.c

运行dumpTest产生core文件


 

生成core文件

如上,运行dumpTest的时候进程coredump了,但是没有产生core文件


如截图所示,系统设置的core文件大小为0,此时即使产生了coredump,也不会产生core文件。

 


如截图所示,ulimit -c unlimited设置core文件大小后,产生了名字为corecore文件。

此时生成的core文件名称都是统一的”core”命名。

 

自定义core文件的文件名

 上面的设置只是使能了core dump功能,缺省情况下,内核在coredump时所产生的core文件放在与该程序相同的目录中,并且文件名固定为core。很显然,如果有多个程序产生core文件,或者同一个程序多次崩溃,就会重复覆盖同一个core文件。

 

我们通过修改kernel的参数,可以指定内核所生成的coredump文件的文件名。例如,Easwy使用下面的命令使kernel生成名字为core_filename_time_pid格式的core dump文件:

echo /usr/core_log/core_%e_%t_%p > /proc/sys/kernel/core_pattern

echo后面内容最好不要带上引号,有的系统会把引号也带入,如下:



这样,系统是不识别该内容的,也就会导致程序coredump而不会生成core文件。



如上截图,通过设置core文件的名称以及路径,程序coredump的时候就会在指定路径按照指定的规则命名生成core文件。

可以在core_pattern模板中使用变量见下面的列表:

 

%%单个%字符

%pdump进程的进程ID

%udump进程的实际用户ID

%gdump进程的实际组ID

%s导致本次core dump的信号

%t core dump的时间 (197011日计起的秒数)

%h主机名

%e程序文件名


设置永久保存

 上面截图可以看到,我后面再次执行生成coredump文件的时候实际上又再次设置了ulimit-c unlimited的,因为中间机器重启了。上面的设置都只是临时的,重启之后就需要重新设置,如何设置永久生效呢?

 

打开/etc/security/limits.conf  文件,在该文件的最后加上两行

#下面是我的配置

@root soft core unlimited

@root hard core unlimited



配置好后,放回原目录,重启reboot。

 

命名规则的修改在/proc/sys/kernel/core_pattern中也只是临时的,这个也是动态加载和生成的。永久修改在/etc/sysctl.conf文件中,在该文件的最后加上两行:

kernel.core_pattern = /var/core_log/core_%e_%t_%p

kernel.core_uses_pid = 0



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



如上截图,当前生成的core文件命名按照上面定义的规则加上了程序名称、coredump时间,进程ID等信息,并放到了指定目录/var/core_log

 

gdb调试coredump初步尝试

 gdb打开core文件的格式为

 gdb程序名(包含路径) core*core文件名和路径),如下截图



 

如上,gdb打开core文件时,有显示没有调试信息,因为之前编译的时候没有带上-g选项,没有调试信息是正常的,实际上它也不影响调试core文件。因为调试core文件时,符号信息都来自符号表,用不到调试信息。如下为加上调试信息的效果。



 

 

查看coredump时的堆栈

查看堆栈使用bt或者where命令



如上,在带上调试信息的情况下,我们实际上是可以看到core的地方和代码行的匹配位置。

但往往正常发布环境是不会带上调试信息的,因为调试信息通常会占用比较大的存储空间,一般都会在编译的时候把-g选项去掉。

 

没有调试信息的情况下找core的代码行



如上截图,没有调试信息的情况下,打开coredump堆栈,并不会直接显示core的代码行。

此时,frame addr(帧数)或者简写如上,f 1 跳转到core堆栈的第1帧。因为第0帧是libc的代码,已经不是我们自己代码了。

disassemble打开该帧函数的反汇编代码。

 


#1  0x080483ec in dumpCrash ()
(gdb) disassemble
Dump of assembler code for function dumpCrash:
   0x080483d4 <+0>:     push   %ebp
   0x080483d5 <+1>:     mov    %esp,%ebp
   0x080483d7 <+3>:     sub    $0x28,%esp
   0x080483da <+6>:     movl   $0x80484d0,-0xc(%ebp)
   0x080483e1 <+13>:    mov    -0xc(%ebp),%eax
   0x080483e4 <+16>:    mov    %eax,(%esp)
   0x080483e7 <+19>:    call   0x80482f0 <free@plt>
=> 0x080483ec <+24>:    leave
   0x080483ed <+25>:    ret
End of assembler dump.  

 

如上箭头位置表示coredump时该函数调用所在的位置



如上截图,shell echo free@plt |C++filt 去掉函数的名词修饰

不过上面的free使用去掉名词修饰效果和之前还是一样的。但是我们可以推测到这里是在调用free函数。

如此,我们就能知道我们coredump的位置,从而进一步能推断出coredump的原因。

当然,现实环境中,coredump的场景肯定远比这个复杂,都是逻辑都是一样的,我们需要先找到coredump的位置,再结合代码以及core文件推测coredump的原因。

 

 

寻找this指针和虚指针


#include "stdio.h"
#include <iostream>
#include "stdlib.h"
using namespace std;
class base
{
public:
    base();
    virtual void test();
private:
    char *basePStr;
};
class dumpTest : public base
{
public:
    void test();
private:
    char *childPStr;
};
base::base()
{
    basePStr = "test_info";
}
void base::test()
{
    cout<<basePStr<<endl;
}
void dumpTest::test()
{
    cout<<"dumpTest"<<endl;
    delete childPStr;
}
void dumpCrash()
{
    char *pStr = "test_content";
    free(pStr);
}
int main()
{
    dumpTest dump;
    dump.test();
    return 0;
}  

 

如上代码,实现了一个简单的基类和一个子类。在main函数里定义一个子类的实例化对象,并调用它的虚函数方法testtest里由于直接delete没有初始化的指针childPStr,肯定会造成coredump。本次我们就希望通过dump文件,找到子类dumpTestthis指针和虚函数指针。

gcc一样,使用g++ -o DumpCppTest dumpTest.cpp编译cpp文件生成可执行程序。

./DumpCppTest  执行该程序,程序因为直接delete未初始化的指针,肯定会coredump。生成core文件如下



如上,使用gdb打开core文件,同时bt打开core的堆栈信息。

从堆栈可以看到,最后两帧为我们程序自己的函数,其他的都是libc的代码。

f 6 调到第6帧上,之后info frame查看堆栈寄存器信息。



如上截图所示,前一帧的栈寄存器地址是0xbf8cdb50,它的前一帧也就是main函数的位置,main函数里调用dump.test()的位置,那我们在这个地址上应该可以找到dump的this指针和它的虚指针,以及虚指针指向的虚函数表



如图所示,0xbf8cdb50地址指向的是前一帧保存dump信息的位置,0xbf8cdc14bf8cdb64就表示dump的this指针,而this指针指向的第一个8字节0x0804893008048958就表示虚指针,如上,通过x 0x0804893008048958看到_ZTV8dumpTest+8的内容。

 shell echo_ZTV8dumpTest|c++filt 可以看到“vtable for dumpTest”的内容。这个就表示dumpTest的虚函数表。

从上面也可以看到,这个地址指向的是虚函数表+8的偏移位置,而这个位置0x000000000804876a 通过x 0x000000000804876a 可以看到,存储的内容就是



dumpTest::test() 函数。

这里也印证了,在继承关系里,基类的虚函数是在子类虚函数的前面。


如上,x 0x000000000804876a-4 就可以看到dumpTest的基类base的虚函数test的位置。

 

如上,在实际问题中,C++程序的很多coredump问题都是和指针相关的,很多segmentfault都是由于指针被误删或者访问空指针、或者越界等造成的,而这些都一般意味着正在访问的对象的this指针可能已经被破坏了,此时,我们通过去寻找函数对应的对象的this指针、虚指针能验证我们的推测。之后再结合代码寻找问题所在。

 

gdb 查看core进程的所有线程堆栈


#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5 //线程数
int count = 0;

void* say_hello( void *args )
{
    while(1)
    {
        sleep(1);
        cout<<"hello..."<<endl;
        if(NUM_THREADS ==  count)
        {
            char *pStr = "";
            delete pStr;
        }
    }
} //函数返回的是函数指针,便于后面作为参数
int main()
{
    pthread_t tids[NUM_THREADS]; //线程id
    for( int i = 0; i < NUM_THREADS; ++i )
    {
        count = i+1;
        int ret = pthread_create( &tids[i], NULL, say_hello,NULL); //参数:创建的线程id,线程参数,线程运行函数的起始地址,运行函数的参数
        if( ret != 0 ) //创建线程成功返回0
        {
            cout << "pthread_create error:error_code=" << ret << endl;
        }
    }
    pthread_exit( NULL ); //等待各个线程退出后,进程才结束,否则进程强制结束,线程处于未终止的状态
}  

如上代码,简单示意C++多线程。

在linux下使用g++直接编译该cpp文件会报错,报错信息如下:



会报 undefined reference to `pthread_create' 的错误信息,解决办法如下:

使用 g++ -o MultiThreadDump MultiThread.cpp -lpthread 编译,编译参数上带上-lpthread即可。



运行./MultiThreadDump



由于上面代码里在count等于5的时候,会delete一个未初始化的指针,肯定会coredump。

 


如上,gdb打开coredump文件,能看到5个线程LWP的信息。

如何,查看每个线程的堆栈信息呢?

首先,info threads查看所有线程正在运行的指令信息



thread apply all bt打开所有线程的堆栈信息



查看指定线程堆栈信息:threadapply threadID bt,如:

thread apply 5 bt



进入指定线程栈空间

thread threadID如下:


 

如上截图所示,可以跳转到指定的线程中,并查看所在线程的正在运行的堆栈信息和寄存器信息。

 

 

总结:

如上,简单介绍了3种不同情况下的gdb调试coredump文件的情况,基本涵盖了调试coredump问题时的大部分会用到的gdb命令。

gdb调试coredump,大部分时候还是只能从core文件找出core的直观原因,但是更根本的原因一般还是需要结合代码一起分析当时进程的运行上下文场景,才能推测出程序代码问题所在。

因此gdb调试coredump也是需要经验的积累,只有有一定的功底和对于基础知识的掌握才能在一堆二进制符号的core文件中找出问题的所在。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

gdb调试coredump(使用篇) 的相关文章

  • 在GDB中,如何在程序停止时自动执行命令? (如显示)

    我希望每次程序停止时自动执行一些命令 就像 display 对 x 所做的那样 我怎么做 这是我发现的简单方法 define hook stop commands to be executed when execution stops en
  • 如何在Windows上构建GDB

    如何在 Windows 上从源代码构建 GDB GNU 调试器 我需要构建它才能使用 Python 支持进行构建 我无法使用随 Cygwin 一起发布的版本 因为它在 Windows 上解释反斜杠时出现问题 构建需要哪些工具链 GnuWin
  • GDB 在 macOS Catalina 上冻结

    我在 mac 上运行了 helloworld 来尝试 GDB 但在输入 run 后输出 New Thread 0x1903 of process 69034 然后就什么也没有了 我等了一个小时了 还是没有任何动静 完成认证并禁用startu
  • -[NSRangeException raise] 上的符号异常断点

    在 Xcode 中添加符号断点为您提供了一个示例模板 NSException raise 我想做同样的事情但是具体来说 on NSRangeException raise 原因是我想断点only关于特定数组边界异常 例如 Terminati
  • 如何在 Xcode 4 中从断点操作打印字符串值?

    我有一个断点操作 并且正在使用下拉列表中的 日志 选项 我想打印出字符串 摘要 值 我正在这样做 the person name is p name 但这会打印内存地址 我可以切换到调试器命令选项并执行以下操作 po f name 但后来我
  • GDB - 如何打破“有些东西被写入cout”?

    我想设置一个断点 每次写入内容时都会触发stdout通过cout流 但我无法找到该断点的可能位置 我怎样才能在 gdb 中做到这一点 这是一种依赖于平台的方式 如果您在 x86 64 上并使用 gcc 进行构建 则写入 std cout 会
  • 如何判断共享库加载到进程地址空间中的位置?

    我正在尝试调试一个共享库 其中有使用 gdb 的源代码和调试符号 我没有实际使用此共享库的进程的调试符号或代码 我自己编译它 所以我可以拥有一切 但生成的二进制文件被剥离 以模拟我没有代码的情况 该进程打印我正在尝试调试的目标函数 foo
  • 如何在 gdb 中附加进程

    我有一个简单的 C 程序 它分叉一个进程 然后运行一个可执行文件 我想将子进程附加到 gdb 我在控制台中运行主程序并打开另一个控制台来查找子进程的 pid 然后使用以下命令启动 gdb gdb attach 12271 where 122
  • fork 后调试子进程(配置了 follow-fork-mode 子进程)

    我正在开发一个应用程序 父级分叉子级来处理某些任务 我遇到一个问题 我已将 gdb 配置为 follow fork mode 子级 但在 fork 后 到达断点后 它发送 SIGTRAP 但子级以某种方式终止并向父级发送 SIGCHLD 我
  • 如何让 gdb 保存命令历史记录?

    我该如何设置gdb这样它就可以保存命令历史记录 当开始新的gdb会话 我想使用向上箭头键来访问之前会话的命令 简短回答 mkdir p config gdb echo set history save on gt gt config gdb
  • 在运行时从应用程序读取调试信息

    除了调试之外 我还有一些关于调试符号以及可以用它们做什么的问题 我最感兴趣的是有关 GCC 的答案 但我也很高兴知道它在其他编译器 包括 MSVC 下的样子 首先 调试符号的常见格式 类型有哪些 它们与编译器和平台有何关系 GCC 和 Mi
  • 使用 gdb 调试时彻底退出 valgrind

    我正在使用 valgrind 和 gdb 调试程序 然而 我以一种野蛮的方式终止了这些调试会话 这真的是它应该做的吗 设置调试会话 按照来自的指示valgrind 官方网站 http valgrind org docs manual man
  • 断点改变程序流程

    我正在尝试分析和逆向我拥有的 Objective C 程序 我通过手动更改一些操作码对可执行文件进行了一些修改 然而 当我测试修改后的软件时 我得到 死亡人数 9 没关系 我想我触碰了不该触碰的东西 我当时就推出了gdb myprogram
  • 有什么方法可以判断我的 iPhone 应用程序在运行时是否在调试器下运行?

    如果我的错误处理代码在调试器下运行 我希望它的行为有所不同 具体来说 如果我在手机上运行 未连接到调试器并且断言失败 我想将错误发送到我的服务器 当我在gdb下时 我想闯入调试器 虽然我可以想象苹果将如何编写代码 但我找不到任何关于测试调试
  • 专门逐行调试

    我有一个用 Pascal 编写的脚本 我会以这种方式调试它 在每一行停止 转储内存中所有变量的值 然后转到下一行 是否可以使用 gdb 或其他 Linux 开源工具来完成此操作 使用选项编译文件 g fpc gpc g file pas R
  • 测试是否定义了 gdb 便利变量

    有没有办法测试 gdb 中是否设置了便利变量 例如 gdb if exitcode 0 gt quit gt end Invalid type combination in equality test gdb p exitcode 1 vo
  • Eclipse 调试模式下的 GDB 找不到 stdlib/rand.c

    我试图让 gdb 在 ubuntu 上与 eclipse cdt 一起运行 以开始调试一些简单的程序 所以我做了我认为必要的步骤来让它运行 1 创建可执行项目 2 Compile 3 Run 4 创建文件 gdbinit 并将其放在主项目文
  • gdb 错误 - 文件不是可执行格式:无法识别文件格式

    我正在尝试使用 gdb 调试某个名为 xdf 的程序 但是当我运行 gdb xdf 时 出现以下错误 home nealtitusthomas X ray astronomy heasoft 6 24 x86 64 pc linux gnu
  • 在 C 程序中追踪数组越界访问/写入的推荐方法

    考虑用 C 语言编写一些不太明显的算法的实现 例如 让它成为递归快速排序 我在 K N King 的 C 编程 现代方法 第二版 书中找到了它 可以从here http knking com books c2 programs qsort
  • 如何模拟ARM处理器运行环境并加载Linux内核模块?

    我尝试加载我的vmlinux into gdb并使用 ARM 内核模拟器 但我不明白为什么我会得到Undefined target command sim 这是外壳输出 arm eabi gdb vmlinux GNU gdb GDB 7

随机推荐

  • 单片机学习(四)——ESP8266(最全教程和说明)

    文章目录 前言一 ESP8266可以干什么 xff1f 二 ESP8266作为传输数据的开发 1 电路连接图2 ESP8266直接烧录3 通过Arduino对ESP8266进行烧录三 ESP8266作为开发板的开发四 可能会存在的问题总结
  • VScode怎么修改主题色

    VScode底部点击 设置 按钮 xff0c 点击colorTheme 出来的弹框选项中 xff0c 点击选择合适的主题色即可
  • 初学者PID进阶——在线调整参数

    原文地址 xff1a http brettbeauregard com blog 2011 04 improving the beginner e2 80 99s pid tuning changes 问题The Problem 在系统运行
  • 路由器WiFi天线拆机详解-路由器天线原理

    xff08 本文已经同步到公众号 原创文章 xff0c 转载请与作者联系 xff09 1 前言 xff1a 随着无线通信的发展 xff0c WiFi对我们日常生活工作越来越重要 作为WiFi的载体 路由器 xff0c 也是经常出现在生活的各
  • 以机器人为中心的视觉惯性里程计--RVIO

    有没有大佬能用这个代码发布一个 39 nav msgs Odometry 39 message的 34 odom 34 话题 xff0c 下面是代码的github地址 xff0c 和作者的意见 代码地址 https github com r
  • ubuntu的gazebo无法加载模型的解决办法

    最近在CMU exploration的平台上做仿真实验时 xff0c 出现了无法加载模型的错误 ERROR 1657256347 244258655 3 370000000 Updating ModelState model camera
  • 开发板(ubuntu系统)通过串口接收数据,并将数据通过网口转发出去

    测试软件windows下串口助手和cmd下的telnet xff0c ubuntu下的minicom 设置好相应的串口号和端口号 此程序可以正常接收数据 include lt termios h gt include lt unistd h
  • 嵌入式操作系统的内存,你了解多少?

    linux 内存是后台开发人员 xff0c 需要深入了解的计算机资源 合理的使用内存 xff0c 有助于提升机器的性能和稳定性 本文主要介绍 linux 内存组织结构和页面布局 xff0c 内存碎片产生原因和优化算法 xff0c linux
  • RK查看gpu占用率方法

    查看gpu占用率 cat sys devices platform ff9a0000 gpu devfreq ff9a0000 gpu load 0 64 200000000Hz 64 前面的数值表示gpu占用率 xff0c 如果一直为0
  • 对视频50p,50i 还有25p,25i区别的粗劣解释

    对视频50p 50i 还有25p 25i区别的粗劣解释 首先跟你解释p和i的概念 xff0c p是逐行扫描模式 xff0c i是隔行扫描模式 帧速率有以下几种 xff1a 60帧 秒 xff08 实际为59 94帧 秒 xff0c 为方便起
  • 对 makefile 中 .c.o 的理解

    LIBS 61 gao o all LIBS 64 echo 34 final 34 c o gcc o 64 lt echo 34 in c o rule 34 执行 结果 xff1a gcc o gao o gao c in c o r
  • 使用arecord、aplay 实现录音和播放

    Linux应用开发 第八章 ALSA应用开发 腾讯云开发者社区 腾讯云 tencent com period size 指定 period size buffer size 指定 buffer size 259条消息 使用arecord a
  • git 查看某个文件的修改记录

    1 git log filename 可以看到fileName相关的commit记录 2 git log p filename 可以显示每次提交的diff 3 查看某次提交中的某个文件变化 xff0c 可以直接加上fileName git
  • C语言wav格式详解,代码实践

    393条消息 C语言wav格式详解 xff0c 代码实践 c语言写wav文件 白屿林的博客 CSDN博客
  • RT-Thread 命令自启动详解

    408条消息 RT Thread 自动初始化详解 init app export Nameless Y的博客 CSDN博客 在msh c增加如下代码 xff0c 即可实现自启动 static int exec audio sample vo
  • (无人机方向)ros小白学习之路(一)ROS创建节点与编译

    文章目录 前言ROS创建节点与编译创建工作空间 1 创建和初始化 2 编译 3 为新建的工作空间配置系统环境ros功能包的创建1 xff1a 自定义功能包的创建2 xff1a 在github上下载功能包源码 ROS功能包常用指令1 xff1
  • 使用pjsip传输已经编码的视频

    pjsip功能很强 xff0c 做sip rtp语音通话库首选 在2 0之后 xff0c 也支持视频 不过 xff0c 它的视频功能缺省是从视频设备采集 xff0c 然后进行编译 xff0c 再发送出去的 假设 xff0c 我们已经有了视频
  • CentOS7中英文输入法及切换

    安装完CentOS7后如果没有拼音输入法 xff0c 先在输入源里找下有没有 打开设置 gt gt 区域和语言 gt gt 输入源 搜索pinyin就可以找到拼音输入法 xff0c 然后点击添加 回到设置 xff0c 选择键盘 gt gt
  • rtpsession详解

    以下过程仅分析推送视频模式 xff1a 服务器监听rtsp端口号 xff0c 当有客户端连接后 xff0c 会创建RTSPSession xff0c 在客户端请求过程中的announce时 xff0c 会创建RTPSession xff0c
  • gdb调试coredump(使用篇)

    什么是coredump Coredump叫做核心转储 xff0c 它是进程运行时在突然崩溃的那一刻的一个内存快照 操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下 xff0c 会把进程此刻内存 寄存器状态 运行堆栈等信息转储保存在