Lecture 4. File & IO
POSIX idea: Everything is a File.
-
File Sys 的抽象
a. File: 文件系统中的一组命名数据集合,拥有一套代表数据的字节流,和表示自身的元数据(例如修改时间,权限,大小,拥有者等等)
b. Directory:代表了一个文件地址(例如/home/lrefrain/CS162/Pintos),它的内部包括若干文件和目录 -
任何一个进程都有一个当前工作目录 (current working directory CWD),可以使用./来访问 CWD,../访问 CWD 的父目录
-
I/O 调用层次
-
High-Level File APIs
前面带 f 前缀的基本都是 High-Level File API,这些 API 主要对流进行操作,流 (stream) 是一组拥有 position 指针(代表当前在流中的位置)的未格式化的字节序列。
下图是一些其他的以 f 开头的文件操作 API
-
stdin,stdout,stderr
这三个是程序启动时默认拥有的三个文件指针,其文件描述符分别为 0, 1, 2。 -
fseek
不同于上述用于读写的 High-Level File APIs,fseek 的修改并不针对于 stream 的字节序列本身,而是针对于流的另一个参数 position,fseek 可以以指定方式调整 File 指针指向内容的 position 的位置,SEEK_SET 就是 position = offset,SEEK_END 是 position = length - offset,SEEK_CUR 就是 position += offset -
High-Level APIs 的效率提升
用户使用时希望以字节方式读取数据,而硬盘对数据的组织方式是按块(页)的方式,数据使用方式和数据组织方式之间的差异性,会导致性能的损耗。
考虑每次用户调用并获取一个字节的数据,而内核需要将所读取字节所在的硬盘的页整个读入到内核中并返回该字节的数据,这造成了巨量的时间浪费。
因此内核采用了缓冲区,在用户空间的缓冲区中存储一定数量的流的信息(也即 position 所处位置的流上下文),当读取完缓冲区内所有元素后,缓冲区再一次性从硬盘中获取信息放入缓冲区。
写数据也是类似的 -
文件描述符 File Descriptor
文件描述符就是一个 int 整形,kernel 中对于每个进程都专门设有一个文件描述符和打开的文件的对应表。
使用文件描述符的原因是,出于安全性考虑,可以让进程无法直接触碰文件,只能通过系统调用来和 kernel 沟通,以 kernel 指定形式进行文件操作。 -
Low-High Level 转换
可以使用途中两个函数对 FILE *和文件描述符做对换。 -
Low-Level File APIs
其实就是 open,read,close,write,功能也类似于带 f 前缀的,主要的区别是不带缓冲区,基本上是直接对 syscall 的封装。 -
Fork & File Descriptor
Fork 会复制所有的文件描述符和文件描述符表,父子进程中的文件描述符副本可以操作同一个打开文件描述符。然而在内核中对同一打开文件描述符的文件描述符副本数量是有计数器的,因此只有每个副本都调用了 close(fd) 后才会真正关闭打开文件描述符。入图,父子进程轮流操作三次文件描述符之后,打开文件描述符的 Position 变为了 300。
一个重要的示例,复制之后 0,1,2 对应的打开文件描述符 stdin,stdout,stderr 都会被复制,这就导致同一终端的输入和输出对应到了两个进程上,这往往会出错,因此我们可以选择关闭父子进程中的某一个进程中的读入文件描述符,以达到我们期望的效果。 -
dup & dup2
文件描述符的复制,dup 会得到传入文件描述符的副本文件描述符,dup2 会得到指定数字的副本文件描述符,他们都指向同一个打开文件描述符。





















