基于流的操作最终会调用read或者write函数进行I/O操作。为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O库函数的次数。

基于流的I/O提供以下3种缓冲:

全 缓冲:直到缓冲区被填满,才调用系统I/O函数。对于读操作来说,直到读入的内容的字节数等于缓冲区大小或者文件已经到达结尾,才进行实际的I/O操作, 将外存文件内容读入缓冲区;对于写操作来说,直到缓冲区被填满,才进行实际的I/O操作,缓冲区内容写到外存文件中。磁盘文件通常是全缓冲的。

行 缓冲:直到遇到换行符'\n',才调用系统I/O库函数。对于读操作来说,遇到换行符'\n'才进行I/O操作,将所读内容读入缓冲区;对于写操作来说, 遇到换行符'\n'才进行I/O操作,将缓冲区内容写到外存中。由于缓冲区的大小是有限的,所以当缓冲区被填满时,即使没有遇到换行符'\n',也同样会 进行实际的I/O操作。标准输入stdin和标准输出stdout默认都是行缓冲的。

无缓冲:没有缓冲区,数据会立即读入或者输出到外存文件和设备上。标准出错stderr是无缓冲的,这样保证错误提示和输出能够及时反馈给用户,供用户排除错误。

以上3种缓冲区分别定义为3个宏,其定义如表21-1所示。

表21-1 缓冲区类型的宏定义

缓冲区类型

定 义 的 宏

全缓冲

_IO_FULL_BUF

行缓冲

_IO_LINE_BUF

无缓冲

_IO_UNBUFFERED

在 使用上表所述的缓冲类型宏时,应将文件流对象中的缓冲区标志与该宏做"与"操作,判断结果是否为0即可知道该缓冲文件流的缓冲区是否属于该类型了。下面实 例演示了得到文件流的缓冲区类型。该程序输出标准输出、标准输入和标准出错3个文件描述符的缓冲区类型、缓冲区大小等信息。

buf.c 输出缓冲区的类型和缓冲区大小

 #include <stdio.h>
int main(void)
{
printf("stdin is ");
if(stdin->_flags & _IO_UNBUFFERED)
printf("unbuffered\n");
else if(stdin->_flags & _IO_LINE_BUF)
printf("line-buffered\n");
else
printf("fully-buffered\n");
printf("buffer size is %d\n", stdin->_IO_buf_end -
stdin->_IO_buf_base);
printf("discriptor is %d\n\n", fileno(stdin)); printf("stdout is ");
if(stdout->_flags & _IO_UNBUFFERED)
printf("unbuffered\n");
else if(stdout->_flags & _IO_LINE_BUF)
printf("line-buffered\n");
else
printf("fully-buffered\n");
printf("buffer size is %d\n", stdout->_IO_buf_end -
stdout->_IO_buf_base);
printf("discriptor is %d\n\n", fileno(stdout)); printf("stderr is ");
if(stderr->_flags & _IO_UNBUFFERED)
printf("unbuffered\n");
else if(stderr->_flags & _IO_LINE_BUF)
printf("line-buffered\n");
else
printf("fully-buffered\n");
printf("buffer size is %d\n", stderr->_IO_buf_end -
stderr->_IO_buf_base);
printf("discriptor is %d\n\n", fileno(stderr));
return ;
}

输出:

stdin is fully-buffered
buffer size is 0
discriptor is 0

stdout is line-buffered
buffer size is 1024
discriptor is 1

stderr is unbuffered
buffer size is 0
discriptor is 2

奇怪,我们不是说stdin是line buffer吗,为什么上面输出的fully-buffer???

看APUE--UNIX环境高级编程有如下内容:

1. 缓冲类型。

标准库提供缓冲是为了减少对read和write的调用。提供的缓冲有三种类型(整理自APUE):

  • 全缓冲。

在这种情况下,实际的I/O操作只有在缓冲区被填满了之后才会进行。对驻留在磁盘上的文件的操作一般是有标准I/O库提供全缓冲。缓冲区一般是在第一次对流进行I/O操作时,由标准I/O函数调用malloc函数分配得到的。

术语flush描述了标准I/O缓冲的写操作。缓冲区可以由标准I/O函数自动flush(例如缓冲区满的时候);或者我们对流调用fflush函数。

  • 行缓冲

在这种情况下,只有在输入/输出中遇到换行符的时候,才会执行实际的I/O操作。这允许我们一次写一个字符,但是只有在写完一行之后才做I/O操作。一般的,涉及到终端的流--例如标注输入(stdin)和标准输出(stdout)--是行缓冲的。

  • 无缓冲

标准I/O库不缓存字符。需要注意的是,标准库不缓存并不意味着操作系统或者设备驱动不缓存。

ISO C要求:

  • 当且仅当不涉及交互设备时,标准输入和标准输出是全缓存的。
  • 标准错误绝对不是全缓存的。

但是,这并没有告诉我们当标准输入/输出在涉及交互设备时,它们是无缓存的还是行缓存的;也没有告诉我们标准错误应该是行缓存的还是无缓存的。不过,大多数实现默认的缓存类型是这样的:

  • 标准错误总是无缓存的。
  • 对于所有的其他流来说,如果它们涉及到交互设备,那么就是行缓存的;否则是全缓存的。

上面的程序stdin没有涉及到输入,所以是全缓存的,如果我们在int main函数之前加入一个:

int a;
  scanf("%d",&a);

那么输出就变成了:

stdin is line-buffered
buffer size is 1024
discriptor is 0

使用重定向后执行该程序如下:

 $./buf < in.txt 1> out.txt 2> err.txt
stdin is full-buffered
buffer size is 4096
discriptor is
 stdout is full-buffered
buffer size is 4096
discriptor is 1
 stderr is unbuffered
"buffer size is 0
discriptor is 2

2. 改变默认缓存类型

可以通过下面的函数改变缓存类型(摘自APUE):

void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);

这些函数必须在流打开之后、但是未对流做任何操作之前被调用(因为每个函数都需要一个有效的文件指针作为第一个参数)。

利用setbuf,可以打开或者关闭缓存。为了打开缓存,buf参数必须一个大小为BUFSIZ的缓存,BUFSIZ是定义在stdio。h中的常量。&amp;lt;&amp;lt;ISO/IEC 9899&amp;gt;&amp;gt;要求:BUFSIZ至少为256。如果要关闭缓存,可以将buf设成NULL。

利用setvbuf,我们可以设定缓存类型。这是通过mode参数指定的。

关于这两个函数,可以看下表(摘自APUE):

Function

mode

buf

Buffer and length

Type of buffering

setbuf

 

non-null

user buf of length BUFSIZ

fully buffered or line buffered

NULL

(no buffer)

unbuffered

setvbuf

_IOLBF

non-null

user buf of length size

fully buffered

NULL

system buffer of appropriate length

_IOFBF

non-null

user buf of length size

line buffered

NULL

system buffer of appropriate length

_IONBF

(ignored)

(no buffer)

unbuffered

需要注意的是:如果在函数内为流分配了自动变量作为缓存,那么在退出之前需要将流关闭。因此最好让系统自己分配缓存,这些缓存在流关闭的时候会自动被释放。

3.如果清理输入缓存

关于这点可以参看comp.lang.c FAQ的Question12.26b:

Q: If fflush won't work, what can I use to flush input?

A: It depends on what you're trying to do. If you're trying to get rid of an unread newline or other unexpected input after calling scanf (see questions 12.18a-12.19), you really need to rewrite or replace the call to scanf (see question 12.20). Alternatively, you can consume the rest of a partially-read line with a simple code fragment like

while((c = getchar()) != '\n' && c != EOF)
/* discard */ ;

(You may also be able to use the curses flushinp function.)

There is no standard way to discard unread characters from a stdio input stream. Some vendors do implement fflush so that fflush(stdin) discards unread characters, although portable programs cannot depend on this. (Some versions of the stdio library implement fpurge or fabort calls which do the same thing, but these aren't standard, either.) Note, too, that flushing stdio input buffers is not necessarily sufficient: unread characters can also accumulate in other, OS-level input buffers. If you're trying to actively discard input (perhaps in anticipation of issuing an unexpected prompt to confirm a destructive action, for which an accidentally-typed ``y'' could be disastrous), you'll have to use a system-specific technique to detect the presence of typed-ahead input; see questions 19.1 and 19.2. Keep in mind that users can become frustrated if you discard input that happened to be typed too quickly.

References: ISO Sec. 7.9.5.2
H&amp;S Sec. 15.2

4. 几点需要注意的地方

  • 对输入流进行fflush操作是无定义的。
  • 无缓存并不意味着一个个的那样处理输入,而是说当操作系统返回它们时,对于标准库函数来说它们是立即可用的。因为还可能有操作系统级甚至是硬件级的缓存,这些并不是setbuf可以控制的。
  • 另外可以参考这里(我就是最先从这里开始看的)。还有这里。我从后面那个链接摘录一些重要的下来:

setbuf() has to do with the delivery of bytes between the
C library FILE* management layer and the OS I/O layer.

Calls to fread(), fgets(), fgetc(), and getchar() work within
whatever FILE* buffered data is available, and when that data
is exhausted, the calls request that the FILE* buffer be refilled
by the system I/O layer.

When full buffering is turned on, that refill operation results in the
FILE* layer requesting that the operating system hand it a full
buffer's worth of data; when buffering is turned off, that
refill operation results in the FILE* layer requesting that the
operating system return a single character.

...setting an input stream to be unbuffered
does NOT tell the operating system to tell the device driver
to go into any kind of "raw" single-character mode. There are
system-specific calls such as ioctl() and tcsetterm() that
control what the device driver will do.

参考:http://www.cppblog.com/lucency/archive/2008/04/07/46419.html

unix 全缓冲、行缓冲、无缓冲的更多相关文章

  1. Go语言中的有缓冲channel和无缓冲channel区别

    Go语言中的有缓冲channel和无缓冲channel区别 结论 ch1:=make(chan int)// 无缓冲 ch2:=make(chan int,1)// 有缓冲 无缓冲: 当向ch1中存值 ...

  2. 标准I/O缓冲:全缓冲、行缓冲、无缓冲

    说明:我仅仅对网络资源进行了整合,方便学习-.- 基于流的操作终于会调用read或者write函数进行I/O操作.为了使程序的执行效率最高,流对象一般会提供缓冲区,以降低调用系统I/O库函数的次数. ...

  3. Unix无缓冲文件操作函数、文件信息查询

    问题描述:         Unix无缓冲文件操作函数.文件信息查询 问题解决:        struct stat 结构体信息: 具体代码: 具体源文件:

  4. 第五篇:使用无缓冲IO函数读写文件

    前言 本文介绍使用无缓冲IO函数进行文件读写. 所谓的无缓冲是指该IO函数通过调用系统调用实现,其实系统调用内部的读写实现也是使用了缓冲技术的. 读写步骤 1. 打开文件 open 函数 2. 读写文 ...

  5. 使用无缓冲IO函数读写文件

    前言 本文介绍使用无缓冲IO函数进行文件读写. 所谓的无缓冲是指该IO函数通过调用系统调用实现,其实系统调用内部的读写实现也是使用了缓冲技术的. 读写步骤 1. 打开文件 open 函数 2. 读写文 ...

  6. go无缓冲通道

    package main import ( "fmt" "math/rand" "sync" "time" ) //wg ...

  7. go之无缓冲channel(通道)和有缓冲channel(通道)

    channel我们先来看一下通道的解释:channel是Go语言中的一个核心类型,可以把它看成管道.并发核心单元通过它就可以发送或者接收数据进行通讯,这在一定程度上又进一步降低了编程的难度.chann ...

  8. 无缓冲文件IO和目录操作

    引言 在后台开发中,对于文件I/O我们通常不使用C语言封装的fopen.fread.fwrite标准I/O,而是直接使用Linux提供的系统调用函数.因为这些系统调用没有使用用户缓冲区,我们直接与内核 ...

  9. Qt组件中的双缓冲无闪烁绘图

      双缓冲绘图在Qt4中,所有的窗口部件默认都使用双缓冲进行绘图.使用双缓冲,可以减轻绘制的闪烁感.在有些情况下,用户要关闭双缓冲,自己管理绘图.下面的语句设置了窗口部件的Qt::WA_PaintOn ...

随机推荐

  1. Ubuntu 12.04下PostgreSQL-9.1安装与配置详解(在线安装) [转]

    说明:       我是用root用户在终端登陆的,如果是非root用户,那在命令前需要加上"sudo",你懂的... 第一步:在Ubuntu下安装Postgresql       ...

  2. dubbo配置约束

    此处主要记录dubbo配置的一些约束规则. 采用官网提供的原文,描述如下: 一.XML配置(官网原文) 以 timeout 为例: 方法级优先,接口级次之,全局配置再次之. 如果级别一样,则消费方优先 ...

  3. ES博客链接

    https://blog.csdn.net/laoyang360/article/details/78290484

  4. .net 编码常见问题

    问题一: windows service 启动时需要用死循环来控制程序,如果不开启异步,死循环会导致windows sevice 程序无法启动,代码情况如下 protected override vo ...

  5. linux中常用压缩与解压命令

    一. tar文件的解压 tar -xvf db.tar 二. zip文件的压缩与解压 压缩 zip -r dest_name.zip directory_to_compress 解压 unzip de ...

  6. smartJS 0.1 API 讲解 - FlowController

    本篇介绍0.1版中最后一个特性,FlowController:同时也对第一版总结一下,因为近两年全部都是在搞前端,都是做一些js框架类的东西,也做了不少有意思的功能,做smartjs对我来说一个是对自 ...

  7. jstat 监控调整GC很好用

    jstat命令使用 jstat命令可以查看堆内存各部分的使用量,以及加载类的数量.命令的格式如下: jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数] 注意:使用的jdk版本是 ...

  8. Java中匿名内部类

    匿名内部类也就是没有名字的内部类 正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写 但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口 实例1:不使用匿名内部类来实现抽象 ...

  9. SQL语法:查询此表有另外一个表没有的数据

    select bei.ExamItem_Code2,*from PeisPatientExamItem ppeijoin PeisPatientFeeItem ppfion ppfi.ID_Patie ...

  10. [LintCode] 用栈实现队列

    class Queue { public: stack<int> stack1; stack<int> stack2; Queue() { // do intializatio ...