Back
Featured image of post File System

File System

基础知识

JOS

JOS不像其他操作系统一样在内核添加磁盘驱动,然后提供系统调用。我们实现一个文件系统进程来作为磁盘驱动。

  1. 引入一个文件系统进程(FS进程)的特殊进程,该进程提供文件操作的接口,并被赋予io权限。(x86处理器使用EFLAGS寄存器的IOPL为来控制保护模式下代码是否能执行设备IO指令,比如in和out。)
  2. 建立RPC机制,客户端进程向FS进程发送请求,FS进程真正执行文件操作,并将数据返回给客户端进程。
  3. 更高级的抽象,引入文件描述符。通过文件描述符这一层抽象就可以将控制台,pipe,普通文件,统统按照文件来对待。(文件描述符和pipe实现原理)
  4. 支持从磁盘加载程序并运行。

结构

superblock

依然使用超级块来记录文件系统的元数据

file

File struct用来描述文件:文件名,大小,类型,保存文件内容的block号

tips: Directories与file结构相同,只是内容是一些file

Block Cache

在FS进程的虚拟内存空间中映射一段磁盘区域。

FS进程在创建时,set特殊的缺页处理函数。

当发生缺页中断时call那个缺页处理函数,从磁盘上把数据读入物理内存。

根据FS进程内存地址空间的映射关系,FS可以很方便的通过虚拟内存找到刚读入的数据在物理内存中的位置。

The Block Bitmap

磁盘块的是否使用的bitmap

The file system interface

文件系统建立好后,还需要通过ipc来构建供用户进程操作文件的API栈,课程的图拿来用一下:

   Regular env           FS env
   +---------------+   +---------------+
   |      read     |   |   file_read   |
   |   (lib/fd.c)  |   |   (fs/fs.c)   |
...|.......|.......|...|.......^.......|...............
   |       v       |   |       |       | RPC mechanism
   |  devfile_read |   |  serve_read   |
   |  (lib/file.c) |   |  (fs/serv.c)  |
   |       |       |   |       ^       |
   |       v       |   |       |       |
   |     fsipc     |   |     serve     |
   |  (lib/file.c) |   |  (fs/serv.c)  |
   |       |       |   |       ^       |
   |       v       |   |       |       |
   |   ipc_send    |   |   ipc_recv    |
   |       |       |   |       ^       |
   +-------|-------+   +-------|-------+
           |                   |
           +-------------------+

Spawning Processes

spawn()创建一个新的进程,从文件系统加载用户程序,然后启动该进程来运行这个程序。spawn()就像UNIX中的fork()后面马上跟着exec():

  1. 从文件系统打开prog程序文件
  2. 调用系统调用sys_exofork()创建一个新的Env结构
  3. 调用系统调用sys_env_set_trapframe(),设置新的Env结构的Trapframe字段(该字段包含寄存器信息)。
  4. 根据ELF文件中program herder,将用户程序以Segment读入内存,并映射到指定的线性地址处。
  5. 调用系统调用sys_env_set_status()设置新的Env结构状态为ENV_RUNNABLE。

Linux Kernel

VFS

虚拟文件系统,是一个中间层,向上给用户提供一致性的文件系统接口,向下兼容各类文件系统。

规定了通用文件模型

结构

  1. 超级块对象: 存放已安装文件系统的元数据
  2. 索引节点对象(inode):关于具体文件的一般信息,索引节点号唯一标识文件,记录文件操作函数指针。
  3. 文件对象file:打开文件会在内核生成的文件对象,通过fd可以找到
  4. 目录项对象dentry:存放目录项与对应文件进行链接的信息。

例子: 三个进程打开同一个文件,生成三个文件对象,其中两个进程用一个硬链接,所以使用两个目录对象,同一个文件所以使用一个inode,通过inode能找到超级块对象,然后来调用对应文件系统的方法IO,除此之外,尝试用的目录项对象还会被缓存在一个叫做目录cache里,方便下次直接取用。

索引节点对象(inode)由链表组织。

file和dentry都没有磁盘映像,属于内核结构,显然slab里有对应的结构。

对于进程查找路径上的每一个分量,都为其创建目录项对象,只是属于不同级:/var/init -> 第一级目录项对象:/ 第二级: var 第三级:init

文件描述符: 文件描述符是进程对象task_struct里的files_struct这个记录进程的文件信息的结构内的一个成员:struct file*[] fd_array的索引。也就是说, 假如我们有一个文件描述符fd, 进程通过fd要操作文件,只需要去task_struct->files_struct->fd_array[fd]处查找,看有无对应的struct file*,找到那个指向内核缓存中的文件对象的指针。然后通过file找到对应的dentry再通过dentry找到inode再通过inode找到superblock,基于superblock解析inode数据找到磁盘上真正的存储位置。 fd_array的第一二项一般是标准输入输出文件,第三项一般是标准错误文件,其他一般指向打开文件

所以进程可打开文件的数量,由这个数组限制。

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy