0%

温故知新·文件系统

inode

虚拟文件系统

一个操作系统可以支持多种底层不同的文件系统(比如NTFS, FAT, ext3, ext4),为了给内核和用户进程提供统一的文件系统视图,Linux在用户进程和底层文件系统之间加入了一个抽象层,即虚拟文件系统(Virtual File System, VFS),进程所有的文件操作都通过VFS,由VFS来适配各种底层不同的文件系统,完成实际的文件操作。

文件描述符

Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。

文件描述符在形式上是一个非负整数, 实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。

IO

习惯上,标准输入(standard input)的文件描述符是 0,标准输出(standard output)是 1,标准错误(standard error)是 2。

POSIX 定义了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 来代替 0、1、2。这三个符号常量的定义位于头文件 unistd.h。
文件描述符的有效范围是 0 到 OPEN_MAX。

Page Cache

文件读写流程

读文件

  • 进程调用库函数向内核发起读文件请求;
  • 内核通过检查进程的文件描述符定位到虚拟文件系统的已打开文件列表表项;
  • 调用该文件可用的系统调用函数read()
  • read()函数通过文件表项链接到目录项模块,根据传入的文件路径,在目录项模块中检索,找到该文件的inode;
  • 在inode中,通过文件内容偏移量计算出要读取的页;
  • 通过inode找到文件对应的address_space;
  • 在address_space中访问该文件的页缓存树,查找对应的页缓存结点:
    • 如果页缓存命中,那么直接返回文件内容;
    • 如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页;重新进行第6步查找页缓存;
  • 文件内容读取成功。

写文件

前5步和读文件一致,在address_space中查询对应页的页缓存是否存在:

  • 如果页缓存命中,直接把文件内容修改更新在页缓存的页中。写文件就结束了。这时候文件修改位于页缓存,并没有写回到磁盘文件中去。
  • 如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页。此时缓存页命中,进行第6步。
  • 一个页缓存中的页如果被修改,那么会被标记成脏页。脏页需要写回到磁盘中的文件块。有两种方式可以把脏页写回磁盘:
    • 手动调用sync()或者fsync()系统调用把脏页写回
    • pdflush进程会定时把脏页写回到磁盘
  • 同时注意,脏页不能被置换出内存,如果脏页正在被写回,那么会被设置写回标记,这时候该页就被上锁,其他写请求被阻塞直到锁释放。