前言
首先我们先引出一个新的概念,叫核心转储。linux系统提供了一种能力,操作系统可以将一个进程在异常的时候将核心代码部分进行核心转储,将内存中进程的相关数据全部dump到磁盘中,一般这个文件会在当前进程的运行目录下,形成core.pid这样的二进制文件。当然如果我们使用的是云服务器的话,这个核心转储功能是默认关闭的,但是我们可以通过命令将这个功能打开:
使用命令:ulimit -a 查看当前系统中特定资源对应的上限
而我们圈出的core file size就是核心转储的功能,默认为0就是关闭状态,想要打开使用选项:
ulimit -c 10240就是将核心转储文件的大小设置为10240.
下面我们直接用一些信号发送给正常的代码使之异常退出看是否有核心转储文件生成:
#include <iostream> #include <signal.h> #include <unistd.h> using namespace std; void handler(int signo) { cout<<"我们的进程确实收到了"<<signo<<"号信号"<<endl; exit(1); } int main() { while (true) { cout<<"我是一个正常的进程,正在模拟某种异常:"<<getpid()<<endl; sleep(1); } return 0; }
下面我们将程序运行起来然后发信号:
为什么没有生成核心转储文件呢?下面我们试试其他信号:
为什么8号信号就可以呢?还记得我们上一篇讲过信号的方式有term和core,term是终止,那么core是什么呢?其实term就是终止,以core终止会先进行核心转储,然后再终止进程。下面我们看一下刚刚的核心转储文件:
里面全是二进制乱码,也就是说这个文件不是给我们看的是给计算机看的,下面我们说一下核心转储有什么用:其实是为了在异常后方便进行调试。
首先将代码修改一下:
然后我们将makefile中的命令调成可调试状态:
只需要在g++指令后面加上-g选项,下面我们重新运行一下代码:
有了核心转储文件后下面我们用gdb进入调试模式:
然后我们直接输入指令:core-file +核心转储文件
这个时候gdb自动帮我们找到了报错的代码行数以及原因,这就验证了我们刚刚说的核心转储文件可以帮助我们在产生异常后方便进行调试,这种方案叫事后调试。当然为什么核心转储功能这么好用云服务器却要默认关闭呢?因为这个文件所占内存很大,一旦有多个出错每次都生成这样的核心转储文件那么服务器很容易挂掉,所以默认不支持打开核心转储功能,下面我们用指令将核心转储功能关闭:
我们只需要用指令:ulimit -c 0即可关闭:
下面我们讲一下系统如何识别核心转储的打开或关闭:
还记得我们之前讲的位图吗?我们的core标志位就在中间的那个比特位,如果这个位置的二进制为1则说明开启了核心转储功能,否则就是没有开始,要验证也很简单,只需要让子进程出现异常让父进程去接收,下面我们演示一下:
int main() { pid_t id = fork(); if (id==0) { cout<<"野指针问题 ....here"<<endl; cout<<"野指针问题 ....here"<<endl; cout<<"野指针问题 ....here"<<endl; int* p = nullptr; *p = 100; //对空指针进行解引用 cout<<"野指针问题........"<<endl; cout<<"野指针问题 ....here"<<endl; cout<<"野指针问题 ....here"<<endl; exit(0); } int status = 0; waitpid(id,&status,0); cout<<"exit code: "<<((status>>8)&0xFF)<<endl; cout<<"exit signal: "<<(status&0x7F)<<endl; cout<<"core dump flag: "<<((status>>7)&0x1)<<endl; return 0; }
以上代码能在退出后给我们打印返回值,退出信号以及core标志位:
下面我们将核心转储重新打开我们再运行一下程序:
当我们重新运行程序后发现core dump的标志位变成了1也就是核心转储功能被打开了,并且我们成功拿到了核心转储文件:
以上就是核心转储的所有知识,下面我们进入信号的保存。