GuangchaoSun's Blog

第10章 系统级I/O

学习目标

  • 理解Unix I/O的设计与应用
  • 理解输入输出重定向的实现机制

Unix I/O

一个 Unix 文件就是一个 m 字节的序列。所有的 I/O 设备,如网络,磁盘和终端,都被模型化为文件。这种将设备优雅地映射为文件的方式,允许 Unix 内核引出一个简单、低级的应用接口,称为UnixI/O。这使得所有的输入输出都用同一种方式来进行:

  • 打开文件
    • 一个应用程序通过内核打开文件
    • 内核返回一个小的非负整数,叫做描述符
    • 内核记录这个文件的所有信息,应用程序记录描述符
  • 改变文件的当前位置
    • 对于打开的文件,内核保持一个文件位置k,初始为0,字节偏移量
    • 应用程序可以通过 seek 操作,显式地设置文件的当前位置为 k
  • 读写文件
    • 一个读操作就是从文件拷贝 n>0 个字节到存储器,从当前文件位置k开始,然后将k增加到 k+n。
    • 给定一个字节大小为 m 的文件,当 K≥m 时执行读操作会触发一个称为 end-of-file(EOF) 的条件。
  • 关闭文件
    • 当应用完成访问这个文件后,会通知内核关闭这个文件
    • 内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中

一次一个字节地从标准输入拷贝到标准输出:

1
2
3
4
5
6
7
8
#include "csapp.h"
int main(void)
{
char c;
while(Read(STDIN_FILENO, &c, 1) != 0)
Write(STDOUT_FILENO, &c, 1);
exit(0);
}

用RIO包健壮地读写

RIO(Robust I/O)提供了两类函数:

  • 无缓冲的输入输出函数
    • 直接在存储器和文件之间传送数据,没有应用级缓冲。它们对将二进制数读写到网络和从网络读写二进制数据尤其有用。
  • 带缓冲的输入输出函数

RIO无缓冲的输入输出函数:

1
2
3
4
#include "csapp.h"
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);

rio_readn函数描述从描述符 fd 的当前文件位置最多传送 n 个字节到存储器位置usrbuf。在遇到 EOF 时只能返回一个不足值。
rio_writen函数从位置usrbuf传送 n 个字节到描述符 fd。

重定向

使用fork:子进程实际上是会继承父进程打开的文件的。在fork之后,子进程实际上和父进程的指向是一样的,这里需要注意的是会把引用进程加1,如下图所示:

重定向的实现:只需要使用dup2(oldfd,newfd)即可。我们只需要改变文件描述符指向的文件,也就完成了重定向过程,下图我们把指向中端的描述符指向了磁盘文件,也就把终端上的输出保存在了文件中: