标签:
第十章系统级I/O
输入/输出(I/O)是主存和外部设备(I/O设备)(如磁盘驱动器、终端、网络)之间拷贝数据的过程。输入是从I/O设备拷贝到主存。反之则反。
学习Unix I/O的原因:
帮助理解其他的系统概念。
有时只能使用Unix I/O。例如:读取文件元数据(文件大小和创建时间)。另外,使用标准I/O库进行网编程非常冒险。
10.1Unix I/O
Unix文件就是一个m字节的序列:b0,b1,b2….bm-1。所有的I/O设备都被虚拟化为文件。所有的输入输出都是在当成相对应的文件的读写。将设备映射为文件,Unix内核引出一个应用接口,Unix I/O。
输入输出的执行方式:
打开文件:
打开文件,内核会返回描述符。标准输入(STDIN_FILENO)描述符为0、标准输出(STDOUT_FILENO)描述符为1、标准错误(STDERR_FILENO)描述符为2。
改变当前文件位置:
文件位置k,是文件开头起始的字节偏移量。
读写文件:
读是从文件拷贝到存储器。写相反。当k超过文件字节数m时,会触发end-of-file(EOF)条件。
关闭文件:
释放文件打开时创建的数据结构(释放文件的存储器资源),将描述符恢复到可用的描述符池中。
10.2打开和关闭文件
open函数的返回值是描述符。
filename文件路径字符串。
flag参数指明进程打算怎么访问这个文件。flag参数的值:
mode参数指定了新文件的访问权限位。mode参数的值:
10.3读和写文件
read 执行输入(读)
write执行输出(写)
read 函数:从fd的当前文件位置 拷贝 最多n个字节到存储器位置 buf。
返回值-1表示错误,0表示EOF,否则表示实际传送的字节数。
write 同理。
不足值:read和write传送的字节数少于应用程序的要求。
读时遇到了EOF
从终端读文本行:如果打开的文件是与终端相联的,那么每个read函数 将一次传送一个文本行,不足值=文本行大小。
读和写网络套接字
10.4用RIO包健壮地读写
RIO包能自动地处理不足值。提供了:无缓冲的输入输出函数,带缓冲的输入函数。
10.4.1RIO的无缓冲的输入输出函数
rio_readn函数从fd拷贝最多n字节到usrbuf。rio_writen也一样。rio_readn遇到EOF时只返回一个不足值,rio_writen绝不会返回不足值。对同一描述符,无缓冲的两个函数可以交叉调用
当errno==EINTR时,表示函数被一个应用信号处理程序的返回中断了,则本次读的字节为0,重新使用read函数接着读入。
10.4.2RIO的带缓冲的输入函数
文本行就是一个由换行符结尾的ASCII码字符序列。换行符数字值为0x0a.rio_readlineb函数从内部读缓冲区拷贝一个文本行,当缓冲区为空时,会自动地调用read重新填满缓冲区。rio_readn带缓冲区的版本:rio_readnb。
rio_readinitb函数将fd和rp处的一个类型为rio_t的读缓冲区联系起来。
rio_readlineb从rp读出一个文本行(包括换行符)并把它存到usrbuf,并用空字符结束这个文本行。最多读maxlen-1个字节,剩下一个给结尾处的空字符。
rio_readnb最多读n个字节。
对同一描述符,带缓冲的函数之间可以交叉调用。
示例为RIO函数一次一行地从标准输入拷贝一个文本文件到标准输出。
rio_t读的缓冲区结构体。以及初始化它的代码。
rp->rio_cnt为缓冲区未读的字节。出错返回-1,EOF返回0,正常就接着读,直到>0。为0表示缓冲区为空,一旦为空就调用read将其填满。在缓冲区非空时,将n和rp->rio_cnt中的最小值拷贝到usrbuf,并且返回这个最小值。
rio_readlineb函数与rio_read相似,不同之处在于,每次调用都从读缓冲区返回一个字节,然后检查这个字节是否是换行符。
与rio_readn相似。
10.5读取文件元数据
检索文件元数据的方式:调用stat和fstat函数。两者功能相似。
stat数据结构中的成员。重点掌握st_mode,st_size.
st_size:文件字节数大小
st_mode:文件访问许可位和文件类型。(普通文件:二进制文件和文本文件。目录文件:其他文件信息。套接字:通过网络与其他进程通信的文件。)
判断了是三种文件的哪一种。(注意套接字是用“other”表示的)只判断了文件拥有者是否可读。
10.6共享文件
用三个相关的数据结构表示打开的文件:
描述符表:每个打开的描述符表项指向文件表中的一个表项。
文件表:打开的文件的集合是由一张文件表表示的,所有的进程共享这张表。包括文件位置、引用计数(当前指向该表项的描述符表项数),指向v-node表的指针。
v-node表:包含stat结构中大多数信息。
打开文件的内核数据结构
文件共享。共享了同一个磁盘文件
子父进程共享文件。子进程有一个父进程描述表符副本,所以他们共享打开文件的集合。注意,在内核删除相应文件表表项之前,子父进程必须都关闭他们的描述符。
10.7 I/O重定向
拷贝oldfd到newfd,将newfd的内容覆盖掉。如果newfd已经打开了,dup2会在覆盖前将它关闭。
使用dup2函数将描述符1重定向到描述符4。
10.8 标准I/O
标准I/O库:一组高级输入输出函数。将一个打开的文件模型化为一个流,一个流即一个指向FILE类型的结构的指针。每个ANSI C程序开始时都有三个打开的流:stdin(标准输入),stdout(标准输出),stderr(标准错误)。
类型为FILE的流是对文件描述符和流缓冲区的抽象。为了减小系统开销。
10.9 综合:该使用哪些I/O函数
大多数时候使用标准I/O就可以了。
在网络套接字的时候使用RIO函数。需要格式化输出,使用sprintf函数格式化一个字符串,然后用rio_writen把它发送到套接口。格式化输入,使用rio_readlineb读一个完整的文本行,再使用scanf从文本行提取不同字段。
小结:
本章内容相对较少,但是花费的时间一点都没有减少,在阅读代码上花费了较多时间。明显可以从阅读中看出本章与其他章的关联较大,有许多伏笔。本章主要是帮助我们理解系统级的输入输出,Unix I/O,告诉我们虽然我们有一些高级的标准库函数可以使用,但是在一些情况下,比如查看元数据,比如网络套接字时,必须使用系统级I/O。重点在于如何使用RIO包解决不停返回不足值这个问题。
疑问:
在书上p603上rio_read函数中,erro!=EINTR旁边的注释写的是被应用信号处理程序的返回中断,但是p601的rio_readn函数中,erro==EINTR才是被中断,我想了一下,满足这个条件return -1,说明这里是出错,应该是书上p603的注释有误。
参考资料: 《深入理解计算机系统》
标签:
原文地址:http://www.cnblogs.com/angelahxy/p/4947681.html