14.1 引言
*高级I/O包括非阻塞I/O、记录锁、系统V流机制、I/O多路转换(select和poll函数)、readv和writev函数以及存储映射I/O(mmap)
14.2 非阻塞I/O
*非阻塞I/O使我们可以调用open、read和write这样的I/O操作,并使这些操作不会永远阻塞
14.3 记录锁
*记录锁(record locking)的功能是:当一个进程正在读或修改文件的某个部分时,它可以组织其他进程修改同一文件区
*考虑数据库访问例程库。如果该库中所以函数都以一致的方法处理记录锁,则称使用这些函数访问数据库的任何进程集为合作进程(cooperating process)。
*建议性锁并不能阻止对数据库文件有写权限的任何其他进程对数据库文件进行随意的写操作
*强制性锁使内核对每一个open、read和write系统调用都进行检查,检查调用进程对正在访问的文件是否违背了某一把锁的作用。强制性锁有时也被称为强迫方式锁(enforcement-mode locking)
*如果一个进程试图读、写一个强制性锁起作用的文件,而欲读、写的部分又由其他进程加上了读或写锁,此时会发生什么呢?对这一问题的回答取决于三方面的因素:操作类型(read或write),其他进程保有的锁的类型(读锁或写锁),以及有关描述符是阻塞还是非阻塞的
14.4 STREAMS
*流在用户进程和设备驱动程序之间提供了一条全双工通路。流无需和实际硬件会话,流也可以用来构造伪设备驱动程序
*任意数量的处理模块可以压入流。我们使用术语压入,是因为每一新模块总是插到流首之下,而将以前的模块下压(这类似于后进先出的栈)
*ioctl的第二个参数request说明执行哪一个操作。所有request都以I_开始。第三个参数的作用与request有关,有时它是一个整数值,有时它是指向一个整型或一个数据结构的指针
14.5 I/O多路转接
*问题:telnet进程有两个输入、两个输出。对这两个输入中的任一个都不能使用阻塞read,因为我们永远不知道哪一个输入有我们需要的数据
*一种方法是仍旧使用一个进程执行该程序,但使用非阻塞I/O读取数据。基本方法是将两个输入描述符都设置为非阻塞的,对第一个描述符发一个read。如果该输入上有数据,则读数据并处理它;如果无数据可读,则read立即返回。然后对第二个描述符作同样的处理。
*一种比较好的技术是I/O多路转换(I/O multiplexing)。先构造一张有关描述符的列表,然后调用一个函数,直到这些描述符中的一个已准备好进行I/O时,该函数才返回。在返回时,它告诉进程哪些描述符已准备好可以进行I/O
14.6 异步I/O
*除了调用ioctl说明产生SIGPOLL信号的条件以外,还应为该信号建立信号处理程序。对于SIGPOLL的默认动作是终止该进程,所以应当在调用ioctl之前建立信号处理程序
14.7 readv和writev函数
*readv和writev函数用于在一次函数调用中读、写多个非连续缓冲区。有时也将这两个函数称为散布读(scatter read)和聚集写(gather write)
14.8 readn和writen函数
*管道、FIFO以及某些设备,特别是终端、网络和STREAMS设备有下列两种性质:
(1)一次read操作所返回的数据可能少于所要求的数据,即使还没达到文件尾端也可能是这样。这不是一个错误,应当继续改设备
(2)一次write操作的返回值也可能少于指定的字节
14.9 存储映射I/O
*存储映射I/O(Memory-mapped I/O)使一个磁盘文件与存储空间中的一个缓冲区相映射
*mmap函数实现就告诉内核将一个文件映射到一个存储区域中
*void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);
*addr参数用于指定映射存储区的起始地址
*len是映射字节数
*prot参数说明对映射存储区的保护要求。对指定映射存储区的保护要求不能超过文件open模式访问权限
*flag参数影响映射存储区的多种属性
*filedes指定要被映射文件的描述符
*off是要映射字节在文件中的起始偏移量