Unix标准IO文件流及缓冲类型

Unix标准IO文件流

文件IO相关函数的一节中,我们所有的I/O函数都是围绕着文件描述符来操作的,当打开一个文件的时候,即返回一个文件描述符,然后该文件描述符用于后续的文件操作。而对于标准IO库,对于文件的操作都是围绕这 文件流 file stream进行的。当我们使用标准IO库打开或创建一个文件的时候,我们已经使一个流和一个文件进行关联。

文件流

由于历史原因,C语言中原来表示流的数据结构是FILE,而不是叫做流。由于大多数的库函数使用到了FILE类型,有的时候在使用FILE指针的时候也叫其为流,这导致后来很多数据把FILE和流搞得十分混乱。实际上流就是标准IO库中程序与文件交互的一种方式。

标准IO函数fopen打开一个文件时返回一个指向FILE对象的指针,该对象通常是一个结构,它包含了标准IO库为管理该流所需要的所有信息,包括该文件的文件描述符,用于指向该流缓冲区的指针,缓冲区的长度,当前缓冲区中的字符数以及出错标志等等

标准输入,标准输出以及标准错误

标准库中对于每一个进程都预定义了三个流,分别是stdin,stdout以及stderr,他们分别对应与Linux文件IO中的STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO。它们的定义在stdio.h中

I/O文件流的缓冲类型

标准IO提供缓冲的目的是为了通过减少使用read和write调用的次数来提高IO读写的效率,它对每个IO流自动的进行缓冲处理,从而避免了用户程序在使用read和write需要考虑的这一点。

标准IO流提供了三种缓冲。分别是全缓冲(fully buffering),行缓冲(line Buffering)以及无缓冲(nonBuffering)。

全缓冲

在使用全缓冲的情况下,当数据填满整个缓冲区之后才进行实际的IO操作。对于驻留在磁盘上的文件的读写通常是使用全缓冲。通常如果不给文件流指定缓冲区的情况下,标准IO函数会首先调用malloc函数获取所需要的缓冲区。

行缓冲

在使用行缓冲的情况下,每当输入输出遇到换行或者缓冲区满了的情况下才会进行实际的IO操作,当涉及到终端输入输出的时候通常使用行缓冲。

对于行缓冲有两个限制。1.由于接收行缓冲的缓冲区的长度是固定的,所以只要填满了缓冲区,即使还没有遇到换行符,也会进行IO操作。2.任何时候,只要通过IO库要求从一个不带缓冲的流或者一个行缓冲的流得到输入数据,那么就会冲洗所有缓冲输出流。

###不带缓冲
此时标准IO库不对字符进行缓冲存储。这就使得输入流要求IO立即进行,如标准错误流,若果出现错误,会立马输出。

flush一个流即刷新缓冲区有两个含义。

  • 在IO库方面,flush意味着将缓冲区中的内容写到磁盘上,该缓冲区可能还没有满
  • 在终端驱动方面表示丢弃已经存储在缓冲区中的内容。

##标准文件流与缓冲类型之间的关系

  • 当标准输入输出指向的是交互式设备(如终端)的时候,它们是行缓冲的,若不是则是全缓冲的。
  • 标准错误永远是无缓冲的。

与缓冲相关的函数

我们可以通过一下两个函数对将缓冲关闭或者改变缓冲的类型。其中这些函数应该在流被打开之后调用,而且也应该在对流进行一切操作之前调用。

1
2
3
#include <stdio.h>
void setbuf(FILE* restrict fd, char* restrict buf);
int setvbuf(FILE* restrict fd, char* restrict buf, int mode, size_t size);

使用setbuf函数打开或者关闭缓冲,当buf是一个有效缓冲区时,此时缓冲打开,若流指向的是终端设备,则此时该流是行缓冲的,否则该流是全缓冲的;当buf为NULL的时候,表示关闭该缓冲。

使用setvbuf可以精确的说明缓冲的类型,这里是使用mode来说明的,mode的值包括以下几个:

  • _IOFBF 全缓冲
  • _IOLBUF 行缓冲
  • _IONBUF 无缓冲

如果指定一个不带缓冲的流,则忽略buf和size参数。如果指定缓冲,则buf和size分别指定一个缓冲区域和缓冲区域的长度。若此时buf为NULL,则标准IO库将自动制定一个适合长度的缓冲区。

上述函数与缓冲之间的关系

函数 mode buf 缓冲区及长度 缓冲类型
setbuf 非空 长度为size的缓冲区buf 全缓冲或行缓冲
setbuf NULL 无缓冲区 不带缓冲
setvbuf _IOFBF 非空 长度为size的缓冲区buf 全缓冲
setvbuf _IOFBF NULL 合适长度的缓冲区buf 全缓冲
setvbuf _IOLBF 非空 长度为size的缓冲区buf 行缓冲
setvbuf _IOLBF NULL 合适长度的缓冲区buf 行缓冲
setvbuf _IONBF 忽略 无缓冲区 不带缓冲

我们还可以通过fflush强制冲洗一个流,此函数使该流所有未写的数据都被传送到内核。作为一种特殊的情况,当流的NULL时,所有的流将被冲洗:

1
2
#include <stdio.h>
int fflush(FILE* fd);