第十章 系统级I/O
-------------------------------------------------------------------------------------------------------------------------------------
I/O
一个Unix文件就是一个m个字节的序列:B0,B1,.......,Bk,......,Bm-1
打开文件:一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备。
内核返回一个小的非负整数,叫做描述符,它在后继对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。应用程序只需记住这个标识符。
Unix外壳创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0)、标准输出(1)和标准错误(2)。
改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0。
这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek操作,显示地设置文件的当前位置为k。
读写文件:一个读操作就是从文件拷贝n>0个字节到存储器,从当前文件位置k开始,然后将k增加到k+n。
给定一个大小为m字节的文件,当k》m时执行读操作会触发一个称为end-of-file(EOF)的条件,应用程序能检测到这个条件。在文件的结尾处并没有明确的“EOF符号”。
类似地写操作就是从存储器拷贝n>0个字节到一个文件,从当前文件位置k开始,然后更新k。
关闭文件:当应用完成了对文件的访问之后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。
无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放他们的存储器资源。
打开关闭文件
open函数:打开一个已存在的文件或者创建一个新文件
此函数将filename转换为一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。flags参数指明了进程打算如何访问这个文件。
O_RDONLY只读
O_WRONLY只写
O_RDWR可读可写
书中例子:fd = Open("foo.txt",O_RDONLY, 0)
flags参数也可以是一个或者更多位掩码的或,为写提供给一些额外的指示:
O_CREAT如果文件不存在,就创建它的一个截断的(空)文件
O_TRUNC如果文件已经存在,就截断它
O_APPEND在每次写操作前,设置文件位置到文件的结尾处。
书中例子:fd = Open("foo.txt",O_WDONLY|O_APPEND, 0)
mode参数指定了新文件的访问权限位。
读写文件文件
调用read和write函数进行输入和输出的。
通过调用lseek函数,应用程序能够显示地修改当前文件的位置,这部分内容不在我们的讨论范围之内。
用RIO包健壮地读写
1、无缓冲的输入输出函数
调用rio_readn和rio_writen函数,应用程序可以在存储器和文件之间直接传送数据。
2、带缓冲的输入函数
一个文本行就是一个由换行符结尾的ASCLL码字符序列。
调用一个包装函数,他从一个内部读缓冲区拷贝一个文本行,当缓冲区边空时,会自动地调用read重新填满缓冲区。
读取文件元数据
调用stat和fstat函数,检索到关于文件的信息(也称为文件的元数据)
stat函数以一个文件名作为输入,并填写其数据结构中的各个成员。
fstat函数是相似的,只不过是以文件描述符而不是文件名作为输入。
共享文件
描述符表:每个进程都有它独立的描述符表,它的表项是由进程打开的文件描述符来索引的。每个打开的描述符表项指向文件表中的一个表项。
文件表:打开文件的集合是由一张文件表来表示的,所有进程共享这张表.
V-NODE表:同文件表一样,所有的进程共享这张V-NODE表。每个表项包含stat结构中的大多数信息。
I/O重定向
Unix外壳提供了I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来。
dup2函数:拷贝描述符表表项oidfd到描述符表表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开了,dup2会在拷贝oldfd之前关闭newfd。
标准I/O
ANSI c定义了一组高级的输入输出函数 称为I/O库
提供了打开和关闭函数:fopen和oclose
读和写字节的函数:fread和fwrite
读和写字符串:fgets和fputs
复杂的格式化的I/O函数:scanf和printf
--------------------------------------------------------------
参考资料:
深入理解计算机操作系统
视频