Ale*_*ing 9 process posix process-groups
在 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 设置为某些其他值),因此避免竞争条件的唯一方法是getpid()
在分叉之前通过调用获取父进程 ID ,然后在子进程中使用该值。
当我看到这个问题时,我很感兴趣,因为我知道我以前见过使用过 getppid ..但我不记得在哪里。所以,我转向了我认为可能使用过每个 Linux 系统调用的项目之一,然后是一些:systemd。一个GitHub的搜索后,我发现了两个使用了一些刻画更普遍的使用情况(也有一些其它用途,但他们更具体的systemd):
在sd-notify 中。对于某些情况:systemd 需要知道服务何时启动,以便它可以继续启动任何依赖于它的服务。这通常是通过sd_notify API从 C 程序完成的,这是守护进程告诉 systemd 其状态的一种方式。
当然,如果您使用 shell 脚本作为服务...调用 C 函数并不是完全可行的。因此,systemd 附带了systemd-notify 命令,它是 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 这样的信号的方式。)