首页 > 系统相关 >【内核】【转载】记一次Linux Hung Task分析过程

【内核】【转载】记一次Linux Hung Task分析过程

时间:2024-10-07 21:32:47浏览次数:1  
标签:Task 里面 work flush 地址 线程 rmdir Linux Hung

vmcore-dmesg.txt截图如下,崩溃栈里面有我们产品的驱动,现在要分析出是不是我们导致的。系统崩溃是因为触发了hung task检测条件,系统panic了。所谓hung task,就是进程的状态为D状态,即TASK_UNINTERRUPTIBLE状态,短时间的D状态是正常的,长时间就会有问题了,可能系统IO有问题,也可能其他bug导致。

img

直接分析vmcore,先看下有哪些进程是D状态:

img

有5个进程是D状态,不清楚是哪个进程D状态导致的,又看了下vmcore-dmesg.txt,触发hung task检测条件的进程为1号进程,看下1号进程的调用栈:

img

从这个调用栈大概可以知道,1号进程(systemd)调用rmdir删除某个文件夹(系统调用号0x0000000000000054,RAX值)时,在内核态等待完成(wait_for_completion)时,系统长时间没有完成并唤醒该进程。首先大概分析看看是删除什么文件夹,根据64位系统调用原理,RDI寄存器里面的地址存储了应用层的文件路径,先获取对应的物理页:

img

物理页(PAGE)是a2c570,但是很遗憾,这个系统开启了swap,这个应用层物理页已经被swap out了,没法读取里面的内容,那只能在内核态栈里面找找了。

img

查看do_rmdir代码,在代码开始的地方,有几个局部变量name、dentry、nd。这几个变量里面应该都存储了路径相关的信息,但是这些变量不一定会保存在栈里面,有可能是保存在寄存器里面,这个需要结合汇编代码去分析,通过dis do_rmdir,找到call user_path_parent的地方,这个函数返回值是name,保存在rax寄存器中,我们发现汇编里面是直接使用的rax,并没有保存到栈里面,因此,我们无从知道name的内容;user_path_parent这个函数有四个参数,分别使用rdi,rsi,rdx,rcx传递,rdx中即保存了nd的地址,那rdx里面的地址是怎么来的呢,上图红框里面的那条汇编指令就是给rdx赋值:lea 0x10(%rsp),%rdx,即把rsp+0x10的地址赋给了rdx,换句话说,rsp+0x10的地方就是nd的起始地址。bt -f 1看下do_rmdir的栈帧:

img

vmcore里面的do_rmdir的rsp是ffff8835b948fd58这个地址,但是这个时候应该是do_rmdir里面call了vfs_rmdir了,因此,在调用user_parent_path的时候,rsp应该是ffff8835b948fd60(后面只有call,没有push了),所以nd的地址为ffff8835b948fd70(ffff8835b948fd60+0x10),我们用这个地址看下nd里面的内容:

img

可以看到,是在删除一个名为sysstat.service的目录,不过这里只是last name,不是完整的路径,完整的路径还需要结合mnt和dentry去向上回溯,mnt和dentry也同样保存在nameidata里面。这里我就不继续分析到底是哪个完整路径了。

现在已经清楚,systemd 1号进程调用rmdir删除sysstat.service目录时,卡住了,至于为什么卡住,我们下面继续分析。还是回到systemd的内核栈帧上,从调用栈可以看出,do_rmdir里面调用了vfs_rmdir,vfs_rmdir里面调用了cgroup_rmdir,这个cgroup应该是和docker相关的技术,在cgroup_rmdir里面,最终调用了flush_work后,调用wait_for_completion等待完成,但是一直未能等待到,从而导致了panic。flush_work这个和Linux内核的工作队列有关,工作队列是Linux内核中异步执行的一种机制,用于延迟执行一些需要在进程上下文中执行的任务,而flush_work是需要等待work执行完成,才继续向下执行,相当于把异步变成了同步。

bool flush_work(struct work_struct *work)
{
        //
	// struct wq_barrier {
	// struct work_struct	work;
	// struct completion	done;
        // };
	//  wq_barrier有work信息,又有completion信息,既能执行work,又能等待完成
	struct wq_barrier barr;

	lock_map_acquire(&work->lockdep_map);
	lock_map_release(&work->lockdep_map);

	// start_flush_work主要就是设置barr的逻辑
	if (start_flush_work(work, &barr)) {
	    // 等待,在这里面设置为D状态然后schedule,等待被唤醒,120s未被唤醒
		wait_for_completion(&barr.done);
		destroy_work_on_stack(&barr.work);
		return true;
	} else {
		return false;
	}
}

flush_work主要逻辑是在start_flush_work里面:

img

start_flush_work里面主要就是get和insert,最终会调用wake_up_process去唤醒对应的kworker线性去执行worker,所有的数据和结构都会保存到flush_work函数里面的barr这个栈变量中,如果知道了barr的地址,就可以知道最终是唤醒哪个kworker去执行的。

img

结合反汇编和源码,可以看到,rbp-0x68的地址即为barr的地址

img

那么flush_work的rbp地址是多少呢,上图所示,ffff8835b948fbf0这个地址是call flush_work自动压入的,此时rsp即为ffff8835b948fbf0,flush_work里面,先push了rbp,然后又mov rsp rbp,所以,rbp是ffff8835b948fbe8,即ffff8835b948fbf0-0x8,所以wq_barrier的地址为ffff8835b948fb80(ffff8835b948fbe8-0x68),看下里面的内容:

img

可以简单验证下找到的是否正确,这个结构里面有个func的函数指针,根据代码,这个函数指针指向了wq_barrier_func这个函数,看下我们获取的结构里面func是否为wq_barrier_func。

img

是一致的,于是可以确认找到的wq_barrier的地址是正确的,于是我们顺着wq_barrier->data这个藤,最终找到了worker这个瓜:

img

最终我们从woker里面找到了那个kworker线程,task为0xffff8835b886af70

img

这个kworker的名为kworker/3:2,也就是绑定在CPU3上的一个内核线程,这个线程从栈来看,还是一个被调度出去的状态,但是线程的state已经是RUNNING了,那就是说,这个线程已经被唤醒加入到runqueue等待执行了,通过runq命令看下执行队列的排队情况:

img

可以看到,kworker/3:2已经就绪,就等着前面的线程执行完成了,而当前正在执行的这个lo开头的线程是一个其他厂商的一个内核线程,有没有可能是这个线程某个原因死循环了,一直在跑,导致后面排队的线程得不到执行机会呢。

img

可以看到,这个线程确实有问题,占用了1038秒的CPU时间,但是只主动切换了1次,也就是说,这个内核线程一直在跑,基本就没有退出过,不过这种情况应该会导致soft_lockup才对,为啥vmcore-dmesg.txt里面没有呢,这个需要看下当前环境上softlockup的配置了,通过sysctl -a|grep watchdog以及sysctl -a|grep softlock可以看到,kernel.watchdog已经关闭,难怪没有能产生soft_lockup日志。如果开启的话,系统早就因为这个内核线程softlockup而panic了,根本不会因为这个问题再引出一个hung task的问题。至此,问题分析清楚,需要客户联系该厂商排查。

原文链接:https://zhuanlan.zhihu.com/p/691542282?utm_campaign=shareopn&utm_medium=social&utm_psn=1782729650826387458&utm_source=wechat_session

标签:Task,里面,work,flush,地址,线程,rmdir,Linux,Hung
From: https://www.cnblogs.com/dongxb/p/18450684

相关文章

  • 4、Linux中断系统中的重要数据结构
    本节内容,可以从request_irq(include/linux/interrupt.h)函数一路分析得到。能弄清楚下面这个图,对Linux中断系统的掌握也基本到位了 最核心的结构体是irq_desc,之前为了易于理解,我们说在Linux内核中有一个中断数组,对于每一个硬件中断,都有一个数组项,这个数组就是irq_desc......
  • Centos linux6 中/etc/rc.d/rc.sysinit配置文件的作用
    系统初始化脚本功能设置主机名设置欢迎信息激活udev和selinux挂载/etc/fstab文件中定义的文件系统检测根文件系统,并以读写方式重新挂载根文件系统设置系统时钟激活swap设备根据/etc/sysctl.conf文件设置内核参数激活lvm及softwareraid设备加载额外设备的驱动程序清理操作......
  • 深入理解Linux进程调度(下)
    一、SMP管理在继续讲解之前,我们先来说一下多CPU管理(这里的CPU是指逻辑CPU,在很多语境中CPU都是默认指的逻辑CPU,物理CPU要特别强调是物理CPU)。最开始的时候计算机都是单CPU的,叫做UP(Uni-Processor),操作系统也只支持UP。后来计算机慢慢发展成了多CPU(包括多物理CPU和多核技术),于是......
  • std::packaged_task<返回类型(参数类型)>
    std::packaged_task概述std::packaged_task是C++11引入的标准库模板类,用于包装一个可调用对象(如函数、lambda表达式、函数对象等),使其能够与std::future协同工作。简单来说,它将一个任务(可调用对象)包装起来,并允许你获取该任务的执行结果,这样你可以在一个线程中执行任务,并在......
  • linux中的source命令和bash命令各有什么作用
    在Linux中,`source`命令和`bash`命令都是用来执行shell脚本或者设置环境变量的,它们在Shell编程和日常的系统管理任务中经常被用到。下面我简要解释一下这两个命令的作用:1.`source`命令:使用`source`命令可以读取并执行一个shell脚本文件中的命令,就好像脚本中的命令是直接在当......
  • Rockchip RK3588 - Rockchip Linux Recovery recovery源码分析
    ----------------------------------------------------------------------------------------------------------------------------开发板:ArmSoM-Sige7开发板eMMC:64GBLPDDR4:8GB显示屏:15.6英寸HDMI接口显示屏u-boot:2017.09linux:5.10-------------------------------......
  • `std::packaged_task`、`std::thread` 和 `std::async` 的区别与联系
    std::packaged_task、std::thread和std::async的区别与联系std::packaged_task、std::thread和std::async都是C++11中提供的并发工具,用于执行任务并处理多线程操作。虽然它们都有类似的作用(并发执行任务),但在功能和使用方式上有显著区别。下面分别解释它们的特点,并说明它......
  • 玄机蓝队靶场_应急响应_48:第五章 linux实战-挖矿(未作出)
    参考:https://blog.csdn.net/administratorlws/article/details/139995863有机会会再做一次。一些想法:黑客利用web服务入侵成功站点后,一般除了留下web木马外,还会留有系统后门和病毒比如可能的挖矿病毒。检查web渗透入口的木马,三个方式,第一个是直接从日志里面硬找,从上传的文件......
  • 【GT240X】【06】Linux文本编辑软件vim
    目录一、说明二、什么是vim?三、vi/vim的使用3.1命令模式3.2输入模式3.3底线命令模式四、vi/vim按键说明4.1 一般模式可用的光标移动、复制粘贴、搜索替换等4.2 一般模式切换到编辑模式的可用的按钮说明4.3一般模式切换到指令行模式的可用的按钮说明一......
  • 【linux安全】禁用 ASLR
    地址空间布局随机化(ASLR)是在大多数现代操作系统中实施的一种安全措施。它会改变程序使用的内存地址,包括堆栈、堆和库,使攻击者更难利用漏洞。在Linux中,可以使用/proc/sys/kernel/randomize_va_space文件配置ASLR。地址空间布局随机化(ASLR)安全功能,使攻击者更难预测特定函......