Linux 进程卡住了怎么办?
在我们使用 Linux 系统时,如果网络或者磁盘等 I/O 出问题,会发现进程卡住了,即使用 kill -9 也无法杀掉进程,很多常用的调试工具,比如 strace, pstack 等也都失灵了,是怎么回事?

此时,我们使用 ps 查看进程列表,可以看到卡住的进程状态显示为 D。

man ps 中描述 D 状态是 Uninterruptible Sleep。
Linux 进程有两种睡眠状态:
- Interruptible Sleep,可中断睡眠,在 ps 命令中显示 S。处在这种睡眠状态的进程是可以通过给它发送信号来唤醒的。
- Uninterruptible Sleep,不可中断睡眠,在 ps 命令中显示 D。处在这种睡眠状态的进程无法立即处理任何发送给它的信号,这也是无法用 kill 杀掉它的原因。
在 Stack Overflow 有一个解答:
kill -9只是给进程发送了一个SIGKILL信号,当一个进程处于特殊状态时(信号处理,或者系统调用中)会无法处理任何信号,包括SIGKILL也不能被正确处理,导致进程不能被立即杀掉,也就是我们常说的D状态(不可中断的睡眠状态)。那些常用的调试工具 (比如strace、pstack等)一般也是利用某个特殊的信号来实现的,在这种状态下也是无法使用。
可见 D 状态的进程一般是处在某个内核态的系统调用中,那怎么知道是哪个系统调用,又是在等待什么呢?幸好 Linux 下提供了 procfs(就是 Linux 下的 /proc 目录), 通过它就可以看到任何一个进程的当前内核调用栈。下面我们用访问 JuiceFS 的进程来模拟一下(因为 JuiceFS 客户端基于 FUSE,是用户态的文件系统,比较容易模拟 I/O 故障)。
先将 JuiceFS 挂载到前台(在 ./juicefs mount 命令中加一个 -f 参数),然后用 Cltr+Z 把这个进程停掉,这时候用 ls /jfs 去访问挂载点,会发现 ls 卡住了。
通过下面的命令可以看到 ls 卡在了 vfs_fstatat 调用上,它会给 FUSE 设备发送 getattr 请求,在等待回应。而 JuiceFS 客户端进程已经被我们停掉了,所以它就卡住了:
$ cat /proc/`pgrep ls`/stack
[<ffffffff813277c7>] request_wait_answer+0x197/0x280
[<ffffffff81327d07>] __fuse_request_send+0x67/0x90
[<ffffffff81327d57>] fuse_request_send+0x27/0x30
[<ffffffff8132b0ac>] fuse_simple_request+0xcc/0x1a0
[<ffffffff8132c0f0>] fuse_do_getattr+0x120/0x330
[<ffffffff8132df28>] fuse_update_attributes+0x68/0x70
[<ffffffff8132e33d>] fuse_getattr+0x3d/0x50
[<ffffffff81220c6f>] vfs_getattr_nosec+0x2f/0x40
[<ffffffff81220ee6>] vfs_getattr+0x26/0x30
[<ffffffff81220fc8>] vfs_fstatat+0x78/0xc0
[<ffffffff8122150e>] SYSC_newstat+0x2e/0x60
[<ffffffff8122169e>] SyS_newstat+0xe/0x10
[<ffffffff8186281b>] entry_SYSCALL_64_fastpath+0x22/0xcb
[<ffffffffffffffff>] 0xffffffffffffffff
这时候按 Ctrl+C 也不能退出。
root@localhost:~# ls /jfs
^C
^C^C^C^C^C
但是用 strace 却能唤醒它,并且开始处理之前的中断信号,然后就退出了。
root@localhost:~# strace -p `pgrep ls`
strace: Process 26469 attached
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
--- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=13290, si_uid=0} ---
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
。。。
tgkill(26469, 26469, SIGINT) = 0
--- SIGINT {si_signo=SIGINT, si_code=SI_TKILL, si_pid=26469, si_uid=0} ---
+++ killed by SIGINT +++
这个时候如果用 kill -9 的话,也是可以把它杀掉的:
root@localhost:~# ls /jfs
^C
^C^C^C^C^C
^C^CKilled
因为 vfs_lstatat() 这种简单的系统调用并没有 屏蔽 SIGKILL,SIGQUIT,SIGABRT 等信号,还可以对它做些常规的处理。
我们再来模拟一个更复杂的 I/O 错误,给 JuiceFS 配置一个无法写入的存储类型,并挂载上,用 cp 尝试往里写入数据,这时候 cp 也会卡住:
root@localhost:~# cat /proc/`pgrep cp`/stack
[<ffffffff813277c7>] request_wait_answer+0x197/0x280
[<ffffffff81327d07>] __fuse_request_send+0x67/0x90
[<ffffffff81327d57>] fuse_request_send+0x27/0x30
[<ffffffff81331b3f>] fuse_flush+0x17f/0x200
[<ffffffff81218fd2>] filp_close+0x32/0x80
[<ffffffff8123ac53>] __close_fd+0xa3/0xd0
[<ffffffff81219043>] SyS_close+0x23/0x50
[<ffffffff8186281b>] entry_SYSCALL_64_fastpath+0x22/0xcb
[<ffffffffffffffff>] 0xffffffffffffffff
怎么卡在 close_fd() ?这是因为往 JFS 写数据是异步的,当 cp 调用 write() 时,数据会先缓存在 JuiceFS 的客户端进程里同时会异步写入到后端存储,等 cp 写完数据,它会调用 close 来确保数据写入完成,对应 FUSE 的 flush 操作。JuiceFS 的客户端在遇到 flush 操作时,需要确保全部写入的数据都持久化到后端存储,而后端存储写入失败了,它就在多次重试的过程中,所以 flush 操作卡住了,还没有回复给 cp,所以 cp 也卡住了。
这个时候如果用 Cltr+C 或者 kill 是可以中断 cp 的运行,因JuiceFS 实现了各种文件系统操作的中断处理,让它放弃当前操作(比如 flush), 返回 EINTR,这样在遇到各种网络故障时可以中断正在访问 JuiceFS 的应用。
这时如果我停止 JuiceFS 客户端进程,让它不能再处理任何 FUSE 请求(包括中断请求),这个时候如果尝试去杀它,就杀不掉了,包括 kill -9 也杀不掉,用 ps 查看进程状态,已经是 D 状态了。
root 1592 0.1 0.0 20612 1116 pts/3 D+ 12:45 0:00 cp parity /jfs/aaa
但这个时候是可以用 cat /proc/1592/stack 来看它的内核调用栈
root@localhost:~# cat /proc/1592/stack
[<ffffffff8132775d>] request_wait_answer+0x12d/0x280
[<ffffffff81327d07>] __fuse_request_send+0x67/0x90
[<ffffffff81327d57>] fuse_request_send+0x27/0x30
[<ffffffff81331b3f>] fuse_flush+0x17f/0x200
[<ffffffff81218fd2>] filp_close+0x32/0x80
[<ffffffff8123ac53>] __close_fd+0xa3/0xd0
[<ffffffff81219043>] SyS_close+0x23/0x50
[<ffffffff8186281b>] entry_SYSCALL_64_fastpath+0x22/0xcb
[<ffffffffffffffff>] 0xffffffffffffffff
内核调用栈显示它卡在 FUSE 的 flush 调用上,这个时候只要恢复 JuiceFS 客户端进程,就可以立即中断 cp 让它退出。
像 close 这种涉及到数据安全性的操作,不是 restartable, 也就不能被 SIGKILL 等随意中断,比如要 FUSE 的实现端响应中断操作才能中断。
因此,只要 JuiceFS 的客户端进程能够健康的响应中断,就不用担心访问 JuiceFS 的应用卡死。或者杀掉 JuiceFS 客户端进程也可以结束当前的挂载点,中断所有在访问当前挂载点的应用。
如有帮助的话欢迎关注我们项目 Juicedata/JuiceFS 哟! (0ᴗ0✿)
Linux 进程卡住了怎么办?的更多相关文章
- Linux进程管理及while循环
目录 进程的相关概念 进程查看及管理工具的使用 Linux系统作业控制 调整进程优先级 网络客户端工具 bash之while循环 20.1.进程类型 守护进程 daemon,在系统引导过程中启动的进程 ...
- 如何灵活运用Linux 进程资源监控和进程限制
导读 每个 Linux 系统管理员都应该知道如何验证硬件.资源和主要进程的完整性和可用性.另外,基于每个用户设置资源限制也是其中一项必备技能. 在这篇文章中,我们会介绍一些能够确保系统硬件和软件正常工 ...
- TODO:Golang Linux进程退出说明
TODO:Golang Linux进程退出说明 Golang使用os.Exit(code)进程退出导致当前程序退出并返回给定的状态代码.传统上,code代码为零表示成功退出,非零错误退出. sysca ...
- Linux进程管理子系统分析【转】
本文转载自:http://blog.csdn.net/coding__madman/article/details/51298732 Linux进程管理: 进程与程序: 程序:存放在磁盘上的一系列代码 ...
- 12个Linux进程管理命令介绍(转)
12个Linux进程管理命令介绍 [日期:2015-06-02] 来源:Linux中国 作者:Linux [字体:大 中 小] 执行中的程序在称作进程.当程序以可执行文件存放在存储中,并且运行的 ...
- linux 进程管理相关内容
简介 当我们运行程序时,Linux会为程序创建一个特殊的环境,该环境包含程序运行需要的所有资源,以保证程序能够独立运行,不受其他程序的干扰.这个特殊的环境就称为进程. 每个 Linux 命令都与系统中 ...
- Linux - 进程查看与管理
标签(空格分隔): Linux 进程的静态查看 查看系统所有进程 ps -ef -- 输出来好乱,看不懂..: ps aux -- a表示所有与终端相关的进程,u表示所有以用户组织的进程状态的信息,x ...
- Linux进程关系
Linux进程关系 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Linux的进程相互之间有一定的关系.比如说,在Linux ...
- Linux进程基础
Linux进程基础 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 计算机实际上可以做的事情实质上非常简单,比如计算两个数的和 ...
- linux进程编程:子进程创建及执行函数简介
linux进程编程:子进程创建及执行函数简介 子进程创建及执行函数有三个: (1)fork();(2)exec();(3)system(); 下面分别做详细介绍.(1)fork() 函数定 ...
随机推荐
- 三、java连接mongo数据库
系列导航 一.linux单机版mongo安装(带密码验证) 二.mongo集群搭建 三.java连接mongo数据库 四.java对mongo数据库增删改查操作 五.mongo备份篇 mongoexp ...
- 【调试】kprobes(一)基本概念
简介 开发人员在内核或者模块的调试过程中,往往会需要要知道其中的一些函数有无被调用.何时被调用.执行是否正确以及函数的入参和返回值是什么等等. 比较简单的做法是在内核代码对应的函数中添加日志打印信息, ...
- Jenkins安装完成后,一直停留在页面Please wait while Jenkins is getting ready to work...的解决方法
一.打开jenkins一直显示如下页面:
- Feign源码解析6:如何集成discoveryClient获取服务列表
背景 我们上一篇介绍了feign调用的整体流程,在@FeignClient没有写死url的情况下,就会生成一个支持客户端负载均衡的LoadBalancerClient.这个LoadBalancerCl ...
- .net core 3.0 获取 IServiceProvider 实例
.net core 3.0后,获取IServiceProvider需要绕点弯路 首先,新建一个类: public class MyServiceProviderFactory : IServicePr ...
- NodeJS安装指南(Mac)
nvm,node,npm之间的区别 nvm:nodejs 版本管理工具. 也就是说:一个 nvm 可以管理很多 node 版本和 npm 版本. nodejs:在项目开发时的所需要的代码库 npm:n ...
- [转帖]OutOfMemory JVM参数一览
https://www.cnblogs.com/kuroro/p/11707951.html JVM提供了有用的参数来处理OutOfMemoryError.在本文中,我们要强调那些JVM参数.在对Ou ...
- Chrony 的学习与使用
Chrony 的学习与使用 背景 之前捯饬 ntp 发现很麻烦, 经常容易弄错了. 昨天处理文件精确时间时 想到了时间同步. 发现只有自己总结的ntpdate 但是还没有 chronyd相关的总结 本 ...
- [转帖]LOAD DATA INFILE 导入数据
https://www.jianshu.com/p/bcafd8f3ad8e LOAD DATA INFILE语句用于高速地从一个文本文件中读取行,并写入一个表中.文件名称必须为一个文字字符串.LOA ...
- 【转帖】eBay 云计算“网”事:网络超时篇
https://www.infoq.cn/article/JmCbkA0XX9NqrcX6loIo eBay技术荟 2020-06-19 本文字数:5508 字 阅读完需:约 18 分钟 导读 eBa ...