在三个层级上完整理解 IO

前言

这已经不知是第几次试图学习和理解 IO 了,但总是因为缺乏足够的实践而遗忘,希望这次记录以后,能够改善这种局面。

IO 基础知识

IO 在计算机中的意思是「输入和输出:Input 和 Output」,具体是指发生在主存外部设备之间的数据传输也就是数据拷贝行为。

在 Unix 系统中,一切外部设备都是文件,包括磁盘,终端,网络等,都统一抽象为文件。

然后,Unix 为所有文件提供统一的读写接口,很自然,读写之前要打开,读写结束之后要关闭

在 Unix IO 之上,各种编程语言都会提供在 Unix IO 基础上封装好的更为高级的 IO 接口。一般情况下,高级别 IO 就足够用了,但总还有需要 Unix IO 的时候:

IO 中的文件打开、关闭与读写操作

首先,再来说说文件。文件就是m 个字节序列,仅此而已。通过一个文件名,我们可以打开一个文件。这个工作由系统内核来完成,即使在高级别 IO 中,依然如此。在成功打开一个文件后,系统内核会返回一个代表文件描述符的整数,一个相应的保存文件其它信息的数据结构,一个代表文件位置的整数。

应用程序在读写时,只需要文件描述符和文件位置即可。

内核会维护一个描述符的池,池中是仍在打开状态的文件,在任何情况下,只要进程结束,进程中打开的文件都会被关闭。关闭就是将相应描述符移出描述符的池,并销毁内核中保存着相应文件信息的数据。

通过文件描述符和文件位置,可以通过指定文件位置和字节大小来进行读写操作。在进行读操作时,文件位置抵达文件字节之外,会触发一个称为 end-of-file(EOF)的条件,应用程序可以检测到这个条件,从而终止读操作。值得注意的是,EOF 只是一个条件,并没有这样一个字符。

相应地,写操作就是在指定文件位置处写入(拷贝进)指定数据之后,再更新文件位置,从而可以不断地写。

注意,文件描述符最好尽快关闭,且可以重复关闭(同一个进程下同一线程可以,多线程下会出问题),不会报错。

读和写文件

应用程序分别调用 read 和 write 函数来执行输入和输出的。

C 语言下的函数参数为文件描述符整数,位置指针(读操作为空指针),字节数,返回值为一个有符号整数,-1 时代表出错,这使得 read 操作一次最大从 4 G 缩减为 2 G。

通过调用 lseek 函数,应用程序可以显式修改当前文件的位置。

某些情况下,read 和 write 传送的字节比应用程序要求的要少。这些不足值不表示有错误。出现这种情况的原因如下:

实际上,除了 EOF,在读磁盘文件时,将不会遇到不足值,而且在写磁盘文件时,也不会遇到不足值。

处理不足值最频繁的场景是 Web 服务器这样的网络应用,为了保证应用的健壮和可靠性,就必须反复调用 read 和 write 处理不足值,直到所需的字节都传送完毕。

RIO

健壮(robust) IO,分为无缓冲和有缓冲两种版本。

读取文件元数据

调用 stat 和 fstat 函数,可以查看文件具体信息,例如文件大小和类型。其中 stat 参数为文件名,fstat 函数参数为文件描述符。对内核而言,文件类型基本只有三类:

共享文件

要想搞清楚内核是如何共享文件的,就要先理解内核中用来表示打开文件的三个数据结构:

进程间共享文件的方式通过这三张表来看就比较容易了:每个进程都可以有自己独立的文件描述符,同时每个文件描述符可以指向各自的文件表,文件表又和 包含文件信息的 v-node 表关联,而文件表和 v-node 进程间共享,所以文件就可以在进程间共享。

再换言之,就是:虽然各个进程拥有独立的文件入口(描述符),但描述符指向的具体文件表和 v-node 表却可以在进程间共享,也就是文件的位置、类型和大小可以在进程间共享。进程有的,只是一个看上去独立存在的文件描述符。

IO 重定向

还是利用上述三个表,重定向时,只需要将文件描述符重定向即可。一旦文件描述符重定向,原来的文件表因为引用计数为零,也会被内核自动销毁。同时,内核通过文件描述符新的指向关系,可以将相应数据的读写行为也进行重定向。

标准 IO

标准 IO 指的是 C 语言定义的一组高级 IO 函数,称为标准 IO 库。

标准 IO 库将文件描述符和应用级缓冲区封装抽象为文件流。这样可以既可以满足用户一系列琐碎的 IO 操作的同时,降低调用内核 Unix IO 的次数,从而降低开销。

因为缓冲区的存在,也就有了 fflush 函数刷新缓冲区。

IO 函数的选择

绝大多数情况下,只需要使用标准 IO 或者相应程序语言提供的高级 IO 函数即可。

然而,例外总是存在。对网络文件的 IO 操作,就最好使用 RIO。

这是由于标准 IO 和 网络 socket 文件之间存在矛盾:

总结

再次鼓起勇气整理 IO 相关的内容,发现内容好多。。。

值得着重理解的关键概念:


不知是该恭喜,还是该怎样,总之阅读到该文的,你是第 人。每一次刷新,都是不同的自己。