本文是Windows上Linux子系统系列博文的第四篇。有关背景资料,你可以读读架构总览,pico进程介绍和WSL系统调用这些博客文章。
代表Sven Groot发布。
Windows上Linux子系统的一个关键目标是允许用户像在Linux系统上一样使用他们的文件,而又对Windows系统中的文件有充分的互操作性。不像一个虚拟机,你必须使用网络共享或其他解决方案来共享主机和其他操作系统的文件,WSL直接访问所有你的Windows磁盘,允许很容易的进行交互操作。
Windows文件系统与Linux文件系统有很大的不同,这篇文章将会介绍WSL如何在这两个世界间架起桥梁。
通过虚拟文件系统(VFS)对抽象文件系统操作,它即提供了接口供用户程序与文件系统交互(通过系统调用如打开、读取、修改文件权限,获取文件信息,等等),又提供了一个文件系统必须实现的接口。这使得多个文件系统可以共存,并提供相同的操作和语义,VFS提供所有这些文件系统的统一的命名空间视图给用户。
在这个命名空间里,文件系统挂载在不同的目录。例如,在一个典型的Linux系统硬盘可能安装在根目录,/,/ dev,/ proc, / sys, / mnt, / cdrom所有挂载的不同文件系统可能在不同的设备上。举例来说,Linux上使用的文件系统包括ext4,rfs,FAT以及其他。
VFS通过使用大量的数据结实现了文件系统操作的各种系统调用,比如索引节点,目录项和文件,以及相关文件系统必须实现的回调函数。
索引节点(Inode)
inode是VFS中使用核心数据结构。它代表一个文件系统对象如一个常规文件、目录、符号链接,等等。一个inode包括文件类型,大小,权限,最后修改时间和其他属性等信息。对于许多常见的Linux磁盘文件系统,如ext4,磁盘上用来表示文件元数据的数据结构直接对应于Linux内核使用的inode结构。
虽然一个索引节点代表一个文件,但它不代表一个文件名。一个文件可能有多个名称或硬链接,但只有一个inode。
文件系统提供了一个查找函数来回调VFS,用来检索特定文件的inode,它是基于父inode和孩子的名字。文件系统必须实现其他一些inode操作,如修改权限,获取文件信息,打开文件等等。
VFS使用目录项缓存来表示文件系统名称空间。目录项只存在于内存中,并包含一个指向该文件的inode。举个例子,如果你有一个像/home/user/foo这样的路径,那么就有一个目录项对应于home,user和foo,每个都有一个指针指向一个inode。目录项是快速查找时的缓存,但如果一个条目在缓存中没有,就用inode查找操作来从文件系统中检索索引节点,然后就可以创建一个新的目录项。
当一个inode被打开,就创建了一个文件的的文件对象,它会记录该文件的很多信息,如跟踪文件偏移量、文件是只读,只写,还是两者兼而有之。文件系统必须提供的文件操作,如读read、写write、同步sync等。
应用程序通过文件描述符引用到文件对象。它们在一个进程中都是唯一的值,指向进程所打开的文件。文件描述符可以指向其他提供文件接口的对象,在Linux中,提供文件接口的对象包括tty、套接字和管道。多个文件描述符可以指向相同的文件对象,例如可使用dup系统调用获得同一个文件对象的文件描述符。
除了常规的文件和目录,Linux支持许多额外的文件类型。包括设备文件、fifo、套接字和符号链接。
其中有些文件会影响路径解析。符号链接是一种特殊文件,指向一个不同的文件或目录,并通过VFS对符号链接指向的文件进行无缝地处理。如果你打开路径/foo/bar/baz,并且bar是/zed的一个符号链接,那么你实际打开的就是/zed/baz。
类似地,可以使用一个目录作为另一个文件系统的挂载点。在这种情况下,当一个路径包含这个目录,那么挂载点下面所有inode的操作实际都会在新的文件系统上。
Linux使用许多并不从磁盘读取文件的文件系统。TmpFs作为临时内存文件系统使用,其内容将不会持久化。ProcFs和SysFs提供进程的内核信息、设备和驱动程序访问。这些文件系统没有相关的磁盘、网络或其他设备,而是由内核虚拟化出来的。
Windows将所有系统资源都泛化成对象。这些不仅包括文件,而且还包括线程,共享内存段,计时器,这里仅举几例。所有打开文件的请求最终都通过NT内核对象管理器,它通过I/O管理器将请求路由到正确的文件系统驱动程序。在Windows上文件系统驱动程序实现的接口更通用,并只需要更少的需求。例如,没有类似的公共的inode结构,也没有目录项;相反,文件系统驱动程序如ntfs.sys负责解析路径和打开文件对象。
Windows文件系统虽然也可以挂载到其它文件系统的目录中,但通常是挂载到诸如C:和D:这样的驱动器号上。这些驱动器号实际上是一种Win32的结构,对象管理器是不能直接对其进行处理的。对象管理器使用的命名空间和Linux文件系统的命名空间有点类似,它的根目录是,文件系统卷标用路径为设备名硬盘卷标1这样的设备对象来表示。
当我们使用C:fooar这样的路径打开某个文件时,Win32的CreateFile函数将其转换成一个NT路径,形式为“Dos设备名C:fooar”,这里的“Dos设备名C:”通常是一个符号链接,例如,链接到“设备名硬盘卷标4”。因此,该文件真正的完整路径应当是“设备名硬盘卷标4fooar”。由对象管理器确定路径中的每个组成部分,直到遇到了设备对象,这和Linux中的VFS很类似。这时候,对象管理器将请求提交给IO管理器,IO管理器创建一个包含剩余路径的IO请求包(IRP),将该请求包发送至文件系统驱动器以确定设备名。
当打开某个文件时,对象管理器会为该文件创建一个文件对象。对象管理器提供指向文件对象的句柄,而不是文件描述符。实际上,句柄可以指向任意的对象管理器对象,而不只是文件。
当你调用某个系统调用时,比如NtReadFile(通常使用Win32的ReadFile函数),I/O管理器会再次创建一个发送给文件系统驱动器的IRP(输入输入请求包),以使文件对象可以执行此请求。
因为在NT中没有索引节点或者类似的东西,所以Windows中针对文件的绝大部分操作都需要一个文件对象。
Windows系统仅能支持两种文件类型:普通文件和目录。文件和目录都可以成为重新解析点,重解析点是具有固定的头部和任意数据块的特殊文件。头部包括一个用来标识重解析点类型的标签,并且必须通过文件系统过滤器进行处理,头部也可能包括内置的重解析点类型,也就是I/O管理器本身。
重解析点用来实现符号链接和挂载点。在这些情况下,标签表示重解析点是一个符号链接或挂载点,和重解析点相关联的数据中包括了链接目标或挂载点的卷标名。重解析点也可以具有其它的用途,比如作为Windows8系统中OneDrive使用的占位符文件。
不像Linux,Windows文件系统默认情况下是不区分大小写的。实际上,Windows和NTFS是支持大小写敏感的,只是默认不启用而已。
Windows上的Linux子系统(WSL)必须将各种Linux文件系统操作转换成NT内核操作。 WSL必须提供一块控件来存放Linux系统文件,来支撑所有的功能,包括Linux权限,符号链接和其他特殊文件如FIFO;它还必须提供访问你系统中Windows卷的能力;它还必须提供一些特殊的文件系统比如ProcFs。
为了做到这些,WSL实现了一个VFS组件,它是仿照Linux上的 VFS。总架构如下所示。
当一个应用程序调用一个系统调用,它是被系统调用层处理,系统调用层定义了各种内核功能项如open、read、 chmod和stat等等。对于这些文件相关的系统调用,系统调用层几乎不做什么处理;它基本上就是把请求传递给VFS。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务