模拟一个不可kill的D状态进程

我们在排查线上故障,有时需要编写脚本或者配置监控 找出 D 进程 ,此时我们可能需要有一个模拟环境来调试程序。那么,怎么在Linux环境下模拟出一个D进程呢?

由于大多数 D 进程都和存储有关,所以模拟出存储故障就能 制造D 进程:

  • 在主机(VM)上挂载NFS,然后 故意 将NFS服务端关闭

  • 在主机(VM)上读写虚拟磁盘,然后将虚拟磁盘移除

但是上述操作磁盘的方法都比较 沉重 ,容易引发不好处理的故障

Simulate an unkillable process in D state 有人提供了一个非常巧妙的方法:

使用 vfork 系统调用: vfork 类似 fork ,但是因为预期 exec 只会丢弃复制的数据,地址空间不会从父进程复制到子进程。这样当 vfork 时,父进程 uninterruptibly 地等待子进程的 execexit ,就能模拟出 D 状态。

  • uninterruptible.c :

模拟D进程的 uninterruptible.c
int main() {
    vfork();
    sleep(60);
    return 0;
}

备注

这里调整源代码 sleep(60) 可以延长模拟 D 状态时间

  • 编译:

    gcc -o uninterruptible uninterruptible.c
    

此时执行进程 uninterruptible 就会 D 住直到60秒睡眠时间结束自动退出,并且这个 D 住的进程实际上是可以 kill 的(另外开一个终端窗口执行如下),不会导致线上故障:

$ ps r -A
    PID TTY      STAT   TIME COMMAND
 111138 pts/1    D+     0:00 ./uninterruptible
 111142 pts/5    R+     0:00 ps r -A
$ kill 111138
$ ps r -A
    PID TTY      STAT   TIME COMMAND
 111143 pts/5    R+     0:00 ps r -A

可以在通过以下方式将 uninterruptible 批量放到后台运行, 就能模拟出大规模的 D 进程:

for i in {0..10};do (./uninterruptible &);done

模拟出load极高的系统故障:

$ ps r -A
    PID TTY      STAT   TIME COMMAND
 111261 pts/1    D      0:00 ./uninterruptible
 111263 pts/1    D      0:00 ./uninterruptible
 111265 pts/1    D      0:00 ./uninterruptible
 111267 pts/1    D      0:00 ./uninterruptible
 111269 pts/1    D      0:00 ./uninterruptible
 111271 pts/1    D      0:00 ./uninterruptible
 111273 pts/1    D      0:00 ./uninterruptible
 111275 pts/1    D      0:00 ./uninterruptible
 111277 pts/1    D      0:00 ./uninterruptible
 111279 pts/1    D      0:00 ./uninterruptible
 111281 pts/1    D      0:00 ./uninterruptible
 111285 pts/1    R+     0:00 ps r -A
$ uptime
 17:46:25 up 17 days,  6:15,  4 users,  load average: 7.02, 2.06, 0.73

参考