【Linux基础IO】缓冲区

前言

       在 Linux 系统中,I/O 操作背后有一个关键概念——缓冲区。缓冲区是数据的临时存放的地方,能有效提升 I/O 效率。本文将带您了解 Linux 系统中的缓冲区概念

在这里插入图片描述

1. 缓冲区是什么

 缓冲区是是什么,有谁来提供?

 缓冲区本质就是块内存空间;

缓冲区可以根据层次分为3种:

  • 用户缓冲区:程序员在程序中手动定义的缓冲区,通常使用数组或容器来存储和管理数据。
  • 库缓冲区:编程语言标准库提供的缓冲区,比如: C 标准库中的 printfscanf 等函数,C++ 中的 std::coutstd::cin 也有缓冲机制。
  • 系统缓冲区:由操作系统管理的缓冲区,存在于内核层。比如:文件 I/O 操作时,操作系统会为打开的文件创建系统缓冲区。

 观察现象:

printf("hello world!");
sleep(3);

执行这两条语句会发现,需要程序运行结束后才能刷新出来,而不是理解就打印出来;

 这块内存是怎么来的?

        在Linux中,无论是用户自己的C程序,还是C标准库,以及Linux操作系统,它们都是用C语言写的,我们可以把它们简单理解为就是malloc申请的;

2. 为什么要存在缓冲区?

 其实就是为了提高效率;缓冲的思想可以运用的面很广,是提高效率的有效手段;

或许在一些简单的程序中感受不出来缓冲区的意义,举个例子:

以菜鸟驿站为例,比如:你想送你朋友一本书,但是你身处在杭州,你朋友在北京,自己亲自跑到山东送给他?肯定不是这样效率太低,当然是先把书交给菜鸟驿站,驿站帮你打包送出,当你把书交给驿站后,就可以认为书已经送出(后续的送书任务不需要你做);

当你把书交给快递点后,站点就立即派专车运算吗?这样成本高,那么多快件,每件都单独运送效率也低,他会积攒一些快件,等到一定数量再送;

缓冲区的作用就类似菜鸟驿站,目的就是为了提高效率;

缓冲区有暂存数据的功能,就必定有一定的刷新方式:

  1. 无缓冲(立即刷新)
  2. 行缓冲(行刷新)
  3. 全缓冲(缓冲区满了再刷新)

 特殊情况:强制刷新,进程退出时,一般需要进行缓冲区刷新;

一般的显示器文件,都是行刷新(行缓冲)对于磁盘文件,全缓冲(缓冲写满再刷新)

3. C库的缓冲区

虽然无法通过源码查看,但是可以通过现象来研究一下C库的缓冲区;

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{

    // C库接口
	fprintf(stdout, "C: hello fprintf\n");
	printf("C: hello printf\n");
	fputs("C: hello puts\n", stdout);
	const char* str = "system call: hello write\n";
    
    // 系统调用
	write(1, str, strlen(str));

	fork();
	return 0;
}

 直接运行:

 输出重定向:

 

 现象解释:

  1. 直接向显示器打印时,显示器的刷新方式是行刷新,而代码中所有的字符串输出都有\n,fork之前数据就已经被刷新,包括系统调用;
  2. 重定向到log.txt,本质是向磁盘文件中写入,数据的刷新方式变成了全缓冲;全缓冲意味着缓冲区变大,写入的简单数据不足以把缓冲区写满,fork执行的时候数据依旧在缓冲区中;
  3. 系统调用输出一次,说明目前我们所说的缓冲区,与操作系统是没有关系的(系统调用不受影响);他只能和C语言本身有关
  4. 进程退出时,一般都要刷新缓冲区,即使数据没有满足刷新条件
  5. 进程在缓冲区的数据依然属于进程,fork(创建子进程)执行完之后,立马退出,任意一个进程退出时,刷新缓冲区,就要发生写时拷贝,所以数据就输出了两份;

 梳理一下重定向的流程:

        printf把数据加载到C语言缓冲区就返回了,如果printf频繁的向文件中写入数据,就会导致printf的效率变低,通过缓冲区积攒一部分数据后再写入,可以有效的提高的printf的调用效率 ;

执行结束,进程退出时,通过进程中的文件描述符表1位置文件描述对象的写方法,将数据写到文件缓冲区(从C缓冲区写到OS的工作就叫作刷新);内核负责在适当的时机刷新数据,将缓冲区内容写入磁盘;

 现在再来看看C库中的IO接口:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

FILE *fopen(const char *pathname, const char *mode);

extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;

         都有FILE*类型,FILE是一个结构体,内部封装了文件描述符,现在还可以推出,他内部还包含了缓冲区;

        如果一个进程它同时打开5个文件,那它就会有5个FILE对象,每个FILE对象都会包含一个缓冲区;并且在底层,每个打开的文件都有自己的文件描述对象 struct file,每个文件也都有自己的文件缓冲区;所以在进行这些文件读写时,每个文件都互相不受影响;

 查看一下C库中FILE结构体:

在Centos7下可以查看:

grep -n 'struct _IO_FILE {' /usr/include/libio.h

vim /usr/include/libio.h +246


以上便是本文的全部内容,希望对你有所帮助,最后感谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值