本文简介:

内核死锁问题一般是读写锁(rw_semaphore)和互斥锁(mutex)引起的,本文主要讲如何通过ramdump+crash工具来分析这类死锁问题。

作者简介:

朴英敏,现就职于国内一家手机研发公司,任职资深系统工程师,主要负责安卓系统方面的调试工作。

0、背景知识点

ramdump是内存转存机制,我们可以在某个时刻把系统的内存转存到一个文件中,然后与符号信息(vmlinux)一起导入到trace32或crash等内存分析工具中做离线分析。是分析崩溃、死锁、内存泄露等内核疑难问题的重要调试手段。

crash是用于解析ramdump的开源工具(http://people.redhat.com/anderson/),是命令行式的交互模式,提供诸多功能强大的调试命令,是分析定位内核复杂问题的利器。

死锁是指两个或两个以上的执行流在执行过程中,由于竞争锁资源而造成的一种阻塞的现象。如图:

1、问题描述

在Android7.1系统中跑monkey时出现界面卡死现象:

1)没有任何刷新,所有输入事件无效,包括电源键

2)watchdog没有重启system_server

3)可以连adb,但ps等调试命令卡住

2、初步分析

由于无法直接用adb调试,用长按电源键的方式进入dump模式并导出ramdump文件,之后再用crash工具载入randump文件开始离线分析。

一般卡死时可能是因为核心线程处在UNINTERRUPTIBLE状态,所以先在crash环境下用ps命令查看手机中UNINTERRUPTIBLE状态的线程,参数-u可过滤掉内核线程:

bt命令可查看某个线程的调用栈,我们看一下上面UN状态的最关键的watchdog线程:

从调用栈中可以看到proc_pid_cmdline_read()函数中被阻塞的,对应的代码为:

这里是要获取被某个线程mm的mmap_sem锁,而这个锁又被另外一个线程持有。

3、推导读写锁

要想知道哪个线程持有了这把锁,我们得先用汇编推导出这个锁的具体值。可用dis命令看一下proc_pid_cmdline_read()的汇编代码:

0xffffff99a680aaa0处就是调用down_read()的地方,它的第一个参数x0就是sem锁,如:

x0和x28寄存器存放的就是sem的值,那x21自然就是mm_struct的地址了,因为mm_struct的mmap_sem成员的offset就是104(0x68),用whatis命令可以查看结构体的声明,如:

因此我们只需要知道x21或者x28就知道mm和mmap_sem锁的值。

函数调用时被调用函数会在自己的栈帧中保存即将被修改到的寄存器,所以我们可以在down_read()及它之后的函数调用中找到这两个寄存器:

也就是说下面几个函数中,只要找到用到x21或x28,必然会在它的栈帧中保存这些寄存器。

先从最底部的down_read()开始找:

显然它没有用到x21或x28,继续看rwsem_down_read_failed()的汇编代码:

在这个函数中找到x21,它保存在rwsem_down_read_failed栈帧的偏移32字节的位置。

rwsem_down_read_failed()的sp是0xffffffd6d9e4bcb0

sp + 32 =0xffffffd6d9e4bcd0,用rd命令查看地址0xffffffd6d9e4bcd0中存放的x21的值为:

用struct命令查看这个mm_struct:

这里的owner是mm_struct所属线程的task_struct:

sem锁的地址为0xffffffd76e349a00+0x68= 0xffffffd76e349a68,因此:

分析到这里我们知道watchdog线程是在读取1651线程的proc节点时被阻塞了,原因是这个进程的mm,它的mmap_sem锁被其他线程给拿住了,那到底是谁持了这把锁呢?

4、持读写锁的线程

带着问题我们继续分析,首先通过list命令遍历wait_list来看一下共有多少个线程在等待这个读写锁:

从上面的输出可以看到一共有2个写者和有17个读者在等待,这19个线程都处于UNINTERRUPTIBLE状态。

再回顾一下当前系统中所有UNINTERRUPTIBLE状态的线程:

其中除标注红颜色的5个线程外的19个线程,都是上面提到的等待读写锁的线程。当持锁线程是写者,我们可以通过rw_semaphore结构的owner找到持锁线程。可惜这里owner是0,这表示持锁者是读者线程,因此我们无法通过owner找到持锁线程。这种情况下可以通过search命令加-t参数从系统中所有的线程的栈空间里查找当前锁:

一般锁的值都会保存在寄存器中,而寄存器又会在子函数调用过程中保存在栈中。所以只要在栈空间中找到当前锁的值(0xffffffd76e349a68),那这个线程很可能就是持锁或者等锁线程

这里搜出的20个线程中19个就是前面提到的等锁线程,剩下的1个很可能就是持锁线程了:

查看这个线程的调用栈:

由于2124线程中存放锁的地址是0xffffffd6d396b8b0,这个是在handle_mm_fault()的栈帧范围内,因此可以推断持锁的函数应该是在handle_mm_fault()之前。

我们先看一下do_page_fault函数:

代码中确实是存在持mmap_sem的地方,并且是读者,因此可以确定是2124持有的读写锁阻塞了watchdog在内的19个线程。

接下来我们需要看一下2124线程为什么会持锁后迟迟不释放就可以了,但在这之前我们先看一下system_server的几个UNINTERRUPTIBLE状态的线程阻塞的原因。

5、其他被阻塞的线程(互斥锁的推导)

先看一下ActivityManager线程:

通过调用栈能看到是在binder_alloc_new_buf时候被挂起的,我们得先找出这个锁的地址。

首先从mutex_lock()函数入手:

从它的声明中可以看到它的参数只有1个,就是mutex结构体指针。

再看看mutex_lock函数的实现:

mutex_lock的第一个参数x0就是我们要找的struct mutex,在0xffffff99a74e1648处被保存在x19寄存器中,接着在0xffffff99a74e1664处调用了__mutex_lock_slowpath(),因此我们可以在__mutex_lock_slowpath()中查找x19:

由于__mutex_lock_slowpath()的sp是0xffffffd75ca379a0:

因此x19的值保存在0xffffffd75ca379a0+ 16 = 0xffffffd75ca379b0

我们要找的mutex就是0xffffffd6dfa02200:

其中owner就是持有该所的线程的task_struct指针。它的pid为:

查看这个线程的调用栈:

这个3337线程就是前面提到的被读写锁锁住的19个线程之一。

用同样的方法可找到audioserver的1643线程、system_server的1909、2650线程也都是被这个3337线程持有的mutex锁给阻塞的。

总结起来的话:
1)一共有4个线程在等待同一个mutex锁,持锁的是3337线程
2)包括3337的19个线程等待着同一个读写锁,持锁的是2124线程。

也就是说大部分的线程都是直接或者间接地被2124线程给阻塞了。

6、死锁

最后一个UNINTERRUPTIBLE状态的线程就是2767(sdcard)线程:

可以看出2124线程是等待fuse的处理结果,而我们知道fuse的请求是sdcard来处理的。

这很容易联想到2124的挂起可能跟2767(sdcard)线程有关,但2124线程是在做read请求,而2767线程是在处理open请求时被挂起的。

就是说sdcard线程并不是在处理2124线程的请求,不过即使这种情况下sdcard线程依然能阻塞2124线程。因为对于一个APP进程来说,只会有一个特定的sdcard线程服务于它,如果同一个进程的多线程sdcard访问请求,sdcard线程会串行的进行处理。

如果前一个请求得不到处理,那后来的请求都会被阻塞。跟之前mutex锁的推导方法一样,得2767线程等待的mutex锁是0xffffffd6948f4090,

它的owner的task和pid为:

先通过bt命令查找2124的栈范围为0xffffffd6d396b4b0~0xffffffd6d396be70:

从栈里面可以找到mutex:

mutex值在ffffffd6d396bc40这个地址上找到了,它是在__generic_file_write_iter的栈帧里。

那可以肯定是在__generic_file_write_iter之前就持锁了,并且很可能是ext4_file_write_iter中,查看其源码:

这下清楚了,原来2124在等待2767处理fuse请求,而2767又被2124线程持有的mutex锁给锁住了,也就是说两个线程互锁了。

本文只限于介绍如何定位死锁问题,至于如何解决涉及到模块的具体实现,由于篇幅的关系这里就不再赘述了。

(完)

Linux阅码场原创精华文章汇总

更多精彩,尽在"Linux阅码场",扫描下方二维码关注

如果您觉得文章不错,请点一点右下角“在看”为朴老师疯狂打Call吧~

(好文重发)朴英敏:用crash工具分析Linux内核死锁的一次实战相关推荐

  1. crash工具分析linux内核,如何使用crash工具分析Linux内核崩溃转储文件

    满意答案 使用 crash 的先决条件 1. kernel 映像文件 vmlinux 在编译的时候必须指定了 -g 参数,即带有调试信息. 2. 需要有一个内存崩溃转储文件(例如 vmcore),或者 ...

  2. linux 安腾,时代谢幕:英特尔安腾IA-64的Linux内核支持已成孤儿

    原标题:时代谢幕:英特尔安腾IA-64的Linux内核支持已成孤儿 来源:cnBeta.COM 上周,Phoronix 报道了与 Linux 5.11 内核中的安腾(Itanium)IA-64 支持有 ...

  3. 一文分析Linux内核klist链表

    1.klist链表相关结构 内核源码中,klist相关的头文件是include/linux/klist.h,实现的文件是lib/klist.c中,接下来分析klist链表头和klist链表节点的定义: ...

  4. crash分析linux内核崩溃转储文件vmcore

    文章目录 一.调试环境准备 二.使用crash分析vmcore 1.bt命令 2.log命令 3.dis命令 4.mod命令 5.sym命令 6.ps命令 7.files命令 8.vm命令 9.tas ...

  5. crash工具分析sysdump使用

    一.准备环境 1)获取crash工具.注意区分版本(arm/arm64/x86_64). 2)获取对应软件版本的符号表文件(如vmlinux),可以将该文件放置 crash工具同一目录下. 3)获取s ...

  6. linux进程 crash 分析工具,crash工具分析大型Linux服务器死锁实战

    Linux服务器背景: CPUS: 40 MEMORY: 127.6 GB MACHINE: x86_64 (2199 Mhz) Linux Kernel: 4.4.121 TASKS: 19411 ...

  7. oracle离线文档查dbms_小白小记-logminer工具分析离线归档日志

    一.安装logminer工具 SQL>@$ORACLE_HOME/rdbms/admin/dbmslm.sq SQL>@$ORACLE_HOME/rdbms/admin/dbmslmd.s ...

  8. 【ARM Linux 系统稳定性分析入门及渐进 1 -- Crash 工具简介】

    文章目录 1.1 Kexec 和 Kdump 1.1.1 Kexec 加载优点 1.1.2 kdump 功能 1.1.3 kdump原理 1.2 Crash tool 1.2.1 ramdump 机制 ...

  9. 用crash分析内核死锁的一次实践

    [推荐阅读] 什么是死锁? 内核死锁的debug方法 上文讲了通过lockdep的方式可以debug出死锁的信息,但是如果出问题的系统没有lockdep的配置,或者没有相关的日志该怎么办?这里分享通过 ...

最新文章

  1. 关闭、刷新、跳转、离开当前网页前提示
  2. java-println连接
  3. 51单片机c语言乘法,求一个 89C51 简易计算器的c语言程序 只要加减乘除就行!
  4. c语言消字母游戏实验报告,C语言编程实验报告格式示例
  5. 海口只有阳光沙滩?错,人家还是“最佳智慧城市”
  6. Java编译过程(传送门)
  7. 【ES9(2018)】for await...of
  8. 201712-2-游戏
  9. Dreamweaver自动生成的垃圾代码
  10. 仿微信导航栏滑动门练习
  11. 支持有道词典单词本更新
  12. 高品质的算法混响插件-Initial Audio AR1 Reverb v1.0.1 WiN-MAC
  13. 【算法专题】链表排序算法总结
  14. html如何使文本自动换行,html 文本自动换行
  15. HTML CSS设计与构建网站 ([美]达科特) PDF原版​
  16. 华为U8500刷了2.2后自定义铃声,短信通知音,闹铃音的方法
  17. gerrit 用法 topic
  18. 日志收集之--将Kafka数据导入elasticsearch
  19. 合伙企业对外债务如何承担
  20. 解决手机端微信公众号内input输入框获取焦点后,底部导航栏显示在输入法软键盘上面的问题

热门文章

  1. 二、C语言基本数据类型全解和基本的数据存储方式
  2. 我的所有的浏览器被hao123 挟持了,终极解决方案
  3. shapely图形合并
  4. framemaker 导出word文档,利用list遍历输出换行以及保留段落格式(首行空两格)
  5. Android第三方推送到达率调研
  6. android获取uid,Android获得UID的办法
  7. 为何硅谷第一性感女人也没能拯救雅虎?
  8. 服务器怎么增加独立显卡,dellr610服务器增加独立显卡(dell服务器装显卡)
  9. 2022-2027年中国发动机行业市场调研及未来发展趋势预测报告
  10. SSL双向认证和单向认证原理