全部博文(842)
分类: 系统运维
2012-05-14 17:23:32
标准I/O库提供的缓冲的目的是使用最少的read和write调用。(回想第3章我们展示了使用各种缓冲的尺寸执行I/O所需要的CPU时间。)此外,它尝试为每个I/O流进行缓冲,消除程序担心它的需要。不幸的是,标准I/O库产生最多混淆的就是缓冲。
有三种缓冲类型被提供:
1、完全缓冲(Fully buffered)在这种情况下,当标准I/O的缓冲被填满后,真实的I/O才会发生。在磁盘上的文件通常都被标准I/O库完全缓冲。这个被使用的缓冲通常被标准I/O库在第一次操作一个流时通过调用malloc得到(7.8节)。
术语“冲洗(flush)”描述了标准I/O缓冲的写操作。一个缓冲可以被标准I/O函数自动冲洗,比如在缓冲满时,或者我们可以调用函数fflush来 冲洗一个流。不幸的是,在UNIX环境里,冲洗有两个意思。在标准I/O库里,它表示缓冲的内容写出,它可能是部分填充的。在终端设备的术语里,比如18 章的tcflush函数,它表示丢弃缓冲里存储的数据。
2、行缓冲(Line buffered)。在这种情况下,标准I/O库在输入输出时碰到一个换行符时会执行I/O操作。这让我们可以一次输出单个字符(使用标准I/O的 fputc函数),因为我们知道只当我们完成了每行的写操作时真实I/O才会发生。行缓冲被典型用在终端的流上:比如标准输入、标准输出。
行缓冲伴随两个警告。第一,标准I/O库用来收集各行的缓冲区大小是固定的,所以当我们在写一个换行符之前填满这个缓冲I/O可能会发生。第二、不管何时 通过标准I/O库从a、一个未缓冲流或b、(需要从内核被请求数据的)一个行缓冲流请求一个输入时,所有行缓冲输出流都会被冲洗。b情况括号内的补充条件 的原因是,请求的数据可能已经在缓冲里了,这时不再需要从内核中读取数据。显然,a情况下任何从未缓冲流的输入,都会要求从内核获取数据。
3、无缓冲(Unbuffered)。标准I/O库不缓冲字符。例如,如果我们用标准I/O函数fputs写15个字符,我们会期望这15个字符尽快地输出,它很可能使用了3.8节的write函数。
例如,标准错误流通常是无缓冲的。这是为了使任何错误消息都能尽快地显示,而不管它们是否包含一个换行符。
ISO C定义了以下缓冲性质:
1、当且仅当它们没有指向交互式的设备,标准输入和标准输出为完全缓冲的。
2、标准错误绝不是完全缓冲的。
然而,这里没有告诉我们如果标准输入和标准输出表示交互式的设备时,它们是无缓冲的还是行缓冲的;又或者标准错误是无缓冲的还是行缓冲的。多数实现默认使用以下的缓冲类型:
1、标准错误总是无缓冲的。
2、所有其它流,如果它们指向一个终端设备,都是行缓冲的;否则,它们是完全缓冲的。
本文讨论的四个平台遵守了这些协定:标准错误是无缓冲的,为终端设备打开的流是行缓冲的,而所有其它流是完全缓冲的。
我们会在5.12节更详细地介绍标准I/O的缓冲。
如果我们不喜欢任何给定流的这些默认行为,我们可以通过调用下面两个函数的其中一个来改变缓冲:
这些函数必须在流打开后(显然,因为它们第一个参数都是一个合法文件指针),但在这个流上执行任何操作之前调用。
通过setbuf,我们可以打开或关闭缓冲。要开启缓冲,buf必须是指向长度为BUFSIZ的缓冲的指针,这个常量定义
在
通过setvbuf,我们精确地指定我们想要的缓冲类型。这通过mode参数来完成:
_IOFBF:完全缓冲的;
_IOLBF:行缓冲的;
_IONBF:无缓冲的。
如果我们指定一个无缓冲的流,buf和size参数会被忽略。如果我们指定完全缓冲或行缓冲,buf和size可以选择性地指定一个缓冲和它的尺寸。如果
流是缓冲的而buf是NULL,标准I/O库会自动使用恰当的尺寸为这个流开辟它自己的缓冲。恰当的尺寸表示由BUFSIZ常量指定的值。
一些C库的实现使用stat结构体的st_blksize成员的值(参考4.2节)来决定优化的标准I/O缓冲大小。我们将在本章的后面部分看到,GNU C库使用了这种方法。
下表总结了这两个函数的行为以及它们的各种选项:
setbuf和setvbuf函数汇总 | ||||
函数 | mode | buf | 缓冲和长度 | 缓冲类型 |
setbuf | 非null | 长度为BUFSIZ的用户buf | 完全缓冲或行缓冲 | |
NULL | (没有缓冲) | 无缓冲的 | ||
setvbuf | _IOFBF | 非null | 长度为size的用户buf | 完全缓冲 |
NULL | 合适长度的系统缓冲 | |||
_IOLBF | 非null | 长度为size的用户buf | 行缓冲 | |
NULL | 合适长度的系统缓冲 | |||
_IONBF | (忽略) | (没有缓冲) | 无缓冲的 |
注意如果我们在一个函数里用一个自动变量(automatic variable)开辟一个标准I/O的缓冲作,我们必须在从函数返回前关闭这个流。(我们将在7.8节更多讨论。)还有,一些实现为内部记账 (internal bookkeeping)而使用这个缓冲的一部分,所以可以存储在这个缓冲的数据的真实字节数要比它的尺寸小。一般来说,我们应该让系统选择缓冲尺寸并自 动地开避这个缓冲。当我们这样做时,在我们关闭这个流时,标准I/O库自动地释放这个缓冲。
任何时候,我们可以强制一个流的冲洗。
这个函数导致流中任何未写的数据都被传递级内核。作为一个特殊情况,如果fp为NULL,函数会冲洗所有的输出流。