**From : http://www.pixelbeat.org/programming/stdio_buffering/ **
**译者:李秋豪**

我发现找出标准流用的是什么缓冲是一件困难的事。

例如下面这个使用unix shell 管道的例子:

$ command1 | command2

下图显示了shell fork了两个进程并通过一个管道将他们联系起来。在这个连接中移动使用了三个缓冲.

内核中的缓冲区室友pipe系统函数生成的,它的大小取决于操作系统的页大小。我们无法也没必要控制这个缓冲区的大小,因为它会立即转送数据(至少在linux上是这样)。[更新:这个pipe buffer 已经变化为 circular buffers (16 x 4KiB)并且有一个新的 proposed patch 使得它的大小是动态的]

另外两个缓冲是关于流的,为了提高效率,仅仅在第一次使用流的时候申请缓冲区空间。三个标准流(stdin, stdout, stderr)会在几乎所有的unix GNU C程序开始执行自动被创建,新的流也可以被创建用来连接文件、套接字、管道等等。你可以通过控制缓冲策略(无缓冲,行缓冲,满缓冲)来控制数据的读写方法。我使用这个程序来确定标准流的默认缓冲区的特性:

/* Output info about the default buffering parameters
* applied by libc to stdin, stdout and stderr.
* Note the info is sent to stderr, as redirecting it
* makes no difference to its buffering parameters.
* Note gnulib has fbufmode() to make this portable.
*/
#include <stdio_ext.h>
#include <unistd.h>
#include <stdlib.h> FILE* fileno2FILE(int fileno){
switch(fileno) {
case 0: return stdin;
case 1: return stdout;
case 2: return stderr;
default: return NULL;
}
} const char* fileno2name(int fileno){
switch(fileno) {
case 0: return "stdin";
case 1: return "stdout";
case 2: return "stderr";
default: return NULL;
}
} int main(void)
{
if (isatty(0)) {
fprintf(stderr,"Hit Ctrl-d to initialise stdin\n");
} else {
fprintf(stderr,"Initialising stdin\n");
}
char data[4096];
fread(data,sizeof(data),1,stdin);
if (isatty(1)) {
fprintf(stdout,"Initialising stdout\n");
} else {
fprintf(stdout,"Initialising stdout\n");
fprintf(stderr,"Initialising stdout\n");
}
fprintf(stderr,"Initialising stderr\n"); //redundant int i;
for (i=0; i<3; i++) {
fprintf(stderr,"%6s: tty=%d, lb=%d, size=%d\n",
fileno2name(i),
isatty(i),
__flbf(fileno2FILE(i))?1:0,
__fbufsize(fileno2FILE(i)));
}
return EXIT_SUCCESS;
}

默认缓冲策略:

  • stdin总是缓冲的
  • stderr总是无缓冲的
  • 如果stdout是终端的话缓冲是行缓冲的,否则是满缓冲的。(补充一下,GNU里面定义的可交互设备,显然终端是可交互设备)

默认缓冲大小:

  • 缓冲大小只会直接影响缓冲策略
  • 内核的pipe buffer 已经变化为 circular buffers (16 x 4KiB)并且有一个新的 proposed patch 使得它的大小是动态的
  • 如果stdin/stdout 连接的是交互设备那么默认大小是1024,否则是4096

stdio 输出缓冲的问题

现在来考虑一个问题:数据源的信息是间隔发送的,并且接受者希望立即收到新产生的数据。

例如,一个人想要过滤 tcpdump -l 或者 tail -f 的输出等等(注意有一些过滤器比如sort要求一次缓存所有数据到内部,所以这里不能使用)。

考虑下面这个操作,从动态网络日志终端数据中过滤出不一样的IP地址:

$ tail -f access.log | cut -d' ' -f1 | uniq

问题在于,如果按照上面这个命令,我们将不能实时的看到增加的主机IP,示例图如下:

高亮的缓冲区导致了问题的发生。由于该缓存区连接了一个管道缓冲区,他会等到数据达到4096字节后再作为一个块传送给uniq。注意到tail的标准输出也有这个问题,但是tail -f调用会自动清除缓冲区当有新的数据输入时,所以这里不会产生影响( tcpdump -l, grep --line-buffered sed --unbuffered 也是这样)。另外,由于uniq标准输出连接的是一个可交互设备,所以当有新的一行数据到达时也会自动清除缓冲区,不会产生影响。

stdio 输入缓冲问题

正如向stdout一样,stdin也使用缓冲区以增加效率。

如果一个一个字节的读入显然会有更多控制的空间,但是这样是不实际的。

考虑以下命令:

$ printf "one\ntwo\nthree\n" | ( sed 1q ; sed 1q ; sed 1q )
one

(译者注:这里的q是sed流编辑器的退出命令,1q表示当输出到达第一行结束时退出。参考The q or quit command

正如你所见到的,第一个sed进程读取了所有数据,导致后面的sed没办法读入数据。注意仅仅将stdin缓冲区设置为行缓冲是没有用的,因为只有当输出缓冲区被清除的时候才会产生控制效果(译者注,如果没有输出的话,第一个sed还是会”一行一行的把输入数据读完)。以上的sed标准输入都是行缓冲Reading lines from stdin.通常你只能控制一个进程能否从stdin读入数据,或者读入特定规模的数据然后禁止读入。以下是这样的一个例子:

$ printf "one\ntwo\nthree\n" | ( ssh localhost printf 'zero\\n' ; cat )
zero

(译者注:后面的cat命令用于从stdin中读取数据输出到屏幕,防止printf的输出存储在缓冲区中。)

这个远程printf命令并不会从stdin读取数据(译者注:'zero\n'是参数),但是ssh client并不知道这个,所以他会读取前面printf传入的数据即stdin中读取数据。为了告诉ssh远程命令不需要读入数据,可以加上-n这个参数:

$ printf "one\ntwo\nthree\n" | ( ssh -n localhost printf 'zero\n' ; cat )
zero
one
two
three

常见的经历是你想要吧ssh放在后台当你知道远程命令不会读取数据的时候(利于常见的图像化程序),设置ssh client阻止读入数据可以防止远程应用程序停滞。你可以通过-f参数告诉ssh忽略stdin并且fork到后台。例如:ssh -fY localhost xterm(译者注:-Y Enables trusted X11 forwarding)。

stdio 缓冲控制

省略...(关键词:stdbuf, BUF_X_=Y where X = 0 (stdin), 1 (stdout), 2 (stderr) )

标准输入输出 stdio 流缓冲 buffering in standard streams的更多相关文章

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

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

  2. 标准输入输出() & 打印流 &配置文件

    public static void main(String[] args) { //System 类 的 public final static InputStream in = null; // ...

  3. stdio - 标准输入输出库函数

    SYNOPSIS 总览 #include <stdio.h> FILE *stdin; FILE *stdout; FILE *stderr; DESCRIPTION 描述 标注 I/O ...

  4. C语言 流缓冲 Stream Buffering

    From : https://www.gnu.org/software/libc/manual/html_node/Stream-Buffering.html 译者:李秋豪 12.20 流缓冲 通常情 ...

  5. C 标准库 - <stdio.h>

    一般地,在C语言或C++中,会把用来#include的文件的扩展名叫 .h,称其为头文件. #include文件的目的就是把多个编译单元(也就是c或者cpp文件)公用的内容,单独放在一个文件里减少整体 ...

  6. C语言的标准输入输出

    1. 标准输入输出 标准输入.输出主要由缓冲区和操作方法两部分组.缓冲区实际上可以看做内存中的字符串数组,而操作方法主要是指printf.scanf.puts.gets,getcha.putcahr等 ...

  7. C语言 流缓冲

    **From : https://www.gnu.org/software/libc/manual/html_node/Stream-Buffering.html** 12.20 流缓冲 通常情况下, ...

  8. c++流缓冲学习---rdbuf()

    我们使用STL编程的时候有时候会想到把一个流对象指向的内容用另一个流对象来输出,比如想把一个文件的内容输出到显示器上,我们可以用简单的两行代码就可以完成: ifstream infile(" ...

  9. UNIX环境编程学习笔记(13)——文件I/O之标准I/O流

    lienhua342014-09-29 1 标准 I/O 流 之前学习的都是不带缓冲的 I/O 操作函数,直接针对文件描述符的,每调用一次函数可能都会触发一次系统调用,单次调用可能比较快捷.但是,对于 ...

随机推荐

  1. Mol Cell Proteomics. |马臻| psims-一个用于编写HUPO-PSI标准下的mzML和mzIdentML的python库

    大家好,本周分享的是发表在MCP(MOLECULAR&CRLLULAR PROTEOMICS)上的一篇关于质谱数据处理和识别的文章,题目是psims - A Declarative Write ...

  2. 树形DP 洛谷P2014 选课

    洛谷P2014 选课 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门 ...

  3. 原生JS轮播-各种效果的极简实现

    寒假持续摸鱼中~此为老早以前博客的重写,当时还是分开写的,这里汇总重写,正好复习一遍~ 春招我来了! 所有有意思的,一股脑先扔进收藏,然后再也不看哈哈,真是糟糕. 今日事,今日毕,说起来容易. 当时竟 ...

  4. 关于苹果出现NaN的情况----由Date格式解析方式不同引起的Bug

    源于一个工作中遇到的问题:IOS 上时间显示为 NaN,而安卓上时间显示正常. 问题的根源在于 安卓 和 苹果 对于 JS Date 对象的不同解析. 安卓: new Date("2018- ...

  5. nagios部署配置流程

    nagios部署配置流程 nagios服务端:1.部署nagios服务端(参考nagios官方部署文档).nagios-plugin.php.apache2 2.需要在服务端添/etc/hosts中添 ...

  6. 解决tomcat闪退问题

    https://blog.csdn.net/zh2nd/article/details/79068680 转载此博客链接内容,非常感谢博主 本文参考CSDN博主 哈克沃德.的<Tomcat8启动 ...

  7. Silverlight Telerik 学习之主题的设置

    Telerik控件版本:RadControls for Silverlight Q1 2013 当前的Telerik Silverlight 控件支持以下主题 Office Black - 这是默认主 ...

  8. postgresql实现插入数据返回当前的主键ID

    <insert id="addUser" parameterType="com.liupan.user"> <selectKey keyPro ...

  9. I - Defeat the Enemy UVALive - 7146 二分 + 贪心

    https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_probl ...

  10. redis数据类型--个人常规用法

    一.String 1.可以容纳最高512M的value. 2.经常用于通过expire来实现自动过期的一些值,直接通过setex 来. 3.通过incrby的方式,来实现value值得增长. 4.执行 ...