第九周学习总结

第十章 系统级I/O

输入/输出(I/O)是在主存和外部设备(如磁盘驱动器、终端和网络)之间拷贝数据的过程。

  • ANSI C提供了标准I/O库——printf和scanf这样带缓冲区的I/O函数。
  • C++中有重载操作符>>(输出)和<<(输入)提供了类似的功能。
  • Unix系统中,是通过使用由内核提供的系统级Unix I/O函数来实现的。

学习系统级I/O,也就是Unix I/O的原因是:

  • 了解Unix I/O将帮助你理解其他的系统概念
  • 有时你除了使用Unix I/O以外别无选择

10.1 Unix I/O

所有的I/O设备,如磁盘,网络,终端,都被模型化为文件。

  • 打开文件。一个应用程序通过要求内核打开相应的文件。文件打开后,应用程序记录描述符,内核记录所有和文件相关的信息。
    • 每个进程开始的时候都有3个打开的文件:标准输入0,标准输出1,标准错误2。
  • 改变当前的文件位置。对于每个打开的文件,内核记录其所有相关的信息,其中一个是文件位置k,初始为0。从文件开头起始的字节偏移量。
  • 读写文件。一个读操作就是从文件拷贝n>0个字节到存储器,从当前文件位置k开始,然后将k增加到k+n。给定的一个大小为m字节的文件,当k>=m时,执行读操作会触发一个称为EOF的条件,应用程序能检测到这个条件。写操作就是从存储器拷贝n>0个字节到一个文件,从当前文件k开始,然后更新k。
  • 关闭文件——应用程序通知内核关闭文件。内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。

10.2 打开和关闭文件

进程通过open函数来打开一个已存在的文件或者创建一个新文件的。

int open(char *filename, int flags, int mode);

flags参数指明了进程打算如何访问这个文件:

  • O_RDONLY:只读
  • O_WRONLY:只写
  • O_RDWR:可读可写
  • O_CREAT:如果文件不存在,就创建它的一个截断的(空)文件。
  • O_TRUNC:如果文件已经存在,就截断它。
  • O_APPEND:在每次写操作前,设置文件位置到文件的结尾处。

当进程通过带某个mode参数的open函数调用来创建一个新文件时,文件的访问权限位被设置为mode & ~umask。

进程通过调用close关闭一个打开的文件。

int close(int fd);

10.3 读和写文件

应用程序通过分别调用read和write函数来执行输入和输出。

ssize_t read(int fd, void *buf, size_t n);
ssize_t write(int fd, const void *buf, size_t n);

read函数调用返回有三种情况:

  • 返回值-1表示一个错误。
  • 返回值0表示EOF。
  • 返回值表示的是实际传送的字节数。

如果打开文件是与终端相关联的,那么每个read函数将一次传送一个文本行,返回的不足值对于文本行的大小。

在某些情况下,read和write传送的字节比应用程序要求的要少。这些不足值不表示有错误。出现这种情况的原因如下:

  • 读时遇到EOF。
  • 从终端读文本行。
  • 读和写网络套接字。

10.4 用RIO包健壮地读写

Robust I/O,RIO提供了两类不同的函数:

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

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

ssize_t rio_readn(int fd, void  *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);

如果rio_readn和rio_writen函数被一个从应用信号处理程序的返回中断,那么每个函数都会手动地重启read或write。

RIO的带缓冲的输入函数:

rio_readnb

void rio_readinitb(rio_t *rp, int fd);
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);

RIO读程序的核心是rio_read的函数。rio_read函数时Unix read函数带缓冲的版本。

10.5 读取文件元数据

文件的元数据,就是关于文件的信息。

int stat(const char *filename, struct stat *buf);
int fstat(int fd, struct stat *buf);

讨论Web服务器时,会需要stat数据结构中的st_mode和st_size成员。st_mode则编码了文件访问许可位和文件类型。

Unix提供的宏指令根据st_mode成员来确定文件的类型。

  • S_ISREG() 普通文件
  • S_ISDIR() 目录文件
  • S_ISSOCK() 套接字

10.6 共享文件

内核用三个相关的数据结构来表示打开的文件:

  • 描述符表。是每个进程都有的。每个条目是由文件描述符索引的,条目值指向文件表中的一项。
  • 文件表。是所有进程共享的。条目包含一个文件的当前位置,引用计数,以及一个指向v-node表中对应表项的指针。
  • v-node表。是所有进程共享的。包含了stat结构中的大多数信息,包括st_mode和st_size成员。

一次open,将在文件表中建立一个新的条目,将在描述符表中建立一个新的描述符指向这个文件表中的条目。

如果已open之后,进程fork了,那么父子进程都拥有相同的描述符内容,指向的也会是文件表中相同的条目,也就是共享了该文件的当前位置。

两次open同一个文件,虽然是同一个文件,但是有两个描述符条目,指向的也是文件表中的两个条目,虽然文件表中的两个条目指向v-node表中的一个条目,但是,文件表两个条目是关键。

10.7 I/O重定向

I/O重定向的一个方式是使用dup2函数。

int dup2(int oldfd, int newfd);

dup2函数拷贝描述符表表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开了,dup2会在拷贝oldfd之前关闭newfd。

10.8 标准I/O

ANSI C定义了一组高级输入输出函数,称为标准I/O库。

标准I/O库将一个打开的文件模型化为一个流。对于程序员而言,一个流就是一个指向FILE类型的结构的指针。

类型为FILE的流是对文件描述符和流缓冲区的抽象。流缓冲区的目的和RIO读缓冲区的一样:就是使开销较高的Unix I/O系统调用的数量尽可能的小。

参考内容:

课本,百度百科。

你可能感兴趣的:(第九周学习总结)