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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值