LWN:listmount() 以及 statmount()!

本文介绍了Linux内核在面对现代系统中大量动态挂载文件系统的需求时,MiklosSzeredi提出的listmount和statmount两个新系统调用。这些调用旨在提供更有效的方式来管理文件系统挂载,避免处理mountinfo的增长带来的复杂性。
摘要由CSDN通过智能技术生成

关注了就能看到更多这么棒的文章哦~

listmount() and statmount()

By Jonathan Corbet
November 10, 2023
ChatGPT translation
https://lwn.net/Articles/950569/

多年前,Unix 或 Linux 机器上挂载(mount)的文件系统很少,且比较固定。添加文件系统通常是一个罕见的事件,一般是在购买新驱动器的时候才会发生。相比之下,具有大量容器的现代系统中可能会有很多个 mount 好的文件系统,并且经常动态变化。正如讨论的那样,在2023 LSFMM+BPF峰会上,Linux 内核提供有关挂载文件系统的信息的机制没有跟上这一最新趋势,导致了系统管理上的问题。现在,由 Miklos Szeredi提出的两个新的系统调用似乎将为此提供一些迫切需要的缓解。

即使在没有容器的情况下,在典型的 Linux 系统上,mount 的文件系统列表也在增长,部分原因是内核提供的虚拟文件系统数量增加。例如,在编者的基本桌面系统中,/proc/self/mountinfo 列出了 34 个挂载点,其中很少分区是对应于实际存储设备的。随着这个虚拟文件内容变得越来越长,对于系统管理工具(以及人类)来说也更难对它进行处理了。因此引出了新增的系统调用 listmount() 和 statmount() ,它们提供了一种替代方法,而不用再查看 mountinfo 文件了。

然而,在实现这些系统调用之前,Szeredi 必须解决一个相关的问题。系统中的每个挂载都被分配了一个挂载 ID 来标识它;该 ID 可以从statx()获取,这是在新系统调用中指明哪个挂载的直接方式。然而,如果卸载了文件系统,其 ID 将被内核重新使用以标识未来的新挂载,因此这是一个含义不唯一的标识符。明显的解决方案是不要再重用已经用过的挂载 ID,因为没有必要。

不幸的是,有一些用户空间程序假定挂载 ID 是一个 32 位的数量,尽管在 statx() 系统调用中被定义为 _u64 。Systemd 在LSFMM+BPF讨论期间被确定为其中一个例子程序. 使 32 位挂载 ID 在系统的整个生命周期内是唯一的,就意味着只允许有 40 亿个挂载,这在某些情况下显然是有限制的;它也可能被攻击者故意进行溢出攻击。因此,需要一个新的、更明确为 64 位的挂载 ID。这组补丁系列添加了这样的一个 ID,以及一个新的 statx() 标志(STATX_MNT_ID_UNIQUE),该标志会返回 unique ID 而不是 32 位 ID(当然,后者不能被删除)。为了避免混淆这两个 ID,唯一挂载(unique mount) ID 的最小值是 2^32。

在此基础上,就可以添加这两个系统调用了。第一个是这样的:

struct __mount_arg {
__u64 mnt_id;
__u64 request_mask;
};

int listmount(const struct __mount_arg *req, u64 *buf, size_t bufsize,
          unsigned int flags);

调用 listmount() 就会返回在由 req->mnt_id 标识的挂载点下挂载的文件系统列表,其中该 ID 必须是唯一值(unique)类型的。得到的结果作为唯一挂载 ID 的数组返回到 buf 中,其长度为 bufsize 。通常,不可访问的挂载点(例如,可能在不同的挂载命名空间中挂载)将被省略;将 flags 设置为 LISTMOUNT_UNREACHABLE 会导致也把它们都列出来;此选项需要 CAP_SYS_ADMIN 权限。 LISTMOUNT_RECURSIVE 标志将导致 listmount() 对起始挂载点下的层次结构进行深度优先遍历,并列出在那里找到的所有挂载;否则,只返回直接子挂载(direct child mount)。返回值是返回的挂载 ID 的个数(或错误代码)。

req 结构的 request_mask 字段不会被 listmount() 使用,必须为零。

另一个调用 statmount() 会返回指定挂载的详细信息:

int statmount(const struct __mount_arg *req, struct statmnt *buf,
          size_t bufsize, unsigned int flags);

在这个系统调用中, req->mnt_id 仍然像以前一样指示感兴趣的挂载点,而 req->request_mask 告诉内核请求哪些信息。 flags 值必须为零,而 buf 指向一个以此结构开始的缓冲区(长度为 bufsize):

struct statmount {
__u32 size;     /* Total size, including strings */
__u32 __spare1;
__u64 mask;     /* What results were written */
__u32 sb_dev_major; /* Device ID */
__u32 sb_dev_minor;
__u64 sb_magic;     /* ..._SUPER_MAGIC */
__u32 sb_flags;     /* MS_{RDONLY,SYNCHRONOUS,DIRSYNC,LAZYTIME} */
__u32 fs_type;      /* [str] Filesystem type */
__u64 mnt_id;       /* Unique ID of mount */
__u64 mnt_parent_id;    /* Unique ID of parent (for root == mnt_id) */
__u32 mnt_id_old;   /* Reused IDs used in proc/.../mountinfo */
__u32 mnt_parent_id_old;
__u64 mnt_attr;     /* MOUNT_ATTR_... */
__u64 mnt_propagation;  /* MS_{SHARED,SLAVE,PRIVATE,UNBINDABLE} */
__u64 mnt_peer_group;   /* ID of shared peer group */
__u64 mnt_master;   /* Mount receives propagation from this ID */
__u64 propagate_from;   /* Propagation from in current namespace */
__u32 mnt_root;     /* [str] Root of mount relative to root of fs */
__u32 mnt_point;    /* [str] Mountpoint relative to current root */
__u64 __spare2[50];
char str[];     /* Variable size part containing strings */
};

内核不一定会填充此结构的所有字段;相反,它只提供了 req->request_mask 字段中指示的那些信息。可用的 request 值包括:

  • STMT_SB_BASIC :挂载的“基本”超级块数据,具体包括 sb_dev_major 、 sb_dev_minor 、 sb_magic 和 sb_flags 字段。

  • STMT_MNT_BASIC :更多基本数据:mnt_id、mnt_parent_id、mnt_id_old、mnt_parent_id_old、mnt_attr、mnt_propagation、mnt_peer_group 和 mnt_master。

  • STMT_PROPAGATE_FROM :填充 propagate_from 字段。 (有关挂载传播 mount propagation 的详细信息,请参见共享子树shared subtrees文档)。

生成字符串的请求处理方式有点不同。实际的字符串数据将写入结构后面的内存中(buf 必须要达到足以容纳该数据),并且字符串的开始偏移将存储在相关的结构字段中。返回字符串的请求包括:

  • STMT_FS_TYPE:在结构后存储文件系统类型的字符串表示形式,将字符串的偏移存储在 fs_type 中。

  • STMT_MNT_ROOT:在结构后存储根文件系统的路径,将字符串的偏移存储在 mnt_root 中。

  • STMT_MNT_POINT:在结构后存储挂载点的路径,将字符串的偏移存储在 mnt_point 中。

成功返回时,mask 字段将被设置为指示内核写入结构的其他字段中的哪些结果。

VFS 维护者 Christian Brauner 已经接受了此系列,可能会在 6.8 版本中合并。但他在其中做了一些更改: struct statmnt 被重命名为 struct statmount ,而 struct __mount_arg 变成了 struct mnt_id_req 。"库可以以任何形式暴露这些值,但我们也会有直接的使用者。我更希望这个结构没有下划线并且得到正式认可。" 尽管最终尚未合入 linux-next 中,但一旦 6.7 合并窗口关闭,它应该很可能会出现在 linux-next 中。

C 库提供的接口与此处显示的可能有所不同,这也并不奇怪。例如, mnt_id_req 结构被用于简化跨多个体系结构的兼容性,但用户空间库没有相同的担忧,可能不希望暴露该结构。在这些系统调用出现在发布的内核中之前,这样的细节不太可能被确定。然而,最终将会有一种更好、更容易获取有关已挂载的文件系统信息的方法。

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

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

format,png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值