缓冲区的深刻理解

文章探讨了C语言程序中缓冲区的作用,包括提高进程效率的原因,以及缓冲区的刷新策略如立即刷新、行刷新、缓冲区满刷新。通过示例代码展示了当使用fork()创建子进程时,如何因缓冲区的存在导致重定向到文件时输出被复制两次。同时,文章提到了C库函数和操作系统层面的缓冲区差异,以及在不同刷新策略下,如显示器的行刷新和文件的缓冲区满刷新,对程序行为的影响。
摘要由CSDN通过智能技术生成

代码&&现象

先来看一份代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
    //C Library
    printf("hello printf\n");
    fprintf(stdout, "hello fprintf\n");
    const char *s1 = "hello fwrite\n";
    fwrite(s1, strlen(s1), 1, stdout);

    //system call
    const char *s2 = "hello write\n";
    write(1, s2, strlen(s2));
    return 0;
}

image-20230105100421574

#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
    //C Library
    printf("hello printf\n");
    fprintf(stdout, "hello fprintf\n");
    const char *s1 = "hello fwrite\n";
    fwrite(s1, strlen(s1), 1, stdout);

    //system call
    const char *s2 = "hello write\n";
    write(1, s2, strlen(s2));
    
    fork();
    return 0;
}

image-20230105100544331

第二份代码多了一个fork,导致重定向的时候,tag.txt里C库函数的输出进行了两次。

为什么要存在缓冲区?

我们无论往显示器里读写、文件里读写、网卡里读写等等,都是在访问外设,而一旦访问外设速度就很慢。假设不存在缓冲区的话当前进程在进行IO时只能够当前进程去等待,就会导致当前进程的效率降低;而此时如果当前进程不自己等待,而是将需要IO的数据拷贝到缓冲区里,由缓冲区决定IO的时间、次数等,这样就会提高当前进程的效率。

缓冲区的刷新策略

缓冲区的刷新策略分为:

  • 立即刷新——无缓冲
  • 行刷新——行缓冲
  • 缓冲区满刷新——全缓冲

但有两个特例:

  • 用户强制刷新——调用fflush函数
  • 进程退出的时候自动刷新

立即刷新策略一般很少用,毕竟这样就跟没有缓冲区一样,想立即刷新的时候我们可以调用fflush函数。

行刷新是对于显示器(人)来说的,因为人的阅读习惯是一行一行读,所以会专门设定一个行刷新策略。

缓冲区满刷新是效率最高的,因为这样IO的次数最少,也是除了显示器外最常用的策略。

C缓冲区&&OS缓冲区

上面说的缓冲区是以C库函数为背景来说的,但其实OS也有一层缓冲区,我们在调用printf等函数时,会将内容拷贝到C库函数的缓冲区里,然后等该缓冲区里的内容刷新的时候其实是拷贝到了OS的缓冲区里,最终由OS的缓冲区决定IO。

C库函数的缓冲区在FILE结构体里,没错,就是我们用fopen打开文件时返回回来的FILE指针对应的结构体里,该结构体还封装了文件描述符fd等等很多东西。

OS的缓冲区我们无法观察到,因为这个极其复杂,OS需要根据整个机器的内存空间等因素综合考虑再决定IO。

原理

这里其实是有一个缓冲区的存在,而根据上面的现象,其实可以锁定缓冲区一定在C库函数的包装中,而不在系统调用函数中,因为C库函数都是调用系统调用函数的。

在未进行重定向时,C库函数和OS函数都往显示器里打印,而显示器采用的是行刷新策略,而我们的代码也添加了\n,所以当每次执行输出代码的时候总会立即刷新到显示器里,最终fork的时候C库缓冲区已经是空的了,而OS函数根本没有往C库缓冲区里拷贝,而是拷贝到了OS缓冲区里。

当进行重定向的时候,是往文件里写入,采用的刷新策略为缓冲区满刷新,所以即使我们的代码里有\n,也不会立即写入到文件里,所以fork的时候,C库缓冲区里是有我们的内容的,子进程会拷贝父进程的PCB,父子进程指向同一份数据代码,当有任意一个进程退出时,这时该进程就会刷新缓冲区,而这时就会发生写时拷贝,刷新的内容会拷贝一份再进行刷新,所以最终会刷新两次,但是OS函数没有往C库缓冲区里拷贝,所以也就不参与上面的过程,所以只刷新一次。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云朵c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值