![从子进程的角度来看,进程的父进程有什么意义吗?](https://linux22.com/image/161696/%E4%BB%8E%E5%AD%90%E8%BF%9B%E7%A8%8B%E7%9A%84%E8%A7%92%E5%BA%A6%E6%9D%A5%E7%9C%8B%EF%BC%8C%E8%BF%9B%E7%A8%8B%E7%9A%84%E7%88%B6%E8%BF%9B%E7%A8%8B%E6%9C%89%E4%BB%80%E4%B9%88%E6%84%8F%E4%B9%89%E5%90%97%EF%BC%9F.png)
在 POSIX 中,进程通过两个基本层次结构彼此“相关”:
父进程和子进程的层次结构。
会话和进程组的层次结构。
setpgid
用户进程通过和可以对后者进行大量控制setsid
,但对前者的控制却很少——父进程 ID 在进程生成时设置,并在父进程退出时由内核更改(通常为 PID 1) ),但除此之外它不会改变。回想起来,我一直在想,亲子关系到底有多重要?
到目前为止我的理解总结如下:
从孩子的角度来看,亲子关系显然很重要。家长进程,因为各种系统调用(例如
wait
和setpgid
)仅允许在子进程上使用。会话-组-进程的关系对于全部进程,无论是会话领导者还是会话中的其他进程,因为像
kill
对整个进程组进行操作的系统调用setpgid
只能用于加入同一会话中的组,并且SIGHUP
如果会话领导者则发送会话前台进程组中的所有进程退出。更重要的是,从父进程的角度来看,这两个层次结构显然是相关的,因为
setsid
只影响新的子进程,并且setpgid
只能用于子进程,但从子进程的角度来看,它们似乎本质上不相关(因为父进程的死亡没有任何影响)在进程的组或会话上)。
然而,显然没有任何理由让子进程关心它当前的父进程是什么。因此,我有以下问题:getppid()
从子进程的角度来看,当前值是否有任何重要性,除了可能确定其生成过程是否已退出之外?
用另一种方式提出同一问题,想象同一个程序以两种不同的方式从同一个父程序生成两次:
第一个孩子以通常的方式产生,
fork()
紧接着是exec()
。第二个子进程是间接生成的:父进程调用
fork()
,然后子进程还调用fork()
,它是孙子调用 的进程exec()
。然后,直接子级退出,因此孙级成为孤儿,并且其 PPID 被重新分配给 PID 1。
在这个假设的场景中,假设其他条件都相同,任何合理的程序是否有理由表现得有所不同?到目前为止,我的结论似乎是“否”,因为会话保持不变,进程继承的文件描述符也是如此……但我不确定。
注意:我不认为“获取父 PID 来与之通信”是该问题的有效答案,因为孤立程序通常不能依赖将其 PPID 设置为 1(某些系统将孤立进程的 PPID 设置为某些值)其他值),因此避免竞争条件的唯一方法是通过调用获取父进程 IDgetpid()
前分叉,然后在子项中使用该值。
答案1
当我看到这个问题时,我很感兴趣,因为我知道我以前见过 getppid 使用过..但我不记得在哪里。因此,我转向了一个我认为可能使用了每个 Linux 系统调用的项目,然后是一些:系统。一GitHub 搜索后来,我发现两个用途描述了一些更一般的用例(还有一些其他用途,但它们更特定于 systemd):
在SD-通知。对于某些上下文:systemd 需要知道服务何时启动,以便它可以继续启动任何依赖于该服务的服务。这通常是通过 C 程序完成的sd_通知 API,这是守护进程告诉 systemd 其状态的一种方式。
当然,如果您使用 shell 脚本作为服务……调用 C 函数并不完全可行。因此,systemd 附带了systemd通知命令,它是 sd_notify API 的一个小包装器。问题之一:systemd 还需要知道发送消息的 PID。对于 systemd-notify,这将是它自己的 PID,这将是一个短暂的进程 ID,会立即消失。没有用。
您可能已经知道我要去哪里了:systemd-notify 使用 getppid 来获取父进程的 PID,因为这通常是实际的服务进程。简而言之,短期 CLI 应用程序可以使用 getppid 代表父进程发送消息。
当我发现这一点时,我想到了另一个可能使用 getppid 的 Unix 工具:polkit,它是一个进程身份验证框架,用于控制发送 D-Bus 消息或运行特权应用程序等内容。 (至少,我猜您已经看到了 polkit 的身份验证代理显示的 GUI 密码提示。) polkit 包含一个名为 的可执行文件,
pkexec
其使用方式有点类似于 sudo,只不过现在 polkit 用于授权。现在,polkit 需要知道请求授权的进程的 PID...是的,你明白了,pkexec 使用 getppid 来查找。(在看的过程中我还发现polkit 的 TTY 身份验证代理也使用它.)
这个有点不太有趣,但仍然值得注意:getppid 用于模拟 PR_SET_PDEATHSIG如果在设置该标志时父母已经去世。 (该标志只是一种在父进程死亡时自动向子进程发送 SIGKILL 等信号的方法。)
答案2
僵尸:这是一种边缘情况,但它在影响程序是否停止的意义上是有效的。我遇到的唯一情况是,子进程的行为根据其父进程的不同而不同,是在它退出时。当进程退出时,SIGCHLD
将被发送到ppid
子进程。如果父进程被楔入或不处理SIGCHLD
,则子进程将处于僵尸状态,直到收到退出信号。如果ppid
子进程在僵尸状态下发生变化,通过杀死父进程并重新设置父进程init
,并且SIGCHLD
收到 ,则子进程将完成终止并被收获。
答案3
似乎进程需要了解系统中任何其他进程(例如它的父进程、祖父进程等)的场景范围涉及进程管理和/或进程间通信。为了进一步缩小范围,以 apid_t
作为参数或返回 a 的操作范围pid_t
主要是信令和其他进程管理任务。鉴于使用范围如此有限,除了诊断信息之外,我能想到使用的唯一原因getppid
是子级是否需要向父级发出信号,或者确定父级是否仍在运行。例如,mod_md
在阿帕奇httpd
向父进程发送优雅重启信号触发重新配置。
虽然您问题中的“注释”描述了 的一个很好的替代方案getppid
,但它似乎也暗示了 的可能用途getppid
。作为背景,我昨晚观看了 PWL Conf 2019 的演讲,论编程语言的表达能力,而该论文中“表达能力”的定义使我对这个问题的解释产生了偏差。该定义归结为程序的行为在存在或不存在某个功能的情况下可能会有所不同。
从假设开始:
- 由于某种原因,进程需要向其父进程发出信号。
- 不同的操作系统以不同的方式处理重新确定孤立进程的父子关系。
fork
如果预PID不匹配是什么意思getppid
?fork
与pre-PID 进行比较getppid
实际上可能为子进程提供跨平台、无竞争的机制来确定它是否已被孤立。如果pre-fork-pid != getppid
那么这个孩子就是孤儿,否则就不是。
这看起来确实是一个不常见的需求,或者#ifdef
在需要时可能会使用特定于平台的编译来编写的东西。此外,使用kill 0
pre- 也fork
可以达到类似的效果pid
,但是当系统重用“旧”进程 ID 时,这可能会在运行时间足够长的进程中产生误报。
fork
因此,不可变的预父进程 ID 和“实时”调用的组合getppid
看起来可能是一种高度可靠的跨平台机制,用于检查父进程或向父进程发送信号。