这次东西少,简单介绍一下UNIX的高级I/O吧。
1. 非阻塞I/O
非阻塞I/O使我们可以调用不会永远阻塞的I/O操作,例如open, read和write。如果这种操作不能完成,则立即出错返回,表示该操作如继续执行将继续阻塞下去。
2. 记录锁
一个进程正在读或修改文件的某个部分时,可以阻止其他进程修改同一文件区。
加锁的区域,可以在记录锁的结构体flock中确定。
多个进程在一个给定的字节上可以有一把共享的读锁,但是在一个给定字节上的写锁则只能由一个进程独用。更进一步而言,如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁;如果在一个字节上已经有一把独占性的写锁,则不能再对它加任何读锁。
3. 流
流在用户进程和设备驱动程序之间提供了一条全双工通路。下图是一个具有处理模块的流模型。流的所有输入和输出都基于消息,消息由下列几部分组成:消息类型、可选择的控制信息以及可选择的数据。在流首、各处理模块和设备驱动程序之间,消息可以顺流而下,也可以逆流而上。使用ioctl根据消息的信息可对流执行各种不同的操作。特别注意的是该流不同于file流。
4. I/O多路转接
其基本思想是:先构造一张有关描述符的表,然后调用一个函数,它要到这些描述符中的一个已准备好进行I/O时才返回。在返回时,它告诉进程哪一个描述符已准备好可以进行I/O。I/O多路转接可以防止进程在同时处理多个描述符时遇到阻塞I/O被长时间阻塞。通常用select和poll函数实现。
5. 异步I/O
使用select和poll可以实现异步I/O。关于描述符的状态,系统并不主动告诉我们任何信息,我们需要主动地进行查询(调用select或poll)。当某个描述符所关心的某个事件已经发生,就产生一个信号,用该信号通知进程。
6. 存储映射I/O
存储映射I/O使一个磁盘文件与存储空间中的一个缓存相映射。于是当从缓存中取数据,就相当于读文件中的相应字节。与其类似,将数据存入缓存,则相应字节就自动地写入文件(即指在写入储映射区时由内核虚存算法自动进行)。这样,就可以在不使用read和write的情况下执行I/O。UNIX中,通常由函数mmap实现映射,文件被映射到进程的地址空间中。
一个例子:
对于映射存储区,同样也有属性设置(如读写权限、共享方式等)。同时映射的起始地址addr和文件的偏移量off通常应当是系统虚存页长度的倍数。
在fork之后,子进程继承存储映射区(因为子进程复制父进程地址空间,而存储映射区是该地址空间中的一部分),但是由于同样的理由,exec后的新程序则不继承此存储映射区。进程终止时,或调用了munmap之后,存储映射区就被自动去除。关闭文件描述符filedes并不解除映射区。
使用存储映射I/O的好处是:内核直接对映射存储缓存作I/O操作,速度更快。而在read/write方式,内核要在用户缓存和它自己的缓存之间进行复制,然后用其缓存作I/O;并且存储映射I/O处理的是存储空间而不是读、写一个文件,所以常常可以简化算法。
对我收获最大的是了解了mmap的意义。