• printf缓冲区踩坑


    问题

    碰到了这样一段代码(经过简化的):

    #include "stdio.h"
    #include "unistd.h"
    #include "sys/wait.h"
    
    int main(){
        fork();
        printf("1\n");
        fork();
        printf("1\n");
        wait(NULL);
        return 0;
    }
    

    这里我们简单算一下, 结果会打印几个1嘞?

    • 进程数: 2, line: 6
    • 进程数: 2, line: 7 打印数: 2
    • 进程数: 4, line: 8
    • 进程数: 4, line: 9 打印数: 4
    • 共计打印6次

    看一下结果gcc main.c && ./a.out , 确实是6个.

    但是, 到这并没有完, 若将printf中的\n去掉, 将结果在一行显示, 就会看到一些不同的内容了:

    image-20220514173406606

    结果竟然有8个? 这这这, 多出来的两个是哪来的嘞?

    揭秘

    我们将printf的数字差异化, 可能就有眉目了.

    int main(){
        fork();
        printf("1");
        fork();
        printf("2");
        wait(NULL);
        return 0;
    }
    

    image-20220514173602873

    其中数字1, 在我们分析时应该是只打印2次, 但是却打印了4次. 二者的唯一差异就是\n.

    经过查证, 发现是printf的缓冲区捣的鬼. 简单来说, printf在调用的时候, 为了提高效率, 并不会立刻将内容输出, 而是先放到缓冲区, 那么什么时候输出呢?

    • 标准输出时为line buffer. 既行缓冲, 当碰到\n时输出
    • 重定向时为full buffer. 当缓冲区满了输出, 一般为1kb

    有没有发现什么是与我们这个问题相关的? line buffer啊, 这不就是是否添加\n的差别么.

    现在应该可以回答, 为什么去掉\n时, 输出了8个数字了, 当时的状态如下:

    image-20220514175544748

    输出了8个的原因, 就是printf将内容写入到了缓冲区中, 而在fork的时候带着缓冲区一起复制了. 真相大白

    扩展

    刷新缓冲器

    既然知道是缓冲区搞的鬼, 有没有办法在fork之前清掉缓冲区呢? 有的:

    #include "stdio.h"
    #include "unistd.h"
    #include "sys/wait.h"
    
    int main(){
        fork();
        printf("1");
      	// 刷新缓冲区
        fflush(stdout);
        fork();
        printf("2");
        fflush(stdout);
        wait(NULL);
        return 0;
    }
    

    这样做的时候, 再fork之前将缓冲区内容输出并清空, fork时缓冲区中没有数据, 就没问题啦.

    修改缓冲区大小

    既然前面发生问题是因为缓冲区, 那么能不能将缓冲区关掉呢? 在输出的时候不进行缓冲不就没问题了么? 确实可以

    #include "stdio.h"
    #include "unistd.h"
    #include "sys/wait.h"
    
    int main(){
        // 将标准输出的缓冲区关闭
        setbuf(stdout, NULL);
        fork();
        printf("1");
        fork();
        printf("2");
        wait(NULL);
        return 0;
    }
    

    full buffer

    还记得在查资料的时候, 不光有line buffer, 还碰到了一个full buffer. 是在重定向的时候使用的.

    之前说, 这段代码直接运行时没有问题的, 正常输出了6个. 是因为缓冲区使用了line buffer, 每次碰到换行都会刷新缓冲区.

    #include "stdio.h"
    #include "unistd.h"
    #include "sys/wait.h"
    int main(){
        fork();
        printf("1\n");
        fork();
        printf("2\n");
        wait(NULL);
        return 0;
    }
    

    那么, 如果说不刷新缓冲区, 也就是换成所谓的full buffer, 不是也会有问题么? 既然重定向结果时为full buffer, 那重定向一下试试咯:

    ./a.out > a.log

    查看结构, 确实与预期相同.


    原文链接: https://hujingnb.com/archives/780

  • 相关阅读:
    关于时间
    ELK日志平台
    Java web项目
    阳光下的挣扎
    Windows Server 2008 R2无法连接无线网络的解决方法
    像进度条的网页加载Loading JS代码
    JAVASCRIPT网页上下切换的打开特效
    霓虹灯文字代码 JS网页特效
    Js代码动态移动层-拖动浮层并关闭
    JS网页特效:星空飞入效果
  • 原文地址:https://www.cnblogs.com/hujingnb/p/16270900.html
Copyright © 2020-2023  润新知