“jobs”显示不再存在的进程正在运行

“jobs”显示不再存在的进程正在运行

我正在后台从 bash 运行一个长期运行的管道:

find / -size +500M -name '*.txt' -mtime +90 |
   xargs -n1 gzip -v9 &

由于存在多个大+旧文件,管道的第二阶段需要很长时间(数小时)才能完成。

相反,管道的第一部分立即完成,并且由于管道未满,并且它已完成,因此find成功退出。

bash进程似乎wait适合子进程。

我可以这么说,因为没有find(pid 20851)根据以下任意一个运行:

     ps alx | grep 20851
     pgrep -l find

没有僵尸进程,也没有20851在系统上的任何位置找到任何具有进程 ID 的进程。

bash 内置jobs正确地将作业显示为单行,没有任何进程 ID:

[1]+  Running find / -size +500M -name '*.txt' -mtime +90 | xargs -n1 gzip -v9 &

OTOH:我偶然发现了一个单独的作业控制命令 ( /bin/jobs),它显示:

[1]+ 20851 Running         find / -size +500M -name '*.txt' -mtime +90
     20852 Running         | xargs -n1 gzip -v9 &

并且(错误地)将已经退出的20851查找进程显示为“正在运行”。

这是在 CentOS(编辑:更准确地说:Amazon Linux 2 AMI)Linux 上。事实证明这/bin/jobs是一个两行/bin/sh脚本:

#!/bin/sh
builtin jobs "$@"

这让我感到惊讶。从另一个程序 ( ) 启动的单独进程如何sh知道由另一个程序 ( ) 管理的进程bash在该进程已经完成并退出并且不是僵尸进程之后的详细信息?

pid进一步:当系统上的其他方法(pspgrep)不能时,它如何知道有关已退出进程的详细信息(包括)?

编辑:

(1) 正如比利叔叔在评论中指出的那样,在这个系统上/bin/sh/bin/bash是相同的(/bin/sh是到 的符号链接/bin/bash),但是/bin/jobs是一个带有 shebang 行的脚本,因此它在单独的进程中运行。

(2) 另外,感谢比利叔叔:一种更简单的复制方法。/bin/jobs这是一个转移注意力的话题。我错误地认为它是产生输出的那个。jobs当使用以下命令调用时,令人惊讶的输出显然来自 bash 内置函数-l

$ sleep 1 | sleep 3600 &
[1] 13616
$ jobs -l
[1]+ 13615 Running                 sleep 1
     13616 Running                 | sleep 3600 &
$ ls /proc/13615
ls: cannot access /proc/13615: No such file or directory

因此进程 13615 不存在,但由 bash 内置作业控制显示为“正在运行”,这看起来像是jobs -l.

它的存在/bin/jobs让我困惑,认为它一定是罪魁祸首(它不是),看起来令人困惑和可疑。我认为它应该从系统中删除,因为它是无用的(sh在单独进程中运行的脚本,无论如何都无法显示调用者的作业)。

答案1

FWIW,我可以通过以下方式重现您的案例:

rhel8$ /bin/jobs(){ jobs -l; }
rhel8$ sleep 1 | sleep 3600 &
[1] 2611
rhel8$ sleep 2
rhel8$ jobs
[1]+  Running                 sleep 1 | sleep 3600 &
rhel8$ /bin/jobs
[1]+  2610 Running                 sleep 1
      2611 Running                 | sleep 3600 &
rhel8$ pgrep 2610
    <nothing!>
rhel8$ ls /proc/2610
ls: cannot access '/proc/2610': No such file or directory
rhel8$ /bin/jobs
[1]+  2610 Running                 sleep 1
      2611 Running                 | sleep 3600 &
rhel8$ cat /bin/jobs
#!/bin/sh
builtin jobs "$@"

或者使用(甚至比以前更差):

rhel8$ unset -f /bin/jobs
rhel8$ export JOBS=$(jobs -l)
rhel8$ builtin(){ echo "$JOBS"; }
rhel8$ export -f builtin
rhel8$ /bin/jobs
[1]+  2610 Running                 sleep 1
      2611 Running                 | sleep 3600 &
rhel8$ type /bin/jobs
/bin/jobs is /bin/jobs

注意:正如已经演示的,jobs -l在 bash 中显示的是过时的信息,已经退出的管道进程仍显示为Running。恕我直言,这是一个错误 - 其他 shell(如 zsh、ksh 或 yash)正确地将它们显示为Done.

相关内容