输入/输出(I/O)是在主存和外部设备(如磁盘驱动器、终端和网络)之间拷贝数据的过程。
学习系统级I/O,也就是Unix I/O的原因是:
所有的I/O设备,如磁盘,网络,终端,都被模型化为文件。
进程通过open函数来打开一个已存在的文件或者创建一个新文件的。
int open(char *filename, int flags, int mode);
flags参数指明了进程打算如何访问这个文件:
当进程通过带某个mode参数的open函数调用来创建一个新文件时,文件的访问权限位被设置为mode & ~umask。
进程通过调用close关闭一个打开的文件。
int close(int fd);
应用程序通过分别调用read和write函数来执行输入和输出。
ssize_t read(int fd, void *buf, size_t n);
ssize_t write(int fd, const void *buf, size_t n);
read函数调用返回有三种情况:
如果打开文件是与终端相关联的,那么每个read函数将一次传送一个文本行,返回的不足值对于文本行的大小。
在某些情况下,read和write传送的字节比应用程序要求的要少。这些不足值不表示有错误。出现这种情况的原因如下:
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函数带缓冲的版本。
文件的元数据,就是关于文件的信息。
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成员来确定文件的类型。
内核用三个相关的数据结构来表示打开的文件:
一次open,将在文件表中建立一个新的条目,将在描述符表中建立一个新的描述符指向这个文件表中的条目。
如果已open之后,进程fork了,那么父子进程都拥有相同的描述符内容,指向的也会是文件表中相同的条目,也就是共享了该文件的当前位置。
两次open同一个文件,虽然是同一个文件,但是有两个描述符条目,指向的也是文件表中的两个条目,虽然文件表中的两个条目指向v-node表中的一个条目,但是,文件表两个条目是关键。
I/O重定向的一个方式是使用dup2函数。
int dup2(int oldfd, int newfd);
dup2函数拷贝描述符表表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开了,dup2会在拷贝oldfd之前关闭newfd。
ANSI C定义了一组高级输入输出函数,称为标准I/O库。
标准I/O库将一个打开的文件模型化为一个流。对于程序员而言,一个流就是一个指向FILE类型的结构的指针。
类型为FILE的流是对文件描述符和流缓冲区的抽象。流缓冲区的目的和RIO读缓冲区的一样:就是使开销较高的Unix I/O系统调用的数量尽可能的小。
参考内容:
课本,百度百科。