Linux 操作系统原理 — 文件系统 — 虚拟文件系统

休言女子非英物,夜夜龙泉壁上鸣。这篇文章主要讲述Linux 操作系统原理 — 文件系统 — 虚拟文件系统相关的知识,希望能为你提供帮助。


目录

文章目录


  • ??目录??
  • ??为什么需要文件系统???
  • ??Linux 的虚拟文件系统??
  • ??创建 Linux 文件系统??



为什么需要文件系统??程序 = 数据结构 + 算法?,所有的应用程序都需要存储和检索信息,进程运行时它能够在内存空间内存储一定量的信息。然而,存储的容量受到进程的用户态虚拟内存空间大小的限制。用户进程的数据需要持久化在拥有更大容量的外存(磁盘)空间。
磁盘(Magnetic disk)一直以来都是持久化数据的设备,理论上,只要有了读写操作就能解决持久化数据存取的问题。但事实上,磁盘需要支持更多的操作,特别是在多程序运行的大型系统上(如服务器)。在这种情况下,很容易产生一些问题,例如:

  • 你如何找到这些信息?
  • 你如何保证一个用户不会读取另外一个用户的数据?
  • 你怎么知道哪些块是空闲的?

为了解决浙西问题,UNIX 设计者提出一个新的抽象 - 文件。在 UNIX-like 操作系统一致秉承着 ?一切皆文件? 的设计理念。
文件(Files)是由进程创建的逻辑信息单元。一个磁盘会包含成百上千万个文件。进程能够读取已存在的文件,并在需要时修改它们。存储在文件中的信息必须是持久的,这也就是说,不会因为进程的创建和终止而受影响。一个文件只能在当用户明确删除的时候才能消失。文件由操作系统进行管理,有关文件的构造、命名、访问、使用、保护、实现和管理方式都是操作系统设计的主要内容。
事实上,如果你能把每个文件都看作一个独立的地址空间,那么你真正理解了文件的概念。操作系统中处理文件的部分称为文件系统(File system)。
Linux 的虚拟文件系统文件系统是对一个存储设备上的数据和元数据进行组织的机制。由于定义如此宽泛,所以 Linux 文件系统的接口实现采用了分层的体系结构:将用户接口层、文件系统实现和操作存储设备的驱动程序分隔开,从而构成了一个虚拟文件系统(Virtual File System,VFS)。
在同一操作系统下,可能会使用到不同的文件系统。在计算机世界中,任何问题都可以添加一个层级来加个代理来解决。UNIX 操作系统通过虚拟文件系统来将多种文件系统构成一个有序的结构。其关键思想是:抽象出所有文件系统都共有的部分,并将这部分代码放在一层,这一层再调用具体文件系统来管理数据。
Linux 使用最普遍的文件系统是 ExtX,也能够支持 FAT、 FAT32、 VFAT 和 ISO9660 等不同类型的文件系统,从而可以方便地和其它操作系统交换数据。

  • ext2:早期 Linux 中常用的文件系统。
  • ext3:ext2 的升级版,带有日志功能。
  • ext4:较新的文件系统版本。
  • RAMFS:内存文件系统,速度很快。

VFS 隐藏了各种硬件的具体细节,把文件系统操作和不同文件系统的具体实现细节分离了开来,为所有的设备提供了统一的接口,VFS 提供了多达数十种不同的文件系统。VFS 可以分为逻辑文件系统和设备驱动程序:

  • 逻辑文件系统指 Linux 所支持的文件系统,如 extX、FAT 等。
  • 设备驱动程序指为每一种硬件控制器所编写的设备驱动程序模块。

VFS 对用户进程提供一个 “上层” 接口,这个接口就是著名的 POSIX 接口。这些来自用户进程的调用,都是标准的 POSIX 系统调用,比如 open、read、write 和 seek 等。VFS 也有一个对于实际文件系统的 “下层” 接口,包含了许多针对特定文件系统的功能调用。
Linux 操作系统原理 — 文件系统 — 虚拟文件系统

文章图片

用户空间包含一些应用程序(例如,文件系统的使用者)和 GNU C 库(glibc),它们为文件系统调用(打开、读取、写和关闭)提供用户接口。系统调用接口的作用就像是交换器,它将系统调用从用户空间发送到内核空间中的适当端点。
VFS 是底层文件系统的主要接口。这个组件导出一组接口,然后将它们抽象到各个文件系统,各个文件系统的行为可能差异很大。有两个针对文件系统对象的缓存(inode 和 dentry)。它们缓存最近使用过的文件系统对象。
Linux 操作系统原理 — 文件系统 — 虚拟文件系统

文章图片

当系统启动时,根文件系统在 VFS 中注册。另外,当装载其他文件系统时,不管在启动时还是在操作过程中,它们也必须在 VFS 中注册。另外,在引导时或操作期间挂载其他文件系统时,它们也必须向 VFS 注册。当文件系统注册时,其基本作用是提供 VFS 所需功能的地址列表、调用向量表、或者 VFS 对象。因此一旦文件系统注册到 VFS,它就知道从哪里开始读取数据块。
装载文件系统后就可以使用它了。比如,如果一个文件系统装载到 /usr 并且一个进程调用它:
open("/usr/include/unistd.h", O_RDONLY)

当解析路径时, VFS 看到新的文件系统被挂载到 /usr,并且通过搜索已经装载文件系统的超级块来确定它的超块。然后它找到它所转载的文件的根目录,在那里查找路径 include/unistd.h。然后 VFS 创建一个 vnode 并调用实际文件系统,以返回所有的在文件 inode 中的信息。这个信息和其他信息一起复制到 vnode (内存中)。而这些其他信息中最重要的是指向包含调用 vnode 操作的函数表的指针,比如 read、write 和 close 等。
当 vnode 被创建后,为了进程调用,VFS 在文件描述符表中创建一个表项,并将它指向新的 vnode,最后,VFS 向调用者返回文件描述符,所以调用者可以用它去 read、write 或者 close 文件。
当进程用文件描述符进行一个读操作时,VFS 通过进程表和文件描述符确定 vnode 的位置,并跟随指针指向函数表,这样就调用了处理 read 函数,运行在实际系统中的代码并得到所请求的块。VFS 不知道请求时来源于本地硬盘、还是来源于网络中的远程文件系统、CD-ROM 、USB 或者其他介质,所有相关的数据结构如下图所示:从调用者进程号和文件描述符开始,进而是 vnode,读函数指针,然后是对实际文件系统的访问函数定位。
Linux 操作系统原理 — 文件系统 — 虚拟文件系统

文章图片

创建 Linux 文件系统创建一个经过初始化的文件:
$ dd if=/dev/zero of=file.img bs=1k count=10000
10000+0 records in
10000+0 records out

现在有了一个 10MB 的 file.img 文件。使用 losetup 命令将一个循环设备与这个文件关联起来,让它看起来像一个块设备,而不是文件系统中的常规文件:
$ losetup /dev/loop0 file.img

这个文件现在作为一个块设备出现(由 /dev/loop0 表示)。然后用 mke2fs 在这个设备上创建一个文件系统。这个命令创建一个指定大小的新的 ext2 文件系统:
$ mke2fs -c /dev/loop0 10000
mke2fs 1.35 (28-Feb-2004)
max_blocks 1024000, rsv_groups = 1250, rsv_gdb = 39
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
2512 inodes, 10000 blocks
500 blocks (5.00%) reserved for the super user
...

使用 mount 命令将循环设备(/dev/loop0)所表示的 file.img 文件挂装到挂装点 /mnt/point1。注意,文件系统类型指定为 ext2:
$ mkdir /mnt/point1
$ mount -t ext2 /dev/loop0 /mnt/point1

还可以继续这个过程:在刚才挂装的文件系统中创建一个新文件,将它与一个循环设备关联起来,再在上面创建另一个文件系统。
$ dd if=/dev/zero of=/mnt/point1/file.img bs=1k count=1000
1000+0 records in
1000+0 records out

$ losetup /dev/loop1 /mnt/point1/file.img

$ mke2fs -c /dev/loop1 1000
mke2fs 1.35 (28-Feb-2004)
max_blocks 1024000, rsv_groups = 125, rsv_gdb = 3
Filesystem label=
...

$ mkdir /mnt/point2
$ mount -t ext2 /dev/loop1 /mnt/point2
$ ls /mnt/point2
lost+found
$ ls /mnt/point1
file.img lost+found

通过这个简单的演示很容易体会到 Linux 文件系统(和循环设备)是多么强大。可以按照相同的方法在文件上用循环设备创建加密的文件系统。可以在需要时使用循环设备临时挂装文件,这有助于保护数据。


【Linux 操作系统原理 — 文件系统 — 虚拟文件系统】


    推荐阅读