如何获取最后一个后台应用程序的pid

如何获取最后一个后台应用程序的pid

从这个问题得出:如何获取shell脚本中最后执行的命令的pid?

那里的线程很好而且很清晰,但是如果命令实际上不需要末尾的“&”来后台运行怎么办?那么是否可以获得它的PID?

注意:我不想使用psand grep;我问这个问题是因为理论上,在运行命令后,该进程的 PID 应该是可以找到的。

一个后台应用程序的示例 - 这不是我需要的,我需要一个通用的解决方案,用于任何自行进入后台的东西(有些强制它根本没有“前台”选项) - 但对于测试一个解决方案,这将工作得很好:

我的应用程序脚本:

#!/bin/bash

(
sleep 10
echo test
sleep 5
) &

正如您所看到的 '&' 位于脚本内部而不是外部,因此启动它不需要 &:

./my-app ; echo $! | od -c ; jobs | od -c
0000000  \n
0000001
0000000

$!为 null,并且jobs不返回任何内容。 10 秒后,仍然会出现“测试”消息;还:

ps x | grep my-app | grep -v grep        
14986 pts/5    S      0:00 /bin/bash ./my-app

更新,在一些评论说 bash 工作不适用于这里之后;我希望找到一个解决方案,而且我的希望不仅限于 bash :)

答案1

背景本身,该应用程序分叉一个子进程并退出父进程。 shell 知道父进程,因为它是在执行命令本身之前分叉出来的进程,但它无法看到该进程可能生成的子进程或孙进程。

这种后台命令的一个更简单的示例是:

$ sh -c 'ps -j; sleep 10 &'
  PID  PGID   SID TTY          TIME CMD
 6562  6562 14469 pts/13   00:00:00 sh
 6563  6562 14469 pts/13   00:00:00 ps
14469 14469 14469 pts/13   00:00:00 zsh

zsh了解该sh流程及其流程组。

然而,它看到的只是 6562 进程退出。它无法知道 6562 进程是否已生成 6563 进程。

$ ps -j
  PID  PGID   SID TTY          TIME CMD
 6564  6562 14469 pts/13   00:00:00 sleep
 6565  6565 14469 pts/13   00:00:00 ps
14469 14469 14469 pts/13   00:00:00 zsh

但是,您可以看到正在运行的进程sleep也在该 6562 进程组中(不过,没有什么可以阻止命令启动新进程组,甚至会话(就像守护进程通常所做的那样))。

这些进程组仅在 shell 交互时才会创建。

您可以做的另一件事是:

cmd | cat &
wait

如果 cmd 生成的进程不关闭其标准输出,则cat在它们全部死亡之前不会死亡。

答案2

这一般是不可能的。在您的具体情况下,这可能是可能的。

运行后台命令后,即可发现进程ID来自父母。如果您运行前台命令(主程序的子程序),并且该命令又运行后台命令(主程序的孙程序),则主程序对其孙程序没有直接可见性:它只知道它是运行一个子进程,该进程现已退出。

内核跟踪子进程→父进程的关系。可以运行ps -o ppid= -p $pid查看ID为 的进程的父进程的进程ID $pid。内核不跟踪进程的祖父母。此外,如果父进程死亡,该进程将被 init(进程号 1)采用,因此从那时起其父进程 ID 将为 1。

还有其他几个继承的进程属性,您可以尝试跟踪它们。然而,孙子可以脱离这些属性中的任何一个。如果孙子打算作为守护进程运行,它可能会尝试尽可能地隔离自己(无论是与中间子还是孙子本身),这样它就不会与您的交互式会话绑定,也不会冒着被卷入本次会议发生的任何事情的风险。

您可以在与当前进程相同的进程组中查找进程 ( ps -o pgid=))。这将捕获在同一进程组中启动的任何其他进程,但相反,如果它由于调用子进程setpgidsetpgrp孙进程而在自己的进程组中运行,则会错过孙进程(守护进程会这样做)。

您可以在与当前进程相同的会话 ID 中查找进程 ( ps -o sid=))。这将捕获在同一会话 ID 中启动的任何其他进程,但相反,如果它在自己的会话中运行,则由于调用setsid子进程或孙进程(守护进程会这样做)而错过孙进程。

您可以打开一个临时文件并查找打开该文件的进程(`fuser "$tmpfile")。这更可靠,因为它只会捕获从打开此文件的主进程部分启动的进程,它不会捕获主进程的其他组件可能已执行的任何操作。然而,与其他解决方案一样,如果它或中间子进程关闭了它不使用的文件描述符,它将错过孙子进程,而守护进程会这样做。

大多数守护进程都有一个命令行选项可以留在前台。然后您可以运行daemon --foreground & daemon_pid=$!,您需要采取预防措施以避免守护进程在会话退出时被捕获(nohup daemon --foreground </dev/null >daemon.log 2>&1 &这是一个好的开始)。

答案3

您可以通过使用strace脚本来获取一些信息,但它确实会保持 strace 运行,直到子命令完成。例如,

$ strace -b execve -e trace=none -e signal=none -f my-app
Process 21697 attached             <-- is the () &
[pid 21696] +++ exited with 0 +++  <-- is my-app ending
Process 21698 attached             <-- is the sleep 10
Process 21698 detached
test
Process 21702 attached             <-- is the sleep 5
Process 21702 detached
+++ exited with 0 +++

所以我们有子 shell 的 pid,并且命令在那里运行,假设 echo是一个内置 shell,因此不会跟踪它。 -b execve是在执行时分离,-f跟踪子级,-e通过不跟踪任何内容来减少输出。

相关内容