[toc]
fs/fs_context.c 文件系统创建上下文(Filesystem Creation Context)
历史与背景
这项技术是为了解决什么特定问题而诞生的?
fs_context
框架的诞生是为了彻底改革和现代化Linux内核中挂载文件系统的方式,以解决传统mount(2)
系统调用长期以来存在的诸多根本性问题:
- 混乱的参数传递:
mount(2)
系统调用使用一个名为data
的参数(void *data
),它实际上被用作一个非结构化的、以逗号分隔的字符串来传递文件系统特定的挂载选项(如"rw,noatime,jounal_checksum"
)。这种方式有几个巨大的缺点:- 解析复杂且容易出错:每个文件系统都必须编写自己的、脆弱的字符串解析器来提取选项。
- 缺乏类型安全:所有选项都被当作字符串,无法以原生方式传递整数、布尔标志或二进制数据(如安全密钥)。
- 难以验证:内核很难在文件系统解析之前对选项进行统一的验证。
- API不灵活且难以扩展:
mount(2)
的参数是固定的。当出现新的挂载需求或新型文件系统(特别是那些没有传统块设备源的,如网络文件系统或虚拟文件系统)时,很难在不滥用现有参数或不增加新系统调用的情况下优雅地支持它们。 - “源”的概念过于狭隘:
mount(2)
的设计思想中心是“将一个设备节点挂载到一个目录上”。这对于NFS、Ceph等网络文件系统或完全在内存中操作的虚拟文件系统来说,模型并不完全匹配。
fs_context
被设计为一个全新的、基于状态机的API,它将挂载操作从一个单一、庞大的系统调用分解为一系列清晰、独立的配置步骤,从而解决了上述所有问题。
它的发展经历了哪些重要的里程碑或版本迭代?
- 概念提出与开发:由内核资深开发者David Howells主导设计和开发,旨在提供一个更干净、更可扩展的挂载API。
- 首次合入内核主线:围绕
fs_context
的一系列新系统调用(fsopen
,fsconfig
,fsmount
,fspick
等)在Linux内核5.1版本中被正式合入。 - 逐步采纳与转换:在新API引入后,内核社区开始逐步将现有的文件系统迁移到使用
fs_context
框架。同时,所有新开发的文件系统都被强烈推荐使用这个新框架。 - 用户空间工具支持:核心的系统工具包
util-linux
(提供了mount
,umount
等命令)也进行了更新,使其能够利用新的fs_context
系列系统调用,从而将新框架的优势带给最终用户。
目前该技术的社区活跃度和主流应用情况如何?
fs_context
现在是Linux内核中推荐的、标准的挂载文件系统的方式。虽然为了向后兼容,旧的mount(2)
系统调用仍然被支持,但新的开发和功能都围绕fs_context
进行。
- 它被视为所有未来文件系统挂载和配置工作的基础。
- 许多主流的文件系统,如Ext4, Btrfs, XFS, NFS, Ceph等,已经支持或正在迁移到
fs_context
框架。 - 它是解决复杂挂载场景(例如,需要传递Kerberos安全票据来挂载网络文件系统)的首选方案。
核心原理与设计
它的核心工作原理是什么?
fs_context
的核心思想是将挂载过程从一个原子操作转变为一个状态化的、分步构建的过程。这个过程由一个名为fs_context
的内核对象来驱动,并通过一系列新的系统调用进行交互。
典型的挂载流程如下:
- 创建上下文 (
fsopen
): 用户空间首先调用fsopen()
,并指定所需的文件系统类型(如 “ext4”)。内核会为这个请求创建一个fs_context
对象,并返回一个指向该对象的文件描述符。这个上下文可以被看作是一个“挂载请求的草稿”。 - 配置上下文 (
fsconfig
): 用户空间随后可以多次调用fsconfig()
来配置这个上下文。fsconfig()
以键值对的形式接收参数,取代了旧的逗号分隔字符串。例如:fsconfig(fd, FSCONFIG_SET_STRING, "source", "/dev/sda1", 0)
: 设置挂载源。fsconfig(fd, FSCONFIG_SET_FLAG, "ro", NULL, 0)
: 设置只读标志。fsconfig(fd, FSCONFIG_SET_BINARY, "key", key_blob, 0)
: 传递二进制数据。
每次调用,文件系统都可以立即解析和验证传入的参数,如果参数无效则立即返回错误。
- 创建/重用超级块 (
fsmount
,fspick
): 当所有配置完成后,用户空间调用一个最终动作:fsmount()
: 指示内核根据配置好的上下文创建一个新的文件系统实例(即一个新的超级块super_block
),并返回一个可用于挂载的文件描述符。fspick()
: 指示内核查找一个已存在的、与当前上下文配置相匹配的文件系统实例,并返回其文件描述符。
这个过程将复杂的挂载操作分解为一系列简单、明确的步骤,使得整个流程更加清晰、健壮和可扩展。
它的主要优势体现在哪些方面?
- 清晰和结构化的API:用类型化的键值对取代了混乱的选项字符串,提高了代码的可读性和安全性。
- 极高的可扩展性:文件系统可以轻松地定义和添加新的配置选项,而无需修改任何系统调用的签名。
- 及早的参数验证:每个选项在通过
fsconfig()
设置时就可以被验证,用户可以得到更及时、更精确的错误反馈。 - 对无源文件系统的原生支持:该模型不强制要求一个块设备源,非常适合网络或虚拟文件系统。它们可以定义自己的配置键(如 “server_addr”)来接收必要的信息。
- 支持复杂数据:可以原生传递二进制数据,解决了之前需要通过临时文件等变通方法来传递密钥等信息的难题。
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
- 对简单场景的开销:对于一个非常简单的挂载操作,执行一系列系统调用(
fsopen
,fsconfig
,fsmount
,mount_setattr
)比执行单个mount(2)
系统调用在代码上更繁琐,并且有更多的上下文切换开销。然而,这种开销在宏观上通常可以忽略不计,并且换来了巨大的灵活性和健壮性。 - 过渡期的复杂性:在过渡期间,内核和用户空间工具需要同时支持新旧两套API,这会增加一定的维护负担。
使用场景
在哪些具体的业务或技术场景下,它是首选解决方案?请举例说明。
fs_context
是所有新的文件系统实现和新的挂载相关功能的首选解决方案。
- 例一:挂载需要复杂认证的网络文件系统
当挂载一个需要Kerberos认证的NFS或Ceph文件系统时,认证票据是一个二进制数据块。使用fs_context
,用户空间程序可以直接通过fsconfig(fd, FSCONFIG_SET_BINARY, ...)
将这个票据传递到内核,而无需将其写入临时文件。 - 例二:实现具有大量可调参数的文件系统
像Btrfs这样的现代文件系统有大量的挂载选项来控制其各种子功能(如压缩、配额组、子卷等)。通过fs_context
,每个功能都可以被实现为一个独立的配置键,使得用户空间的管理工具可以清晰、独立地配置它们。 - 例三:容器化环境中的挂载
在容器运行时中,需要以编程方式创建和配置各种挂载点。fs_context
提供的分步、可验证的API非常适合这种需要精确控制和错误处理的自动化场景。
是否有不推荐使用该技术的场景?为什么?
基本上没有不推荐使用该技术的新开发场景。它的设计目标就是成为未来的标准。唯一会使用旧mount(2)
接口的场景是:
- 维护遗留应用程序:为了保持与旧的、未经修改的应用程序的二进制兼容性,内核必须继续支持
mount(2)
。但任何对这些程序的新功能添加或重构都应该考虑迁移到新的API。
对比分析
请将其 与 其他相似技术 进行详细对比。
fs_context
最直接的对比对象就是它所要取代的传统mount(2)
系统调用。
特性 | fs_context API (fsopen , fsconfig , …) |
传统 mount(2) 系统调用 |
---|---|---|
API风格 | 状态化、多步构建。通过一个上下文对象逐步配置。 | 原子化、单次调用。所有参数一次性传入。 |
参数传递 | 结构化的键值对,支持字符串、标志、二进制等多种类型。 | 非结构化。依赖一个扁平的标志位掩码和一个混乱的、文件系统特定的选项字符串。 |
可扩展性 | 非常高。添加新选项只需文件系统实现新的键,无需改变API。 | 差。选项字符串的复杂性不断增加,难以管理。添加非字符串类型的参数几乎不可能。 |
参数验证 | 及早、分步验证。每个fsconfig 调用都可以进行验证。 |
延迟、整体验证。只有在mount 调用时,文件系统才会一次性解析和验证整个选项字符串。 |
对无源FS的支持 | 原生设计。上下文的配置不依赖于特定的源类型。 | 需要变通。通常将源参数设为某个占位符字符串,然后在选项字符串中传递真正的信息。 |
错误报告 | 精确。可以明确指出是哪个配置键或值出了问题。 | 模糊。通常只返回一个通用的错误码,难以定位问题源头。 |
用户空间复杂度 | 对于简单挂载,代码略显冗长(需要多次调用)。 | 对于简单挂载,代码非常简洁(一次调用)。 |
fs/fs_context.c
put_fs_context 处理超级块配置上下文
1 | /** |
alloc_fs_context 创建一个文件系统上下文
1 | /** |
fs_context_for_mount 用于创建文件系统上下文
1 | struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, |
vfs_parse_sb_flag 解析超级块标志
1 | static const struct constant_table common_set_sb_flag[] = { |
vfs_parse_fs_param_source 用于处理文件系统上下文(fs_context)中的 “source” 参数
1 | /** |
vfs_parse_fs_param 解析文件系统参数
1 | /** |
vfs_parse_fs_string 仅用于解析字符串
1 | /** |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 wdfk-prog的个人博客!
评论