概念简介
输入输出I/O是在主存和外部设备(如磁盘,网络和终端)之间拷贝数据的过程。
输入就是从I/O设备拷贝数据到贮存,而输出就是从主存拷贝数据到I/O设备。
所有语言的运行时系统都提供执行I/O的较高级别的工具。例如,ANSI C提供标准I/O库,包含像printf和scanf这样执行带缓冲区的I/O函数。C++语言用它的重载操作符<<(输出)和>>(输入)提供了类似的功能。在UNIX系统中,是通过使用由内核提供的系统级Unix I/O函数来实现这些比较高级的I/O函数的。
打开和关闭文件
进程是通过调用open函数来打开一个已存在的文件或者创建一个新文件。
flags参数表示进程打算如何访问这个文件,它的值包括
- O_RDONLY
- O_WRONLY
- O_RDWR
flags参数也可以是一个或者更多位掩码的或,提供一些额外的指示:
- O_CREAT
- O_TRUNC:如果文件已经存在,就截断它。
- O_APPEND
读和写文件
应用程序是通过分别调用系统函数 read和write函数来执行输入和输出的。
size_t是作为usigned int,而ssize_t是作为int。
在某些情况下,read和write传送的字节比应用程序要求的要少。出现这种情况的可能的原因有:
- 读时遇到EOF?? 假设该文件从当前文件位置开始只含有20个字节,而应用程序要求我们以50个字节的片进行读取,这样一来,这个read的返回的值是20,在此之后的read则返回0.
- 从终端读文本行? 如果打开的文件是与终端相关联的,那么每个read函数将一次传送一个文本行,返回的不足值等于文本行的大小。(具体的含义可看我以前的文章,关于缓冲区的)
- 读和写socket??? 如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会导致read和write返回不足值。
RIO的无缓冲的输入输出函数
rio_readn函数从描述符fd的当前文件位置最多传送n个字节到存储器位置usrbuf。类似的,rio_writen函数从位置usrbuf传送n个字节到描述符fd。rio_readn函数在遇到EOF时只能返回一个不足值。rio_writen函数绝不会返回不足值。具体代码如下:
注意:如果rio_readn和rio_writen函数被一个从应用信号处理程序的返回中断,那么每个函数都会手动地重启read或write。
RIO的带缓冲的输入输出函数
一个文本行就是一个由 换行符 结尾的ASCII码字符序列。在Unix系统中,换行符是‘\n’,与ASCII码换行符LF相同,数值为0x0a。假设我们要编写一个程序来计算文本文件中文本行的数量应该如何来实现呢??嘿嘿这个问题,可是我在微软面试的时候,面试官给我出的一道考题。
一种方法是用read函数来一次一个字节地从文件传送到用户存储器,检查每个字节来查找换行符。这种方法的问题就是效率不高,每次取文件中的一个字节都要求陷入内核。
一种更好的方法是调用一个包装函数(rio_readlineb),它从一个内部缓冲区拷贝一个文本行,当缓冲区变空时,会自动的调用read系统调用来重新填满缓冲区。
在带缓冲区的版本中,每打开一个描述符都会调用一次rio_readinitb函数,它将描述符fd和地址rp处的一个类型为rio_t的读缓冲区联系起来。
rio_readinitb函数从文件rp读取一个文本行(包括结尾的换行符),将它拷贝到存储器位置usrbuf,并且用空字符来结束这个文本行。
?
RIO读程序的核心是rio_read函数,rio_read函数可以看成是Unix read函数的带缓冲区的版本。当调用rio_read要求读取n个字节的时候,读缓冲区内有rp->rio_cnt个未读的字节。如果缓冲区为空的时候,就会调用read系统函数去填满缓冲区。这个read调用收到一个不足值的话并不是一个错误,只不过读缓冲区的是填充了一部分。
一旦缓冲区非空,rio_read就从读缓冲区拷贝n和rp->rio_cnt中较小值个字节到用户缓冲区,并返回拷贝字节的数目。
对于应用程序来说,rio_read和系统调用read有着相同的语义。出错时返回-1;在EOF时,返回0;如果要求的字节超过了读缓冲区内未读的字节的数目,它会返回一个不足值。rio_readlineb函数多次调用rio_read函数。每次调用都从读缓冲区返回一个字节,然后检查这个字节是否是结尾的换行符。rio_readlineb函数如下所示:
rio_readlineb函数最多读取(maxlen-1)个字节,余下的一个字节留给结尾的空字符。超过maxlen-1字节的文本行被截断,并用一个空字符结束。