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

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

man ps 中描述 D 状态是 Uninterruptible Sleep。

Linux 进程有两种睡眠状态:

  1. Interruptible Sleep,可中断睡眠,在 ps 命令中显示 S。处在这种睡眠状态的进程是可以通过给它发送信号来唤醒的。
  2. Uninterruptible Sleep,不可中断睡眠,在 ps 命令中显示 D。处在这种睡眠状态的进程无法立即处理任何发送给它的信号,这也是无法用 kill 杀掉它的原因。

在 Stack Overflow 有一个解答:

kill -9 只是给进程发送了一个 SIGKILL 信号,当一个进程处于特殊状态时(信号处理,或者系统调用中)会无法处理任何信号,包括 SIGKILL 也不能被正确处理,导致进程不能被立即杀掉,也就是我们常说的 D 状态(不可中断的睡眠状态)。那些常用的调试工具 (比如 stracepstack 等)一般也是利用某个特殊的信号来实现的,在这种状态下也是无法使用。

可见 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() 这种简单的系统调用并没有 屏蔽 SIGKILLSIGQUITSIGABRT 等信号,还可以对它做些常规的处理。

我们再来模拟一个更复杂的 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 进程卡住了怎么办?相关推荐

  1. Linux正在更新缓存卡住,Linux 进程卡住了怎么办?

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

  2. linux进程卡住_鸿蒙系统,Linux? Android?

    昨日(2020.9.10),在华为开发者大会2020上,华为宣布推出鸿蒙OS2.0版本(HarmonyOS 2.0),并且将其开源,代码捐赠给开放原子开源基金会. 笔者带大家进入开源社区,看看鸿蒙的神 ...

  3. (大集合)Linux进程和线程的基本编程、通讯和例程【杂记】

    Linux 进程 和 线程 的基本 编程.通讯 和 例程 注:[杂记]系列为日常网搜资料的堆砌而积累成之.如有错误恭谢指出.标识为"原创"其实不完全是,只是多引用再整理和加上自己的 ...

  4. 【Linux进程概念】冯 诺依曼体系结构 操作系统 进程 fork 进程状态 优先级

    文章目录 [写在前面] 一.冯 ? 诺依曼体系结构 ?? 体系结构 ?? 数据流向 ?? 实例 二.操作系统 (Operator System) ?? 概念 ?? 计算机体系及操作系统定位 ?? 管理 ...

  5. Linux——进程概念(进程状态)

    目录 进程状态 三态模型 五态模型 七态模型 Example eg1:阻塞态:等待某种资源的过程 eg2:挂起态 Linux内核源代码 Linux进程状态查看 Linux运行状态 R运行状态(runn ...

  6. Linux - 第3节 - Linux进程概念

    目录 1.冯诺依曼体系结构 2.操作系统(Operator System) 2.1.如何理解管理 2.2.操作系统 3.进程 3.1.基本概念 3.2.进程相关操作 4.进程状态 4.1.进程状态概述 ...

  7. Linux 进程及进程之间的通信机制——管道

    参考: LInux C编程从初学到精通 电子工业出版社 Linux 进程 Linux 进程简介 Linux是一个多用户多任务的操作系统,多用户是指多个用户可以在同一时间使用同一台计算机系统:多用户是指 ...

  8. Linux进程与线程的区别 详细总结(面试经验总结)

    首先,简要了解一下进程和线程.对于操作系统而言,进程是核心之核心,整个现代操作系统的根本,就是以进程为单位在执行任务.系统的管理架构也是基于进程层面的.在按下电源键之后,计算机就开始了复杂的启动过程, ...

  9. Linux进程编程基础介绍

    Linux系统是一个多进程的系统,它的进程之间具有并行性.互不干扰等特点.也就是说,每个进程都是一个独立的运行单位,拥有各自的权利和责任.其中,各个进程都运行在独立的虚拟地址空间,因此,即使一个进程发 ...

最新文章

  1. sql 分号变成多行_SQL(2)
  2. 有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和
  3. 深入剖析ThreadLocal
  4. python:django应用问题汇总
  5. 终于!《iOS 全埋点解决方案》正式出版
  6. 网站数据分析四:网站用户分析
  7. Apache Camel 3 –骆驼核心vs骆驼核心引擎(较小的核心)
  8. RHEL6.4 Keepalive+LVS(DR)部署文档
  9. 连载13:软件体系设计新方向:数学抽象、设计模式、系统架构与方案设计(简化版)(袁晓河著)...
  10. 在集群环境中使用 EhCache 缓存系统|RMI 集群模式
  11. python的if循环语句_第二个是Python的循环语句,基础,使用,if,条件,判断,while,for
  12. Dijkstra算法的C语言程序
  13. vs2010运行c++程序时,控制台一闪而过的解决方案
  14. ISO 12233和SFR
  15. 报道|香港科大校友“盐马行”活动成功举办
  16. 计算机基础——计算机基础知识
  17. 2019考研 | 天津大学计算机专硕初试394分考研经历与经验总结(初试)
  18. 狼人杀攻略:你当我好骗吗,我们相信谁!
  19. 领导逼迫员工离职的10大套路
  20. 【以太坊】ubuntu安装以太坊ethereum的测试网络ropsten-net以及雷电网络raiden-network环境

热门文章

  1. python名单查询_Python爬虫实现全国失信被执行人名单查询功能示例
  2. 超级天碟《竹宴》(320K)
  3. MySql中时间戳的转换
  4. vue 上传附件设置必填项
  5. 在线文件预览工具kkFileView初体验
  6. 第6次博文;使用python 自带得IDLE,写出12星座的符号
  7. 深度学习主机环境配置2---显卡配置:ubuntu-16.04.2-desktop-amd64.iso + GTX1070TI
  8. PostgreSQL 实时高效搜索 - 全文检索、模糊查询、正则查询、相似查询、ADHOC查询...
  9. Java 操作Excel POI
  10. C# 超简单的左/右移运算符理解