Linux之缓冲区的理解

目录

一、问题引入

二、缓冲区

1、什么是缓冲区

2、刷新策略

3、缓冲区由谁提供

4、重看问题

三、缓冲区的简单实现


一、问题引入

我们先来看看下面的代码:我们使用了C语言接口和系统调用接口来进行文件操作。在代码的最后,我们还使用fork函数创建了一个子进程。

 代码运行结果如下:

结果没有什么问题啊?结果很正确。但是我们再来看看下面的操作:我们对其进行输出重定向。然后,查看log.txt的代码。

我们惊奇地发现,文件里面的内容和打印到显示器的内容是不一样的!我们再仔细观察,发现,C语言的函数都打印了两次,而系统调用接口只打印了一次。为什么呢? 

这种现象就和fork函数以及我们下面要讲的缓冲区有关了。

二、缓冲区

1、什么是缓冲区

缓冲区的本质就是一段内存空间。

我们知道,内存的速度比磁盘的速度快了几个数量级。所以数据如果直接从内存写到磁盘,那么访问外设效率比较低,那就太消耗时间了。所以缓冲区的意义就是通过减少与外设的IO次数,来节省进程进行数据IO的时间。

所以C语言中就提供了缓冲区。而有了缓冲区的存在,可以提高整机效率,并提高用户的响应速度。

2、刷新策略

~ 立即刷新。
~ 行刷新(行缓冲)。(常见的对显示器进行数据刷新)以\n为标志
~ 满刷新(全缓冲)。(常见的对磁盘文件写入数据)

特殊情况:1、用户强制刷新(fflush)          2、进程退出

注:所有的设备,永远都倾向于全缓冲,即缓冲区满了才刷新,因为这样只需要更少的IO操作,更少次的外设访问,效率更高。

当然,我们要根据实际情况去改变刷新策略。如:显示器是直接给用户看的,一方面要照顾效率,一方面要照顾用户体验。所以显示器一般使用行刷新。

3、缓冲区由谁提供

从上面的例子,我们发现直接往显示器上打印的结果为4条,往文件打印的结果为7条,这跟缓冲区有关,同时这也说明了缓冲区一定不在Linux内核中,为什么?因为write是系统接口,如果在内核中,write也应该打印两次。所以缓冲区是由C标准库提供的。

我们之前所说的所有缓冲区都指的是用户级语言层面提供的缓冲区。stdout,stdin,stderr对应的类型——FILE*,FILE是一个结构体,里面封装了fd,同时还包括了一个缓冲区。

从源码出发,我们可以来看一看FILE结构体:

4、重看问题

有了缓冲区的概念,我们就来解释解释问题引入中的现象。

首先,我们要先知道,代码运行完了,并不代表数据已经刷新了。上面代码中,使用C语言函数的操作在执行完了后,先将数据写入了缓冲区中,并没有直接向显示器上打印。

第一次运行,没有重定向操作,就是直接向显示器打印,而显示器的刷新策略是行刷新,且每个代码后面都有\n,所以在调用fork之前,代码不仅执行完了,而且数据都已经刷新了。所以fork对结果没有影响。

第二次运行,我们有了重定向操作,于是函数就由向显示器打印变成了向磁盘文件打印。所以刷新策略也由行刷新变成了满刷新。那么\n就已经没有意义了。所以代码在运行到fork时,之前的代码虽然已经运行完成了,但是数据还没有刷新到文件中。数据还在当前进程对应的C标准库中的缓冲区中,且该数据属于父进程。

于是最后,我们fork创建了子进程。接着,父进程或子进程退出,这时数据会强制刷新出来。我们假设父进程先退出:父进程退出后,其数据强制刷新,而刷新的过程也是一种写入,所以这时,为了父子进程的数据不会相互影响,就会发生写时拷贝!这样数据就会有两份,于是父子进程各自退出时都会刷新各自的数据。(当然,如果子进程先退出也是同样的)

所以,简单总结来说:重定向导致刷新策略发生了改变(由行缓冲变成了全缓冲)。同时发生了写时拷贝,父子进程各自刷新。

三、缓冲区的简单实现

有了缓冲区的一些基本概念。我们可以自己实现一个简单的带有缓冲区的struct file。

主函数:

int main
{MyFILE* fp = fopen_("log.txt", "r");if(fp==NULL){printf("open file fail");return 0;}fputs_("hello world", fp);fclose_(fp);return 0;
}

struct file

struct MyFILE_
{int fd;char buff[NUM];int end;//当前缓冲区的结尾
};typedef struct MyFILE_ MyFILE;

 fopen函数的简单实现

MyFILE* fopen_(const char* pathname, const char* mode)
{assert(pathname);assert(mode);MyFILE* fp = NULL;if(strcmp(mode, "w")==0){int fd = open(pathname, O_WRONLY|O_TRUNC|O_CREAT);if(fd>0){MyFILE* fp=(MyFILE*)malloc(sizeof(MyFILE));memset(fp,'\0',sizeof(MyFILE));fp->fd = fd;}}else if(strcmp(mode, "w+")==0){}else if(strcmp(mode,"r")==0){}else if(strcmp(mode,"r+")==0){}else if(strcmp(mode,"a")==0){}else if(strcmp(mode,"a+")==0){}else {}return fp;
}

fputs函数的简单实现

void fputs_(const char* message, MyFILE* fp)
{assert(message);assert(fp);strcpy(fp->buff+fp->end, message);fp->end += strlen(message);if(fp->fd==0){}else if(fp->fd==1){if(fp->buff[fp->end-1]== '\n'){write(fp->fd, fp->buff,fp->end);fp->end = 0;}}else if(fp->fd==2){}else {}
}

fclose函数简单实现和fflush函数

void fclose_(MyFILE* fp)
{assert(fp);fflush_(fp);close(fp->fd);free(fp);
}void fflush_(MyFILE* fp)
{assert(fp);if(fp->end != 0){write(fp->fd, fp->buff, fp->end);syncfs(fp->fd);fp-> end = 0;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/308826.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客网面试题知识点记录-02

1.collection接口 2.在构造方法中调用方法A,若方法A被子类重写,则会先调用子类的方法A。举例如下题: 此时会输出null,调用顺序为:子类初始化,但是子类实现了Base,会先执行Base的构造方法,构造…

【C语言数组传参】规则详解

目录 数组传参介绍 数组传参规则 数组传参的实参 特殊情况一:sizeof(数组名) 特殊情况二:&数组名 数组传参的形参 数组传参使用数组名作为形参接收 形参如果是⼀维数组 形参如果是⼆维数组 数组传参使用指针作为形参…

登峰造极,师出造化,Pytorch人工智能AI图像增强框架ControlNet绘画实践,基于Python3.10

人工智能太疯狂,传统劳动力和内容创作平台被AI枪毙,弃尸尘埃。并非空穴来风,也不是危言耸听,人工智能AI图像增强框架ControlNet正在疯狂地改写绘画艺术的发展进程,你问我绘画行业未来的样子?我只好指着Cont…

nginx源码分析-3

这一章内容讲述nginx中的事件是如何一步步添加到epoll实例中的。 在初始化http连接的函数ngx_http_init_connection中,nginx为http连接初始化了处理请求的回调函数,之后调用ngx_handle_read_event函数对可读数据进行处理。这里只为连接设置read而没有设…

Python生成器 (Generators in Python)

Generators in Python 文章目录 Generators in PythonIntroduction 导言贯穿全文的几句话为什么 Python 有生成器Generator?如何获得生成器Generator?1. 生成器表达式 Generator Expression2. 使用yield定义生成器Generator 更多Generator应用实例表示无…

一文道破Java NIO

文章目录 一、常见的几种 Java IO 工作模式1.1 同步阻塞 IO1.2 同步非阻塞 IO1.3 异步非阻塞 IO 二、Java NIO 多路复用详解2.1 原理图2.2 基础组件简介SelectorChannelSelectionKey 2.3 Java NIO 代码示例2.4 Linux 支持多路复用的系统调用函数select 函数poll 函数epoll 函数…

十二星座女生、谁最拥有当潮 “女神范儿” 排名 。

请点击 → 「链接」 ← 查看! ​​​​​​​ 冠军(天秤座)、亚军(处女座)、季军(巨蟹座) 第四名(双鱼座)、第五名(狮子座)、第六名&…

Python/R/GUI/BI类型常用数据可视化工具

什么是数据可视化工具? 数据可视化工具是指旨在可视化数据的所有形式的软件。它们处理数据输入,将其转换为用户可以根据自己的需求进行定制的视觉效果。 不同的工具可以包含不同的功能,但最基本的是,数据可视化工具提供输入数据集…

Android MVC 写法

前言 Model:负责数据逻辑 View:负责视图逻辑 Controller:负责业务逻辑 持有关系: 1、View 持有 Controller 2、Controller 持有 Model 3、Model 持有 View 辅助工具:ViewBinding 执行流程:View >…

python测试工具: 实现数据源自动核对

测试业务需要: 现有A系统作为下游数据系统,上游系统有A1,A2,A3... 需要将A1,A2,A3...的数据达到某条件后(比如:A1系统销售单提交出库成功)自动触发MQ然后再经过数据清洗落到A系统,并将清洗后数据通过特定…

二叉树的中序遍历,力扣

目录 题目地址: 题目: 解题方法: 解题分析: 解题思路: 代码实现: 注: 代码实现(递归): 代码实现(迭代): 题目地址&#xf…

【Vue】computed详解

✨ 专栏介绍 在当今Web开发领域中,构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架,正是为了满足这些需求而诞生。它采用了MVVM架构模式,并通过数据驱动和组件化的方式,使…