Two pidfd : and

By

16, 2024

-1.5-flash

pidfd 机制使用文件描述符(file )来以一种明确且无竞态(race-free)的方式来对进程进行引用,该机制最初是在 2018 年首次引入的。之后该接口获得了一些新功能,但随着接口的成熟,开发速度逐渐放缓。然而,目前有一些补丁正在进行讨论,希望在简化某些情况下使用 pidfd。

从 pidfd 获取信息

pidfd 可以执行许多操作,包括向其表示的进程发送信号或等待进程退出。然而,目前缺少一项功能:获取有关进程的信息。在当前的内核中,唯一可行的方法是将 pidfd 转换为常规的进程 ID( ID),然后使用该 PID 在/proc下打开文件并读取所需信息。此解决方案可行,但它速度慢、步骤繁琐且存在竞态条件,因为目标进程可能在操作序列的中间消失。它还要求存在/proc目录。似乎应该有一种更好的方法。

Luca 提出的此补丁是目前该解决方案的最佳候选。它为 pidfd 添加了一个名为的新ioctl()操作:

size = ioctl(pidfd, PIDFD_GET_INFO, &info);

info指针用于存放返回的信息;它目前定义为:

struct pidfd_info {
/* Let userspace request expensive stuff explictly, and let the kernel
* indicate whether it knows about it. */
__u64 request_mask;
__u64 cgroupid;
__u32 pid;
__u32 tgid;
__u32 ppid;
__u32 ruid;
__u32 rgid;
__u32 euid;
__u32 egid;
__u32 suid;
__u32 sgid;
__u32 fsuid;
__u32 fsgid;
__u32 spare0[1];
};

字段是操作的输入参数,用于控制信息请求的选项;其余参数用于返回所需信息:进程 ID(=pid=)、线程组 ID(=tgid=)、父进程 ID(=ppid=)、实际用户和组 ID(=ruid= 和rgid)、有效用户和组 ID(euid和egid)、已保存的用户和组 ID(suid和sgid)以及文件系统用户和组 ID(fsuid和fsgid)。包含进程的控制组的 ID 存储在中,但前提是在中设置了位(因为获取此值需要更多开销)。如果有效,则在返回时也会在中设置该位。

不难想象madvise,将来可能需要获取的数据列表会不断增加。与最近添加的一些内核功能一样,内核会检查从用户空间传入的结构的大小,以检测接口的旧版本或新版本。提供新功能需要在结构中添加新字段,从而改变结构的大小,以便检测不匹配。内核不会返回超过传入结构可以容纳的信息,因此将来可以添加新字段,而不会破坏二进制兼容性。

虽然系统调用通常需要用户空间显式地将结构的大小传递给内核以便进行此测试,但使用ioctl()可以简化一些操作。与ioctl()命令的常规做法一样,结构的大小直接编码在宏中,因此当请求该操作时,它会隐式地传递给内核。

截至目前,此补丁已经经历了九次修订,并且似乎至少还会进行一次修订。然而,似乎并没有人反对其目标或提供的接口,因此可以预期它将在不久的将来合并到主线()中。

自我引用

pidfd 旨在允许一个进程维护对另一个进程的句柄,并且有几个系统调用允许使用该句柄以某种方式对另一个进程进行操作。然而,有时一个进程希望使用其中一个系统调用来对自己进行操作。以进程 ID 作为参数的系统调用通常将值为零解释为指代调用进程,但 pidfd 系统调用没有等效的表示法。因此,任何希望使用这些系统调用的进程都必须首先使用()获取自身的 pidfd。

大多数情况下,这种遗漏并不重要;对于大多数面向 pidfd 的系统调用,都存在一个使用进程 ID 作为参数的等效调用,可以用来代替。唯一例外是(),它没有非 pidfd 的等效调用——这一事实引发了一些讨论,当时该调用正在开发中。因此,如果一个进程希望对自己调用(),除了获取自身的 pidfd 之外别无选择。因此,如果目标是使用()对调用进程进行操作,则必须获取自身的 pidfd。

建议通过一个看似简单的补丁来改变这种情况madvise,该补丁允许将相关系统调用的 pidfd 参数指定为。然后,该调用将对调用进程进行操作,而无需打开和管理单独的 pidfd。这个想法似乎直截了当且没有争议,但事实证明,其中有一个小问题。

问道,是指代当前进程(这意味着指代进程中的线程组组长),还是指代实际进行调用的线程?对于某些系统调用,这种区分很重要;结果将根据使用哪种解释而有所不同。本来打算让指代线程组组长,但 令人信服地论证了,解释成当前线程可能更符合调用者的意愿。因此,补丁集不得不进行修改。

更新后的版本创建了两个新的特殊 pidfd 值:,它将指代调用线程,以及OUP,它指代线程组组长。虽然这些名称可供用户空间使用,但也存在这些名称的别名(分别为和),用户空间程序实际上应该使用这些别名。总之,默认值()已更改为指代当前线程,但组长语义仍然可用。

解决了这个问题后,似乎不再有人对该补丁系列提供的功能有任何顾虑。除非出现意外情况,和似乎很可能在 6.13 版本中出现。

全文完

LWN 文章遵循 CC BY-SA 4.0 许可协议。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注