已停止运行的进程占用CPU资源

4

ps aux 的输出包含以下内容:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
ubuntu    1496  9.1  0.0      0     0 pts/0    Z+   19:47   0:30 [python] <defunct>
ubuntu    1501 14.6  0.0      0     0 pts/0    Z+   19:47   0:48 [python] <defunct>
ubuntu    1502 14.8  0.0      0     0 pts/0    Z+   19:47   0:48 [python] <defunct>
ubuntu    1503 15.1  0.0      0     0 pts/0    Z+   19:47   0:49 [python] <defunct>
ubuntu    1504 15.4  0.0      0     0 pts/0    Z+   19:47   0:50 [python] <defunct>
ubuntu    1505 15.8  0.0      0     0 pts/0    Z+   19:47   0:52 [python] <defunct>
ubuntu    1506 16.0  0.0      0     0 pts/0    Z+   19:47   0:53 [python] <defunct>
ubuntu    1507 14.1  0.0      0     0 pts/0    Z+   19:47   0:46 [python] <defunct>
ubuntu    1508 14.3  0.0      0     0 pts/0    Z+   19:47   0:47 [python] <defunct>
ubuntu    1509 14.4  0.0      0     0 pts/0    Z+   19:47   0:47 [python] <defunct>
ubuntu    1510 14.6  0.0      0     0 pts/0    Z+   19:47   0:48 [python] <defunct>
ubuntu    1511 14.9  0.0      0     0 pts/0    Z+   19:47   0:49 [python] <defunct>
ubuntu    1512 10.7  0.0      0     0 pts/0    Z+   19:47   0:35 [python] <defunct>
ubuntu    1513 71.3  0.0      0     0 pts/0    Z+   19:47   3:55 [python] <defunct>

这些是通过多进程生成的一堆进程,它们已经完成并等待父进程加入。为什么它们会占用CPU?

如果这只是ps的一个副作用,我如何获得准确的CPU使用量视图?


请看我的回答。注意“显示累积CPU利用率”和“占用CPU”的区别。运行几次ps命令,查看是否增加了TIME。如果有增加,您可能需要深入了解。 - Mike S
3个回答

4
一个僵尸进程(即“defunct”进程)不会消耗CPU资源:内核仅仅为了让父进程可以检索有关它的信息(例如返回状态、资源使用情况等)而保留它。 ps 命令所指示的 CPU 使用情况对应于进程运行时的 CPU 使用情况,也就是说,在进程终止并变成僵尸进程之前。

2
我目前正在查看一个已停止运行的Linux进程,它的CPU使用时间不断增加。这可能是由于驱动程序或内核错误引起的。 - Mattias Wadman

1
那些进程是僵尸进程,状态栏中的Z表示 - 它们只有在其父进程终止后才会被清除。我不太了解Python,但可能你在Python解释器中调用了fork或类似的函数来生成它们。杀掉解释器,僵尸进程将被回收(清理)。
如果您想获取最新的CPU信息,请尝试“top”命令。
此外,我更喜欢“ps -ef”输出,而不是“ps aux”,因为aux总让我感觉像是一个非标准的黑客(因此缺少'-'以分隔命令和参数),它也无法在许多其他Unix系统(如HPUX,AIX等)上工作。
“ps -ef”显示ppid(父进程ID),可帮助您跟踪此类问题。

ps的优点在于可以将其输出导入到“grep”中以查找各种模式(如“python”)。那么如何使用top实现这一点呢? - Zags
是的,但正如上面的评论所说,PS的输出与CPU使用情况不准确(即它显示进程运行时的使用情况 - 直到它变成僵尸进程的那一点 - 而不是当前使用情况)。Top是获取CPU使用情况更“准确”的简单方法。您可以查询各种/proc条目(无论如何,top都会在内部执行此操作)。类似于“cat /proc/stat”(或/proc/<pid>/stat用于单个进程)。这可以被管道传输到grep,但您可能需要进行一些数学计算来计算“%” - 目前不在Linux盒子上进行测试。 - Matt
@Matt 我在回答中提到的 ps 命令关于 CPU 使用率似乎是准确的。它的确在不断增加 CPU 使用率。我相信它只是从 /proc 中获取数据,就像 top 命令一样。你可以尝试运行 strace -f -e open,openat ps - Mike S

1
有趣的是,也许令人困惑的是,我目前系统上有一个僵尸进程在积累 CPU 时间。因此问题是,为什么?通常认为,ps 的任何输出显示僵尸进程意味着唯一使用的是进程表项;来自维基百科的定义是:"... 僵尸进程或死进程是已完成执行(通过 exit 系统调用)但仍在进程表中具有条目的进程:它是处于“终止状态”的进程。" ,而来自unix.stackexchange的定义是:https://unix.stackexchange.com/questions/11172/how-can-i-kill-a-defunct-process-whose-parent-is-init“僵尸进程几乎不会占用任何资源,因此让它们悬挂没有性能成本。”
因此我有一个僵尸进程:
# ps -e -o pid,ppid,stat,comm| grep Z
 7296     1 Zl   myproc <defunct>

似乎正在使用 CPU 时间:

# ps -e -o pid,ppid,bsdtime,stat,comm| grep Z; sleep 10; ps -e -o pid,ppid,bsdtime,stat,comm | grep Z
 7296     1  56:00 Zl   myproc <defunct>
 7296     1  56:04 Zl   myproc <defunct>

那么,一个僵尸进程如何累积CPU时间呢?

我改变了我的搜索:

# ps -eT -o pid,lwp,ppid,bsdtime,stat,comm| grep 7296 
 7296  7296     1   1:29 Zl   myproc <defunct>
 7296  8009     1  56:11 Dl   myproc

我看到有一个正在运行并使用系统I/O的线程。实际上,如果我这样做,我可以看到字段15(stime)在改变:

# watch -d -n 1 cat /proc/8009/stat
Every 1.0s: cat /proc/8009/stat                  Fri Jun  4 11:19:55 2021

8009 (myproc) D 1 7295 7295 0 -1 516 18156428 12281 37 0 11609 344755

(截取至第15个字段)

我试图用TERM杀死进程8009...但是没有成功。使用KILL命令也无济于事。

听起来像是内核bug。我尝试了strace,这很愚蠢,因为现在我的strace无法退出。

这是在RHEL 7.7上,内核版本为3.10.0-1062。虽然已经有点老了,但足够年轻(在我看来)以致于一个僵尸进程可能会由于某个bug而积累系统资源。

顺便说一句,根据iotop的显示,我们的I/O峰值达到了4 GBps,这很多。我认为这个问题肯定对我们的系统产生了影响,我想重新启动。

/proc/8009目录的ls输出如下:

# ls -l /proc/8009
ls: cannot read symbolic link /proc/8009/cwd: No such file or directory
ls: cannot read symbolic link /proc/8009/root: No such file or directory
ls: cannot read symbolic link /proc/8009/exe: No such file or directory

(以下是正常的/proc/pid输出...但我已经省略了一些)

/proc/8009/fd为空。所以即使我有大量的I/O操作,它也不会写入任何文件。我没有看到文件系统空间被使用,如df -h输出所示。

最后:尝试重新启动正在变得不可能。 shutdown -r now无法工作。有几个systemd进程被卡在I/O等待中:

  PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
22725 root       20   0  129M  2512  1548 R  0.0  0.0  0:00.19 htop
22227 root       20   0  195M  4776  2652 D  0.0  0.0  0:00.00 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
    1 root       20   0  195M  4776  2652 D  0.0  0.0  0:58.41 /usr/lib/systemd/systemd --switched-root --system --deserialize 22

这是关机输出。我会说此时 init 相当困惑:

# shutdown -r now
Failed to open /dev/initctl: No such device or address
Failed to talk to init daemon.

重启说的是同样的事情。我得拔掉这台机器的插头。

...更新:就在我登录控制台的时候,系统重新启动了!可能花了约10分钟的时间。所以我不知道systemd在做什么,但它正在做某些事情。

...另一个更新:今天有3台机器发生了这种情况,它们都具有相同的特征:相同的二进制文件,某种行为(没有打开的文件描述符,但进行了I/O操作,两个线程,子线程累积了CPU时间)。正如@Stephane Chazelas所提到的,我执行了堆栈跟踪。以下是典型输出;我对内核不太熟悉,但也许对未来的某个闯入者有兴趣...请注意242603是父线程,242919是繁忙的子线程:

# grep -H . /proc/242919/task/*/stack
/proc/242919/task/242603/stack:[<ffffffff898a131e>] do_exit+0x6ce/0xa50
/proc/242919/task/242603/stack:[<ffffffff898a171f>] do_group_exit+0x3f/0xa0
/proc/242919/task/242603/stack:[<ffffffff898b252e>] get_signal_to_deliver+0x1ce/0x5e0
/proc/242919/task/242603/stack:[<ffffffff8982c527>] do_signal+0x57/0x6f0
/proc/242919/task/242603/stack:[<ffffffff8982cc32>] do_notify_resume+0x72/0xc0
/proc/242919/task/242603/stack:[<ffffffff89f8c23b>] int_signal+0x12/0x17
/proc/242919/task/242603/stack:[<ffffffffffffffff>] 0xffffffffffffffff
/proc/242919/task/242919/stack:[<ffffffffc09cbb03>] ext4_mb_new_blocks+0x653/0xa20 [ext4]
/proc/242919/task/242919/stack:[<ffffffffc09c0a36>] ext4_ext_map_blocks+0x4a6/0xf60 [ext4]
/proc/242919/task/242919/stack:[<ffffffffc098fcf5>] ext4_map_blocks+0x155/0x6e0 [ext4]
/proc/242919/task/242919/stack:[<ffffffffc0993cfa>] ext4_writepages+0x6da/0xcf0 [ext4]
/proc/242919/task/242919/stack:[<ffffffff899c8d31>] do_writepages+0x21/0x50
/proc/242919/task/242919/stack:[<ffffffff899bd4b5>] __filemap_fdatawrite_range+0x65/0x80
/proc/242919/task/242919/stack:[<ffffffff899bd59c>] filemap_flush+0x1c/0x20
/proc/242919/task/242919/stack:[<ffffffffc099116c>] ext4_alloc_da_blocks+0x2c/0x70 [ext4]
/proc/242919/task/242919/stack:[<ffffffffc098a4d9>] ext4_release_file+0x79/0xc0 [ext4]
/proc/242919/task/242919/stack:[<ffffffff89a4a9cc>] __fput+0xec/0x260
/proc/242919/task/242919/stack:[<ffffffff89a4ac2e>] ____fput+0xe/0x10
/proc/242919/task/242919/stack:[<ffffffff898c1c0b>] task_work_run+0xbb/0xe0
/proc/242919/task/242919/stack:[<ffffffff898a0f24>] do_exit+0x2d4/0xa50
/proc/242919/task/242919/stack:[<ffffffff898a171f>] do_group_exit+0x3f/0xa0
/proc/242919/task/242919/stack:[<ffffffff898b252e>] get_signal_to_deliver+0x1ce/0x5e0
/proc/242919/task/242919/stack:[<ffffffff8982c527>] do_signal+0x57/0x6f0
/proc/242919/task/242919/stack:[<ffffffff8982cc32>] do_notify_resume+0x72/0xc0
/proc/242919/task/242919/stack:[<ffffffff89f8256c>] retint_signal+0x48/0x8c
/proc/242919/task/242919/stack:[<ffffffffffffffff>] 0xffffffffffffffff

2
sudo grep -H . /proc/7296/task/*/stack 可能会给你一些线索。 - Stephane Chazelas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接