真题考点
考点一:Unix I/O
所有的 I/O 设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许 Linux 内核引出一个简单、低级的应用接口,称为 Unix I/O, 这使得所有的输人和输出都能以一种统一且一致的方式来执行:
打开文件:一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个 I/O 设备。内核返回一个小的非负整数,叫做描述符。Linux shell 创建的每个进程开始时都有三个打开的文件:标准输入(描述符为 0)、标准输出(描述符为 1)和标准错误(描述符为 2)。
考点二:文件操作
1.打开和关闭文件
int open(char *filename, int flags, mode_t mode);
open 函数将 filename 转换为一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。flags 参数指明了进程打算如何访问这个文件
int close(int fd);
进程通过调用 close 函数关闭一个打开的文件。
2.文件的读和写
应用程序是通过分别调用 read 和 write 函数来执行输入和输出的
ssize_t read(int fd, void *buf, size_t n);
read 函数从描述符为 fd 的当前文件位置复制最多 n 个字节到内存位置 buf,返回值一 1
表示一个错误,而返回值 0 表示 EOF。否则,返回值表示的是实际传送的字节数量。
ssize_t write(int fd, const void *buf, size_t n);
write 函数从内存位置 buf 复制至多 n 个字节到描述符 fd 的当前文件位置。
3.不足值
在某些情况下,read 和 write 传送的字节比应用程序要求的要少。这些不足值(short count)不表示有错误。出现这样情况的原因有:
读时遇到 EOF:假设我们准备读一个文件,该文件从当前文件位置开始只含有 20 多个字节,而我们以 50 个字节的片进行读取。这样一来,下一个 read 返回的不足值为 20, 此后的 read将通过返回不足值 0 来发出 EOF 信号。
从终端读文本行:如果打开文件是与终端相关联的(如键盘和显示器), 那么每个 read 函数将一次传送一个文本行,返回的不足值等于文本行的大小。
读和写网络套接字(socket):如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会引起 read 和 write 返回不足值。对 Linux 管道(pipe)调用 read 和 write时,也有可能出现不足值。
实际上,除了 EOF, 当你在读磁盘文件时,将不会遇到不足值(即读文件遇到 EOF 才会产生不足值),而且在写磁盘文件时,也不会遇到不足值。
考点三:共享文件
内核用三个相关的数据结构来表示打开的文件:
描述符表:每个进程都有它独立的描述符表,它的表项是由进程打开的文件描述符来索引的。每个打开的描述符表项指向文件表中的一个表项。
文件表:打开文件的集合是由一张文件表来表示的,所有的进程共享这张表。每个文件表的表项组成(针对我们的目的)包括当前的文件位置、引用计数(reference count)(即当前指向该表项的描述符表项数), 以及一个指向 v-node 表中对应表项的指针。关闭一个描述符会减少相应的文件表表项中的引用计数。内核不会删除这个文件表表项,直到它的引用计数为零。
v-node 表:同文件表一样,所有的进程共享这张 v-node 表。每个表项包含 stat 结构中的大多数信息,包括 st_mode 和 st_size 成员。
考点四:I/O 重定向
int dup2(int oldfd, int newfd);
dup2 函数复制描述符表表项 oldfd 到描述符表表项 newfd,覆盖描述符表表项 newfd 以前的内容。如果 newfd 已经打开了,dup2 会在复制 oldfd 之前关闭 newfd。
例子:dup2(4,l):将标准输出重定向到磁盘文件 foo.txt(fd=4)
考点五:I/O 总结
Unix I/O 模型是在操作系统内核中实现的。应用程序可以通过诸如 Lseek、read、write 和stat 这样的函数来访问 Unix I/O。较高级别的 RIO 和标准 I/O 函数都是基于(使用)Unix I/O函数来实现的。RIO 函数是专为本书开发的 read 和 write 的健壮的包装函数。它们自动处理不足值,并且为读文本行提供一种高效的带缓冲的方法。标准 1/◦函数提供了 Unix I/O 函数的一个更加完整的带缓冲的替代品,包括格式化的 I/O 例程,如 printf 和 scanf。
下面是一些基本的指导原则:
①只要有可能就使用标准 I/O。对磁盘和终端设备 I/O 来说,标准 I/O 函数是首选方法
②不要使用 scanf 或 rio readlineb 来读二进制文件,像 scanf 或 rio readlineb 这样的函数是专门设计来读取文本文件的。
③对网络套接字的 I/O 使用 RIO 函数