CSAPP读书笔记-chap10-系统级I/O

44 阅读4分钟

输入/输出(I/O)是在主存(memory)和外部设备之间拷贝数据的过程。输入数据是从I/O设备拷贝数据到主存,输出数据是从主存拷贝数据到I/O设备。

Unix I/O

所有的的I/O设备都模型化为文件,而所有的输入和输出都当作相应文件的读和写来执行。

所有的输入输出都以一种统一且一致的方式来执行:

  • 打开文件
  • 改变当前的文件位置
  • 读写文件
  • 关闭文件

文件

每个Linux文件都有一个类型,主要有文件(file)、目录(directory)、套接字(socket)三种类型,其他还有命名管道、符号链接、块设备等。文件系统目录层级呈现树状结构。

打开和关闭文件

open函数将filename转换为一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符

读和写文件

read函数从描述符为fd的当前文件位置拷贝至多n个字节到存储器位置buf。

write 函数从存储器位置拷贝至多n个字节到描述符fd的当前文件位置

通过调用lseek函数,应用程序能够显式地修改当前文件的位置

读写过程中会出现计数不足(short count)的情况,原因有:遇到EOF;从终端读取文本行,一次只能读一行;网络传输,读写套接字的时候,由于缓冲区大小和网络延迟原因会出现不足。

读取文件元信息

利用statfstat函数提取元信息

截屏2023-02-25 12.37.05.png

读取目录内容

opendir函数返回目录流指针,流是一组目录条目的抽象

readdir函数返回下一个目录条目(directory entry)的指针

struct dirent {
    ino_t d_ino; /* inode number, aka file location */
    char d_name[256]; /* Filename */
};

closedir函数关闭流,释放资源

共享文件

可以用许多不同的方式来共享 Unix 文件。内核用三个相关的数据结构来表示打开的文件:

  • 描述符表(descriptor table)。每个进程都有它独立的描述符表,它的表项是由进程打开的文件描述符来索引的。每个打开的描述符表指向文件表中的一个表项。
  • 文件表(file table)。打开文件的集合是由一张文件表来表示的,所有的进程共享这张表。每个文件表的表项包括当前的文件位置、引用计数(reference count),以及一个指向 v-node 表中对应表项的指针。
  • v-node 表(v-node table)。同文件表一样,所有的进程共享这张表。每个表项包含 stat 结构中的大多数信息。

多个描述符也可以通过不同的文件表表项来引用同一个文件。这种情况的主要应用场景是,打开同一文件,但是读取不同的offset位置。

子进程继承父进程打开文件

I/O重定向

I/O重定向(>输出 & <输入)可以将标准输入输出与磁盘文件相关联,例如将ls的结果输出到文件中可以用ls > foo.txt,重定向的原理是借助dup2函数,改变文件描述符descriptor所指向的文件表条目file table entry。

我们该使用哪些I/O函数

Unix I/O属于操作系统内核。一般我们只使用语言或者语言框架封装好的接口函数进行I/O操作。例如C中绝大多数情况都只考虑使用Standard I/O。

小结

Unix 提供了少量的系统级函数,它们允许应用程序打开、关闭、读和写文件,提取文件的元数据,以及执行 I/O 重定向。 Unix 的读和写操作会出现不足值,应用程序必须能正确地预计和处理这种情况。应用程序不应直接调用 Unix I/O 函数,而应该使用 RIO 包,RIO 包通过反复执行读写操作,直到传送完所有的请求数据,自动处理不足值。

Unix 内核使用三个相关的数据结构来表示打开的文件。描述符表中的表项指向打开文件表中的表项,而打开文件表中的表项又指向 v-node 表中的表项。

标准 I/O 库是基于 Unix I/O 实现的,并提供了一组强大的高级 I/O 例程。对于大多数应用程序而言,标准 I/O 更简单,是优于 Unix I/O 的选择。然而,因为对标准 I/O 和网络文件的一些相互不兼容的限制,Unix I/O 比标准 I/O 更适用于网络应用程序。