1、I/O有哪些?
输入/输出是在主存和外部设备之间拷贝数据的过程。下图是本文讨论的各种I/O之间的关系。Unix I/O模型是在操作系统内核中实现的,较高级的I/O都是基于Unix I/O函数来实现的。
2、Unix I/O
一个Linux文件就是一个由多个字节组成的序列,所有的I/O设别都被模型化为文件,所有的输入和输出都被当作对相应的文件进行读和写来执行。这种允许Linux内核引出一个简单的接口,以映射为文件的方式,叫做UnixI/O。这种方式使得所有的输入和输出都能以一种统一且一致的方式来执行。
<1>打开和关闭文件
进程通过open函数来打开一个已存在的文件或者创建一个新文件。
int open(char *filename,int flags,mode_t mode); 返回:若成功则为新文件描述符;出错则为-1 |
flags参数指明了进程打算如何访问这个文件,分为只读、只写、可读可写。
mode参数指定了新文件的访问权限位。如下图所示。
进程通过调用close函数关闭一个打开的文件。
int close(int fd); 返回:若成功则为0,否则为-1 |
<2>读写文件
PS:ssize_t和size_t的区别
在x86_64系统中,size_t被定义为unsigned long,而ssize_t被定义为long。
3、用RIO包健壮的读写
RIO(Robust I/O)会自动的处理上面所述的不足值。它提供两种类型的函数:无缓冲的和带缓冲的。
<1>无缓冲的输入输出函数
这些函数直接在内存和文件直接按传送数据,没有应用级缓冲。他们对将二进制数据读写到网络和从网络读写二进制数据尤其重要。
rio_readn函数从描述符fd的当前文件位置最多传送n个字节到内存位置usrbuf。类似地,rio_writen函数从位置usrbuf传送n个字节到描述符fd。Rio_read函数在遇到EOF时只能返回一个不足值。rio_writen函数决不会返回不足值。
<2>带缓冲的输入函数
每打开一个描述符都会调用一次rio_readinitb函数,它将描述符fd和地址rp处的一个类型为rio_t的读缓冲区联系起来。
rio_readlineb函数从文件rp读出一个文本行(包括结尾的换行符),将它复制到内存位置usrbuf,并且用NULL(零)字符来结束这个文本行。rio readlineb函数最多读maxlen-1个字节,余下的一个字符留给结尾的NULL字符。超过maxlen-1字节的文本行被截断,并用一个NULL字符结束。
rio_readnb函数从文件rp最多读n个字节到内存位置usrbuf。对同一描述符,对rio_readlineb和rio_readnb的调用可以任意交叉进行。然而,对这些带缓冲的函数的调用却不应和无缓冲的rio_readn函数交叉使用。
4、读取文件元数据
应用程序可以通过调用stat和fstat函数,检索到关于文件的元信息。
5、共享文件
内核用三个数据结构来表示打开的文件:
描述符表:每个进程都有它独立的描述符表,它的表项是由进程打开的文件描述符来索引的。每个打开的描述符表项指向文件表中的一个表项。
文件表:打开文件的集合是由一张文件表来表示的,所有的进程共享这张表。每个文件表的表项组成包括当前的文件位置、引用计数以及一个指向v-node表中对应表项的指针。关闭一个描述符会减少相应文件表表项中的引用计数。内核不会删除设个文件表表项,指导它的引用计数为零。
v-node表:同文件表一样,所有的进程共享这张v-node表。每个表项包含stat结构中的大多数信息,包括st_mode和st_size成员。
6、关于标准I/O