Linux 崩溃了,你能做什么?

公众号关注 「奇妙的 Linux 世界」

设为「星标」,每天带你玩转 Linux !

2739c522e777b73de0c1c9ee20bdb374.png

Linux崩溃了,你能干什么

如果你发现你的Linux机器重启了,你能查出来是什么原因导致的吗。

绝大多数人是束手无策的,今天,本文(结合真实案例)教你怎么做。

一、首先你要有dump文件

接触过Linux的,大概知道有个core dump的,很多人以为这是核心转储,nonono,它是内存转储的意思,core来源于早年间磁芯存储器的那个磁芯(core)。

实现core dump机制有很多工具,kdump技术是目前最可靠、最常用的,已被主要的Linux厂商选用。当Linux内核崩溃时,kdump工具捕获当时内存等状态信息,生成转储文件(vmcore),保留现场证据,然后才重启。

当然,如果要用kdump,需要安装kdump工具、修改内核启动参数、修改kdump配置文件参数、启动kdump服务功能。

二、怎么解析dump文件

如果你装了kdump,Linux崩溃重启后,你会在/var/crash目录发现vmcore文件,恭喜你,你可以用它来发现根因了。

为了分析core dump,你需要安装crash工具,crash工具是RedHat公司提供的一个开源的内核分析工具,它在gdb的基础上实现了解析内核的功能。

你还需要安装Linux内核相应版本的debuginfo包,这个包安装好后会在操作系统上生成一个vmlinux文件,该文件包含完整的符号信息,用于提供调试信息。

三、怎么使用crash工具

crash工具包括了很多命令,包括查看内核日志的log命令、查看调用栈的bt命令、查看进程情况的ps命令、查看某个地址对应符号的sym命令、查看文件系统信息的files命令等。

总之,有了这个大杀器,你再有一点操作系统知识,有一点编程知识,就能干一般人干不了的事了。

比如,有一天,你发现你的若干台服务器意外重启了,你一脸懵,你的领导责令你务必找到根因。

你就可以悠然打开crash工具,来一趟探因之旅:

crash /usr/lib/debug/lib/modules/2.6.32-754.35.1.el6.x86_64/vmlinux  /var/crash/vmcore

上面那个vmlinux,是调试所需的内核镜像;上面那个vmcore,就是core dump。

四、用sys命令看看基本信息

用crash打开vmcore文件后,使用sys命令,你可以看到系统内核的基本信息,比如崩溃时系统中的进程数量、系统内核版本、内存大小、系统崩溃时的报错信息等。

比如,报错信息是:BUG: unable to handle kernel paging request at ffffffffa0395070

懂的人都懂,这是内核分页请求报错。

可能的原因是:错误的内存访问、内存不足、硬件故障等等。

当然最可能的是:错误的内存访问。

下面,我们用bt命令看看内核崩溃的调用栈信息。

所谓调用栈,就是谁调用了谁,谁又进一步调用了谁。

五、用bt命令查看进程信息

这一步是非常重要的,毕竟,我们非常想看到,机器是怎么一步步崩溃的。

使用crash工具的bt命令(栈跟踪backtrace的缩写)查看调用栈信息,显示信息如下:

crash> bt
第1行   PID: 177488  TASK: ffff880435b92ab0  CPU: 2   COMMAND: "ss"
第2行   #0 [ffff880437c0b7e0] machine_kexec at ffffffff8104179b
第3行   #1 [ffff880437c0b840] crash_kexec at ffffffff810d7a52
第4行   #2 [ffff880437c0b910] oops_end at ffffffff81560310
……
第10行   #8 [ffff880437c0bb40] page_fault at ffffffff8155f265
第11行      [exception RIP: strnlen+9]
第12行      RIP: ffffffff812ae3a9  RSP: ffff880437c0bbf8  RFLAGS: 00010286
……
第25行  #15 [ffff880437c0be70] proc_reg_read at ffffffff8120faf0
第26行  #16 [ffff880437c0bec0] vfs_read at ffffffff811a3447
第27行  #17 [ffff880437c0bf00] sys_read at ffffffff811a3791
第28行  #18 [ffff880437c0bf50] system_call_fastpath at ffffffff81566391

不用细看,bt在第一行就说了,崩溃就是“ss”这个程序引起的。从第28行开始,ss调用了system_call_fastpath、然后是sys_read(第27行)、然后是我这里省略了的一连串调用,然后是第10行令人胆战心惊的page_fault,然后是oops_end(哦,要完蛋了,第4行)、crash_kexec(准备kdump,第3行)、machine_kexec(调起一个新内核采集信息,第1行),然后kdump就生成了core dump文件:vmcore。

注意第25行,可以看出ss干了一件事,调用了proc_reg_read,这表明它读了proc文件系统。

proc是一个虚拟文件系统,提供了内核和进程的运行信息。

ss读proc文件是正常的,因为要读取一些内核层面的信息,但正是这个读,导致了崩溃。

注:ss是一个用于查看和分析Linux系统中的网络连接和套接字(socket)状态的工具。它是 netstat命令的替代品,通常比netstat更加高效和快速。

下面我们看看ss这个进程的具体信息。

六、用ps命令查看都有哪些进程

使用crash工具的ps命令查询内核崩溃前所有进程的状态信息

比如:

crash> ps
PID    PPID    CPU        TASK           ST  %MEM  VSZ  RSS    COMM
...
177488 64302    1  ffff880436662ab0   RU   0.0  6280  568     ss
...

当然,崩溃时的进程有很多了,这里只显示ss进程,可以看到它的进程号为177488。

有了ss的进程信息,现在我们看看ss到底调用了哪个文件。

七、用files命令查看进程访问了哪个文件

一般是使用struct file命令查看某进程访问文件的信息。

刚才我们用ps命令查到ss的进程地址为ffff880436662ab0,用它作为struct file的输入参数。

crash> struct file.f_path ffff880436662ab0
  f_path = {
    mnt = 0xffff880432adbe80, 
    dentry = 0xffff880101cae5c0
  }

该命令显示了ss进程访问文件路径的结构信息,mnt表示文件所在的挂载点,dentry表示文件名在地址ffff880101cae5c0。

接下来使用files命令来解析这个dentry地址。

crash> files -d 0xffff880101cae5c0
     DENTRY           INODE           SUPERBLK     TYPE      PATH
ffff880101cae5c0 ffff880101c1d598 ffff88043a23e800 REG  /proc/slabinfo

此时可以得知,故障时,ss进程访问的文件是/proc/slabinfo。

注:/proc/slabinfo文件包含了当前内核中所有slab内存的详细信息。

八、使用sym命令看故障相关源码

从前面bt命令的调用栈信息看到,异常报错位置为[exception RIP: strnlen+9](第11行),RIP指向异常调用地址为ffffffff812ae3a9(第12行)。

使用sym命令,看看这个异常地址是个啥。

crash> sym ffffffff812ae3a9
ffffffff812ae3a9 (T) strnlen+9 /usr/src/debug/kernel-2.6.32-754.35.1.el6/ linux-2.6.32-754.35.1.el6.x86_64/lib/string.c: 407

这个命令很牛,它告诉我们,这个异常地址,对应的源码(string.c)和行号(第407行)都告诉我们了。

我第一次见到这个的时候,不禁惊呼,这么牛啊,从内存dump能看出问题源码在哪?

对,就这么牛,kdump很牛,debuginfo也很牛,一个用于调试,一个提供调试信息,程序员不会亏待自己的。

注:当你调试一个内核时,需要安装对应版本的debuginfo包,如下:

debuginfo-install kernel-debuginfo-common-2.6.32-754.35.1.el6.x86_64.rpm

当然,看到源码并不稀奇,Linux是开源的嘛!(如果你玩Windows,如果机器崩溃了,那就崩溃了吧。)

这段完整代码如下:

size_t strlen(const char *s) {
  const char *sc;
  for (sc = s; *sc != '\0'; ++sc)/*这就是那个第407行*/;
  return sc - s;
}

这段代码通过for循环,从输入字符串初始字符s开始,遍历其所指的内容,循环直到遇到字符串结尾的空字符'\0',最后返回字符串长度。

那个*sc就是读sc这个地址里的内容。

但是,读着读着,就崩溃了,因为读到翔了。

八、用log命令查更多的内容

使用log命令查到的信息如下:

crash> log
...
VMAGENTMOD: 3846: init_module: get into init_module, syshook_enable:1
VMAGENTMOD: 177350: cleanup_module: get into cleanup_module 
...

可以看出,内核崩溃前,有加载和卸载某个驱动模块的动作。相关的进程号是3846和177350。

经查询,这2个进程号都属于防病毒工具的进程。它调用的cleanup_module是内核函数,用于卸载驱动模块。

用crash的mod -t命令,显示内核模块加载的详细信息:

crash> mod -t
NAME           TAINTS
syshook_linux  (U)
vmsecmod       (U)

在log命令的输出中,还可以看到“[last unloaded: vmsecmod]”,这说明,内核最后卸载的驱动模块就是vmsecmod。

另外,防病毒工具的本地日志也显示,服务器重启前刚刚执行了停止防病毒进程的操作。

这些信息都告诉我们,这次崩溃的发生,防病毒工具有相当的嫌疑。

注:mod的-t选项,是显示taints信息。所谓taints(污点),是内核运行时的一个标志,用来指示内核在运行过程中遇到了某些潜在问题或非标准情况。如果taints是U,表明该模块是未经签名的,是用户开发的。

九、回到sys命令

我们最开始使用sys命令查到有如下的报错信息:

BUG: unable to handle kernel paging request at ffffffffa0395070

用sym命令看看这个地方到底是何方神圣。

crash> sym ffffffffa0395070
ffffffffa0395070 (r) hash_info_mempool_name [vmsecmod]

可以看到,这个地址来自vmsecmod驱动,对应的源码是hash_info_mempool_name。

现在基本可以判断出来是怎么回事:

防病毒工具申请并使用了slab分配器提供的内存,相关信息记录在/proc/slabinfo中,ss进程会去查询slabinfo,获取必要的信息。就是在访问slabinfo时,造成了内核崩溃。

有人在崩溃前卸载了防病毒的驱动,停止了防病毒服务,按道理,防病毒申请的slab内存应该也释放掉,slabinfo中也不会有对应信息。但是,ss进程居然还在访问这块数据,说明防病毒进程申请的slab内存未正常释放!

知识普及:slab主要用于内核态中的内存分配,可高效分配和管理小块内存。在/proc/slabinfo这个虚拟文件中,记录了系统中所有slab内存块的信息,如对象数量和内存使用量等。ss读取/proc/slabinfo,获取与网络套接字相关的内存使用和分配信息,提供详细的网络连接状态。 

九、原来是防病毒工具的bug

把上述信息给到防病毒厂商,他们的研发工程师分析确认,确实是防病毒客户端有bug,导致了这次重启。

bug很简单,就是在卸载vmsecmod驱动时,应该同步释放所申请的slab内存区,但程序员没有这么做。

在重启的服务器上,有服务器管理工具,它会定期调用ss命令,ss会读取slabinfo,防病毒没有释放slab内存,所以ss仍然可以读取slabinfo中的指针,该指针却指向了已经释放了内存区(vmsecmod驱动曾经用过的地方)。

由于指针指向的内存已经释放,所以这就是访问非法地址,其实就是分页机制无法将该地址映射到物理地址,此时处理器就会向操作系统发出一个“page fault”错误,如果处理器此时处于超级用户模式,系统就会产生一个Oops,哦,完蛋了。

注:如果在用户态访问了非法地址,那么,你大概会得到一个经典的Segmentation fault(初级程序员的噩梦)。

十、当时发生了什么?

那天,某个工程师做了一件事,更新防病毒工具的许可,这个防病毒工具是企业版的,有一个管理端,还有运行在若干台服务器上的防病毒客户端。

他先在防病毒管理端导入软件许可,接着管理端将软件许可分发给每台服务器上的客户端,由客户端本地更新许可文件。

在客户端更新许可文件时,会先停止防病毒客户端进程(更新完许可文件后,再启动进程),停止进程会导致vmsecmod驱动模块的卸载,由于有bug,清理动作不完善,残留了无主的slab内存。

而服务器上部署的自动化工具,会定时执行ss命令,ss遍历slabinfo信息时,读取了在野的指针,引发page fault,内核崩溃。

后记

Linux这么稳定的内核都会崩溃。

做一个内核稳定的人,很不容易呢。

注:本文是一个简单的介绍,相对完整的分析见《小白也能看懂的Linux崩溃分析大法》

本文转载自:「卫sir说」,原文:https://url.hi-linux.com/8dtUW,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。

9eed1314d34724693ea15cb0530bd4c0.gif

🚀 最近,我们建立了一个技术交流微信群。目前群里已加入了不少行业内的大神,有兴趣的同学可以加入和我们一起交流技术,在 「奇妙的 Linux 世界」 公众号直接回复 「加群」 邀请你入群。

🌟『极客视界』科技达人必备综合资讯指南,等你来探索!访问网址 https://bestgeek.org 即可打开新世界。

📕 关注『奇妙的 Linux 世界』公众号,带你开启有趣新生活!更多好用好玩的软件资源,可访问 https://666666.dev 免费获取。

c015163ac1732bd3ac77382722468993.png

你可能还喜欢

点击下方图片即可阅读

ef76f08591b16f0125f051c2ffed1dff.jpeg

ptcpdump: 新一代抓包神器,可捕获任何进程、容器或 Pod 的网络流量

9040b4d79de06b9aa70e36aeb130ee8f.png
点击上方图片,『美团|饿了么』外卖红包天天免费领

066bfe3ab5114ede1c38160aed0771b0.png

更多有趣的互联网新鲜事,关注「奇妙的互联网」视频号全了解!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值