在我们使用 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 目录,参见 Wikipedia 的解释), 通过它就可以看到任何一个进程的当前内核调用栈。下面我们用访问 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

[] request_wait_answer+0x197/0x280

[] __fuse_request_send+0x67/0x90

[] fuse_request_send+0x27/0x30

[] fuse_simple_request+0xcc/0x1a0

[] fuse_do_getattr+0x120/0x330

[] fuse_update_attributes+0x68/0x70

[] fuse_getattr+0x3d/0x50

[] vfs_getattr_nosec+0x2f/0x40

[] vfs_getattr+0x26/0x30

[] vfs_fstatat+0x78/0xc0

[] SYSC_newstat+0x2e/0x60

[] SyS_newstat+0xe/0x10

[] entry_SYSCALL_64_fastpath+0x22/0xcb

[] 0xffffffffffffffff

这时候按 Ctrl+C 也不能退出。

root@localhost:~# ls /jfs

^C

^C^C^C^C^C

但是用

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

[] request_wait_answer+0x197/0x280

[] __fuse_request_send+0x67/0x90

[] fuse_request_send+0x27/0x30

[] fuse_flush+0x17f/0x200

[] filp_close+0x32/0x80

[] __close_fd+0xa3/0xd0

[] SyS_close+0x23/0x50

[] entry_SYSCALL_64_fastpath+0x22/0xcb

[] 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

[] request_wait_answer+0x12d/0x280

[] __fuse_request_send+0x67/0x90

[] fuse_request_send+0x27/0x30

[] fuse_flush+0x17f/0x200

[] filp_close+0x32/0x80

[] __close_fd+0xa3/0xd0

[] SyS_close+0x23/0x50

[] entry_SYSCALL_64_fastpath+0x22/0xcb

[] 0xffffffffffffffff

内核调用栈显示它卡在 FUSE 的 flush 调用上,这个时候只要恢复 JuiceFS 客户端进程,就可以立即中断 cp 让它退出。

像 close 这种涉及到数据安全性的操作,不是 restartable, 也就不能被 SIGKILL 等随意中断,比如要 FUSE 的实现端响应中断操作才能中断。

因此,只要 JuiceFS 的客户端进程能够健康的响应中断,就不用担心访问 JuiceFS 的应用卡死。或者杀掉 JuiceFS 客户端进程也可以结束当前的挂载点,中断所有在访问当前挂载点的应用。

Linux正在更新缓存卡住,Linux 进程卡住了怎么办?相关推荐

  1. linux关闭磁盘缓存,在linux上禁用apache2的所有磁盘缓存

    出于基准测试目的,我想强制Apache 2从磁盘加载每个请求的文件,而不是从内存中的缓存加载它.从我所读到的做同步后跟 echo 3 > /proc/sys/vm/drop_caches 让我放 ...

  2. linux中清理缓存命令,linux清理缓存的命令

    查看缓存的命令 free -m 清理缓存的命令 echo 1 > /proc/sys/vm/drop_caches echo 2 > /proc/sys/vm/drop_caches ec ...

  3. linux系统更新字体,更换Linux下字体

    我的Linux系统是Ubuntu 14.04,更换完字体之后,Web页面效果是这样的: Shell显示效果是这样的: 我采用的字体方案是,Monaco雅黑混合字体 下面是具体更换步骤: 一.下载安装字 ...

  4. 红帽linux怎么更新源,redhat Linux配置yum更新源

    redhat Linux配置yum更新源 (2011-11-19 20:23:04) 标签: 杂谈 YUM是RedHat Linux在线安装更新及软件的工具,但是这是RHEL5的收费功能,如果没有购买 ...

  5. linux touch更新文件时间,Linux文件时间的查看和修改touch

    1. Linux文件的时间 Linux下文件时间主要有下面三种: 1.1 modification time(mtime) 文件修改时间,即文件内容的修改时,更新这个时间,不包括文件权限和属性的修改. ...

  6. linux删除更新的系统内核,Linux系统如何删除旧内核

    Linux系统本身就是一个开源的系统,所以在使用过程中可以进行修改,只有它的内核是不变的,只有在一段时间之内,才可以对Linux内核进行升级.升级了以后就会留下一些旧的无用的内核,中本文就以Ubunt ...

  7. linux touch更新文件时间,linux 使用 touch 修改文件的修改时间

    rsync有时候因为服务器时间错了,需要更改文件的修改时间时间,可以使用 touch命令来修改文件的修改时间: 1 touch -c -m -t 201101110000 teadme.txt 修改r ...

  8. Linux 进程卡住了怎么办?

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

  9. linux篇【4】:进程与操作系统

    目录 一.冯诺依曼体系结构 注意: 1.为什么要有内存? a.技术角度 b.成本角度 2.存储器/内存 的意义 二.操作系统 1.管理的理解一:管理的本质-对数据做管理. 2.管理的理解二:如何管好大 ...

最新文章

  1. python list 和矩阵的切片
  2. SpringBoot中使用hikariCP
  3. Docker之Dockerfile 指令详解
  4. 嵌入式研发人员的核心竞争力浅谈 .
  5. echarts使用记录(二)legend翻页,事件,数据集,设置y轴最大/小值,让series图形从右侧出往左移动...
  6. ANDROID模拟器不能启动
  7. 谈谈R中的乱码(二)
  8. PreferenceScreen使用
  9. cai鸟驿站管理系统
  10. React-native学习-59:使用react-native-vector-icons图标库
  11. 数据湖 数据孤岛 数据沼泽
  12. Dilated/Atrous Convolution
  13. 天平应什么放置_天平的使用规程
  14. 记一次配置rewrite和return的经历
  15. 创客学院9天C语言四
  16. Vue学习之前端路由
  17. js简繁转换,两种实现方式,妥妥的~
  18. Qemu-spice云桌面创建
  19. 将灰度图像转换成彩色图像
  20. js字符串转日期时间,并比较大小

热门文章

  1. linux 免sudo,linux 创建用户,免密sudo,ssh免密登录
  2. c语言输入字符串做四则运算,C++字符串四则运算实例讲解
  3. c语言解析字符串报文,传递字符串数组报文和解析
  4. python信息传送管道_python – 获取返回管道输入的命令
  5. android dialog 隐藏状态栏_Flutter-最近搞了个项目-启动页Splash,Navigator.pop无法关闭Dialog...
  6. gdbstub中的基本命令_程序员应该知道的 20 个 Linux 系统管理命令
  7. Spring Cloud服务网关 Zuul
  8. 计算机科学与技术专业分多少学科,计算机科学与技术专业,电子科学与技术有什么分别?...
  9. java取整数位_java获取整数的各位数值
  10. 离线在线计算机系统,离线计算机系统