Unix标准IO文件流及缓冲类型
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意味着将缓冲区中的内容写到磁盘上,该缓冲区可能还没有满
- 在终端驱动方面表示丢弃已经存储在缓冲区中的内容。
##标准文件流与缓冲类型之间的关系
- 当标准输入输出指向的是交互式设备(如终端)的时候,它们是行缓冲的,若不是则是全缓冲的。
- 标准错误永远是无缓冲的。
与缓冲相关的函数
我们可以通过一下两个函数对将缓冲关闭或者改变缓冲的类型。其中这些函数应该在流被打开之后调用,而且也应该在对流进行一切操作之前调用。
|
|
使用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时,所有的流将被冲洗: