I/O简述
一、 缓冲区
缓冲区,以及缓冲区如何工作,是所有 I/O 的基础。“输入/输出”也就是把数据移进或移出缓冲区。
进程执行I/O操作,归结起来,也就是向操作系统发出请求,让它要么把缓冲区里的数据排干(写),要么用数据把缓冲区填满(读)。进程使用这一机制处理所有数据进出操作。如图1.1描述了数据从外部磁盘向运行中的进程的内存区域移动的过程。进程使用read( )系统调用,要求其缓冲区被填满。内核随即向磁盘控制硬件发出命令,要求其从磁盘读取数据。磁盘控制器把数据直接写入内核内存缓冲区,这一步通过DMA完成,无需主CPU协助。一旦磁盘控制器把缓冲区装满,内核即把数据从内核空间的临时缓冲区拷贝到进程执行read( )调用时指定的缓冲区。
图1.1 I/O 缓冲区操作简图
当进程请求I/O操作的时候,它执行一个系统调用(有时称为陷阱)将控制权移交给内核。当内核以这种方式被调用,它随即采取任何必要步骤,找到进程所需数据,并把数据传送到用户空间内的指定缓冲区。内核试图对数据进行高速缓存或预读取,因此进程所需数据可能已经在内核空间里了。如果是这样,该数据只需简单地拷贝出来即可。如果数据不在内核空间,则 进程被挂起,内核着手把数据读进内存。
从我们的视角看,把数据从内核空间拷贝到用户空间似乎有些多余,直接让磁盘控制器把数据送到用户空间的缓冲区效率不是更高?其原因是:首先,硬件通常不能直接访问用户空间。其次,像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而用户进程请求的可能是任意大小的或非对齐的数据块。在数据往来于用户空间与存储设备的过程中,内核负责数据的分解、再组合工作,因此充当着中间人的角色。
二、虚拟内存
虚拟内存使用虚假(或虚拟)地址取代物理(硬件 RAM)内存地址。1.1中提到,设备控制器不能通过DMA直接存储到用户空间,但通过把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,这样DMA硬件(只能访问物理内存地址)就可以填充对内核与用户空间进程同时可见的缓冲区。这样做可以带来两个明显的优点:一个以上的虚拟地址可指向同一个物理内存地址;虚拟内存空间可大于实际可用的硬件内存。
图1.2 内存空间多重映射
这样做的前提条件是,内核与用户缓冲区必须 使用相同的页对齐,缓冲区的大小还必须是磁盘控制器块大小(通常为512字节磁盘扇区)的倍数。操作系统把内存地址空间划分为页,即固定大小的字节组。内存页的大小总是磁盘块大小的倍数,通常为2次幂(简化寻址操作)。典型的内存页为1024、2048 和 4096 字节。虚拟和物理内存页的大小总是相同的。图1.3显示了来自多个虚拟地址的虚拟内存页是如何映射到物理内存的。
图1.3 内存页
三、文件I/O
文件I/O属文件系统范畴。磁盘把数据存在扇区上,通常一个扇区512字节。磁盘属硬件设备,对何谓文件一无所知,它只是提供了一系列数据存取窗口。在这点上,磁盘扇区与内存页颇有相似之处:都是统一大小,都可作为大的数组被访问。
文件系统是更高层次的抽象,是安排、解释磁盘(或其他随机存取块设备)数据的一种独特方式。而代码几乎无一例外地要与文件系统打交道,而不是直接与磁盘打交道,是文件系统定义了文件名、路径、文件、文件属性等抽象概念。
所有I/O都是通过请求页面调度完成的,页面调度是非常底层的操作,仅发生于磁盘扇区与内存页之间的直接传输,而文件I/O则可以任意大小、任意定位。如何将底层的页面调度转换为文件I/O?
文件系统把一连串大小一致的数据块组织到一起。有些块存储元信息,如空闲块、目录、索引等的映射,有些包含文件数据。单个文件的元信息描述了哪些块包含文件数据、数据在哪里结束、最后一次更新是什么时候,等等。
当用户进程请求读取文件数据时,文件系统需要确定数据具体在磁盘什么位置,然后着手把相关磁盘扇区读进内存。老式的操作系统往往直接向磁盘驱动器发布命令,要求其读取所需磁盘扇区。而采用分页技术的现代操作系统则利用请求页面调度取得所需数据。操作系统还有个页的概念,其大小或者与基本内存页一致,或者是其倍数。典型的操作系统页从2048到8192字节不等,且始终是基本内存页大小的倍数。
四、内存映射文件
传统的文件I/O是通过用户进程发布read( )和write( )系统调用来传输数据的。为了在内核空间的文件系统页与用户空间的内存区之间移动数据,一次以上的拷贝操作几乎总是免不了的。因为在文件系统页与用户缓冲区之间往往没有一一对应关系。但是大多数操作系统都支持一种特殊类型的I/O操作,允许用户进程最大限度地利用面向页的系统I/O特性,并完全摒弃缓冲区拷贝,即内存映射I/O,如图1.4。
图1.4 用户内存到文件系统页的映射
内存映射I/O使用文件系统建立从用户空间直到可用文件系统页的虚拟内存映射,其优点是:
(1)用户进程把文件数据当作内存,所以无需发布 read( )或 write( )系统调用。
(2)当用户进程碰触到映射内存空间,页错误会自动产生,从而将文件数据从磁盘读进内存。如果用户修改了映射内存空间,相关页会自动标记为脏,随后刷新到磁盘,文件得到更新。
(3)操作系统的虚拟内存子系统会对页进行智能高速缓存,自动根据系统负载进行内存管理。
(4)数据总是按页对齐的,无需执行缓冲区拷贝。
(5)大型文件使用映射,无需耗费大量内存,即可进行数据拷贝。
五、流I/O
I/O有面向块的,也有流I/O,其原理模仿了通道。I/O字节流必须顺序存取,如打印机端口、网络连接。
流的传输一般(也不必然如此)比块设备慢,经常用于间歇性输入。多数操作系统允许把流置于非块模式,这样进程可以查看流上是否有输入,即便当时没有也不影响它干别的。这样一种能力使得进程可以在有输入的时候进行处理,输入流闲置的时候执行其他功能。