Skip to content

文件描述符层

Unix 接口的一个很酷的方面是,Unix 中的大多数资源都表示为文件,包括设备(如控制台)、管道,当然还有真正的文件。文件描述符层就是实现这种统一性的层。

正如我们在第 1 章中看到的,Xv6 为每个进程提供了自己的打开文件表,或文件描述符。每个打开的文件都由一个 struct file 表示,它是一个 inode 或管道的包装器,外加一个 I/O 偏移量。每次调用 open 都会创建一个新的打开文件(一个新的 struct file):如果多个进程独立打开同一个文件,不同的实例将有不同的 I/O 偏移量。另一方面,一个单独的打开文件(同一个 struct file)可以多次出现在一个进程的文件表中,也可以出现在多个进程的文件表中。如果一个进程使用 open 打开文件,然后使用 dup 创建别名,或使用 fork 与子进程共享,就会发生这种情况。引用计数跟踪对特定打开文件的引用次数。一个文件可以以只读、只写或读写方式打开。readablewritable 字段跟踪这一点。

系统中所有打开的文件都保存在一个全局文件表中,即 ftable。文件表有分配文件的函数(filealloc)、创建重复引用的函数(filedup)、释放引用的函数(fileclose),以及读写数据的函数(filereadfilewrite)。

前三个遵循了现在熟悉的形式。filealloc 扫描文件表,查找一个未被引用的文件(f->ref == 0)并返回一个新的引用;filedup 增加引用计数;而 fileclose 减少它。当一个文件的引用计数达到零时,fileclose 根据类型释放底层的管道或 inode。

函数 filestatfilereadfilewrite 实现了对文件的 statreadwrite 操作。filestat 只允许在 inode 上调用,并调用 statifilereadfilewrite 检查操作是否被打开模式允许,然后将调用传递给管道或 inode 的实现。如果文件代表一个 inode,filereadfilewrite 使用 I/O 偏移量作为操作的偏移量,然后将其推进。管道没有偏移量的概念。回想一下,inode 函数要求调用者处理锁定。inode 锁定的一个方便的副作用是读写偏移量是原子更新的,因此多个进程同时写入同一个文件不会覆盖彼此的数据,尽管它们的写入最终可能会交错。