标准I/O实现了三种类型的用户缓冲,并为开发者提供了接口,可以控制缓冲区类型和大小。

  • 无缓冲(Unbuffered)

    不执行用户缓冲。数据直接提交给内核。因为这种无缓冲模式不支持用户缓冲(用户缓冲一般会带来很多好处),通常很少使用,只有一个例外:标准错误默认是采用无缓冲模式。

  • 行缓冲(Line-buffered)

    缓冲是以行为单位执行。每遇到换行符,缓冲区就会被提交给内核。行缓冲对把流输出到屏幕时很有用,因为输出到屏幕的消息也是通过换行符分隔的。因此,行缓冲是终端的默认缓冲模式,比如标准输出。

  • 块缓冲(Block-buffered) 或 完全缓冲(full buffered)

    缓冲以块为单位执行,每个块是固定的字节数。很适用与处理文件。默认情况下,和文件相关的所有流都是块缓冲模式。

大部分情况下,默认的缓冲模式对于特定场景是最高效的。但是,标准 I/O 还是提供了一个接口,可以修改使用的缓冲模式:

#include <stdio.h>

int setvbuf(FILE *stream, char *buf, int mode, size_t size);

mode 值必须是以下之一:

_IONBF : 无缓冲

_IOLBF : 行缓冲

_IOFBF : 块缓冲

在 _IONBF 无缓冲模式下,会忽略参数 buf 和 size;对于其他模式,buf 可以指向一个 size 字节大小的缓冲空间,标准 I/O 会用它来执行对给定流的缓冲。如果 buf 为空,glibc 会自动分配指定 size 的缓冲区。

setvbuf() 函数必须在打开流后,并在执行任何操作之前被调用。

行缓冲:

 #include <stdio.h>

 int main(int argc, char* argv[])
{
printf("hello world");
while();
return ;
}

执行程序,看到没有输出“hello world”

如果在“hello world”后面加上“\n”,可以看到输出了“hello world”

说明标准输出的缺省模式是行缓冲

行缓冲大小:

 #include <stdio.h>

 int main(int argc, char* argv[])
{
int i = ;
while(i < )
{
printf("h");
i++;
}
while();
return ;
}

如果把1024改成1025,就能看到输出,说明行缓冲的大小是1024字节

无缓冲:

 #include <stdio.h>

 int main(int argc, char* argv[])
{
fprintf(stderr, "hello world");
while();
return ;
}

可以看到“hello world”输出了,表明stderr是无缓冲的

 #include <stdio.h>

 int main(int argc, char* argv[])
{
setvbuf(stdout, NULL, _IONBF, );
fprintf(stdout, "hello world");
while();
return ;
}

可以把 stdout 设置为无缓冲

 #include <stdio.h>

 char buf[];

 int main(int argc, char* argv[])
{
setvbuf(stdout, buf, _IOLBF, );
int i = ;
while(i < )
{
printf("h");
i++;
}
while();
return ;
}

可以改变缓冲区大小

行缓冲的输出条件:遇到换行符“\n”;缓冲区满

 #include <stdio.h>

 char buf[];

 int main(int argc, char* argv[])
{
setvbuf(stdout, buf, _IOLBF, );
int i = ;
while(i < )
{
printf("h");
if (i == ) printf("\n");
i++;
}
while();
return ;
}

在关闭流时,其使用的缓冲区必须存在。一个常见的错误就是把缓冲区定义成某一个作用域中的局部变量,在关闭流之前就退出这个作用域了。

 #include <stdio.h>

 int main(int argc, char* argv[])
{
char buf[BUFSIZ];
setvbuf(stdout, buf, _IOFBF, BUFSIZ);
printf("Arrr!\n");
return ;
/* 'buf' exits scope and is freed, but stdout isn't closed until later */
}

这类错误可以通过两种方式解决:一是在离开作用域之前显示关闭流,二是把 buf 设置为全局变量

 #include <stdio.h>
#include <unistd.h>
#include <string.h>
#define BUF_SIZE 10
char buf[BUF_SIZE]; int myprintf(char* str)
{
if (NULL == str) return;
char *cur = str;
int len = ;
int have_n = ;
while(*cur != '\0') {
len++;
cur++;
if (*cur == '\n') have_n = ;
} int buf_len = strlen(buf);
int len_sum = buf_len + len; if (len_sum >= BUF_SIZE - || have_n) {
write(STDOUT_FILENO, buf, buf_len);
memset(buf, , BUF_SIZE);
write(STDOUT_FILENO, str, len);
}
else {
strncpy(buf, str, len);
buf[BUF_SIZE] = '\0';
}
return ;
} int main(int argc, char* argv[])
{
myprintf("1234\n5678");
//myprintf("9");
return ;
}

标准 I/O 函数在本质上是线程安全的。在每个函数的内部实现中,都关联了一把锁、一个锁计数器,以及持有该锁并打开一个流的线程。每个线程在执行任何 I/O 请求之前,必须首先获得锁而且持有该锁。两个或多个运行在同一流上的线程不会交叉执行标准 I/O 操作,因此,在单个函数调用中,标准 I/O 操作是原子操作。

标准I/O的缓冲的更多相关文章

  1. 标准输入输出 stdio 流缓冲

    **From : http://www.pixelbeat.org/programming/stdio_buffering/** 我发现找出标准流用的是什么缓冲是一件困难的事. 例如下面这个使用uni ...

  2. 对文件 I/O,标准 I/O 的缓冲的理解

    1.标准I/O缓冲区 要理解标准I/O,就要先知道文件I/O的业务逻辑. 下面图示为文件I/O 如执行下面的代码: write(fd, buf2, sizeof(buf2)); 图中 buf:就是bu ...

  3. 标准输入输出 stdio 流缓冲 buffering in standard streams

    From : http://www.pixelbeat.org/programming/stdio_buffering/ 译者:李秋豪 我发现找出标准流用的是什么缓冲是一件困难的事. 例如下面这个使用 ...

  4. 为什么需要标准IO缓冲?

    (转)标准I/O缓冲:全缓冲.行缓冲.无缓冲 标准I/O库提供缓冲的目的是尽可能地减少使用read和write调用的次数.它也对每个I/O流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的 ...

  5. Linux系统编程之IO_缓冲和非缓冲

    下面是一段类似日志记录的代码,已获取通讯的报文内容和当时的环境参数内容,就是创建一个文件,使用标准IO的fopen.fprintf进行输出记录.但是在调试中,刚开始我就傻眼了,文件创建成功了,但是实时 ...

  6. 标准IO

    标准IO由ISO C 标准的IO库,它处理了很多底层细节,比如合适的缓冲大小等等,因此更易于使用,但是也引入了一些其他问题. 流向 标准IO使用FILE对象关联流,流可以是面向宽字节的也可以是面向单字 ...

  7. 带缓冲的IO和不带缓冲的IO

    文件描述符: 文件描述符是一个小的非负整数,是内核用来标识特定进程正在访问的文件 标准输入/输出/出错: shell为每个程序打开了三个文件描述符,STDIN_FILEON,STDOUT_FILEON ...

  8. 套接字和标准I/O缓冲区

    设置标准I/O函数缓冲区的主要目的是为了提高性能.但套接字中的缓冲主要是为了实现TCP协议而设立的.例如,TCP传输中丢失数据时将再次传递,而再次发送数据则意味着在某地保存了数据.存在什么地方呢?套接 ...

  9. 系统编程--标准IO

    1.流和FILE对象 对于国际字符集,一个字符可以由一个以上的字节来表示.标准I/O文件流可以用来操作单字节和多字节(宽,wide)字符集.一个流的方向(orientation)决定了字符是以单字节还 ...

随机推荐

  1. 正则表达式处理BT的html嵌套问题

    在博问里面求教大神,把问题搞定.在此做个记录备份,也给碰到类似问题的园友提供解决思路. 简化的业务场景就是,在页面html标签中的属性中嵌套了html标签,怎么用用正则表达式过滤闭合的html标签(& ...

  2. 题解-bzoj4320 Homework

    Problem bzoj4320 Solution 前置技能:分块+线段树+卡常+一点小小的数学知识 考试时A的 这种题无论怎么处理总有瓶颈,套路分块,设\(k\)以下的插入时直接暴力预处理,查询时直 ...

  3. exiting pxe rom 无法启动

    背景 我这是给人装了多少次机器了,上千次不敢说,几百次是有了.有个奇怪现象,为什么每次总有新的问题呢,极少能一次成功的.除了让我涨了见识,没想到其他的用处.程序员修电脑,搞笑吧,还有找我修洗衣机的,说 ...

  4. ansible笔记(3):ansible模块的基本使用

    ansible笔记():ansible模块的基本使用 在前文的基础上,我们已经知道,当我们使用ansible完成实际任务时,需要依靠ansible的各个模块,比如,我们想要去ping某主机,则需要使用 ...

  5. bootstrap4简单使用和入门03-响应式布局

    响应式布局的原理 页面源码 <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  6. Kubernetes重要概念理解

    Kubernetes重要概念理解 kubernetes是目前最主流的容器编排工具,是下一代分布式架构的王者.2018年的kubernetes第一个版本1.10已经发布.下面整理一下,kubernete ...

  7. 我的大学,我的SPR机器人队

    时间过的真快,我这个在协会呆了好多年的老油条今年都毕业了,在石油大学大学七年几乎三分之二的时间就是在协会度过的.实话说在北京这是我最亲切的地方,这里有我喜欢的各种设备,有亲爱的老师和一起奋斗的队友,在 ...

  8. [HTTP] 基本认证的工作流程

    HTTP的基本认证涉及两个字段,一个是请求字段 Authorization: Authorization: Basic xxx 一个是响应字段 WWW-Authenticate WWW-Authent ...

  9. bootstrap DataTable绑定数据带服务器分页

    <!-- DataTables -->  这两个文件在我的文件夹里面<script src="~/bower_components/datatables.net/js/jq ...

  10. IP保留地址

    保留地址的网络只能在内部进行通信,而不能与其他网络互连.因为本网络中的保留地址同样也可能被其它网络使用,如果进行网络互连,那么寻找路由时就会因为地址的不唯一而出现问题. 但是这些使用保留地址的网络可以 ...