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.mzph.cn/news/585751.shtml

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

相关文章

万界星空低代码云MES-才是工业MES的未来

万界星空科技作为一家在云MES系统的研发、生产自动化方面拥有很多年行业经验的科技型企业,多年来专注于云MES系统的研发与技术支持服务,目前已成为国内知名的智能制造整体解决方案提供商。 近几年,万界星空科技发掘制造行业生产及物流难点、…

Linux防火墙设置查看攻略

inux系统是一种常用的服务器操作系统,它的安全性备受关注。为了保障系统网络的安全linux查看防火墙设置,管理员需要设置防火墙规则。本文将从8个方面详细介绍如何查看Linux防火墙设置。 1.查看当前防火墙状态 在Linux系统中,默认安装iptabl…

图像的颜色及Halcon颜色空间转换transfrom_rgb/trans_to_rgb/create_color_trans lut

图像的颜色及Halcon颜色空间转换 文章目录 图像的颜色及Halcon颜色空间转换一. 图像的色彩空间1. RGB颜色 2. 灰度图像3. HSV/ HSI二. Bayer 图像三. 颜色空间的转换1. trans_from_rgb算子2. trans_to_rgb算子3. create_color_trans_lut算子 图像的颜色能真实地反映人眼所见的真…

挑战Python100题(8)

100+ Python challenging programming exercises 8 Question 71 Please write a program which accepts basic mathematic expression from console and print the evaluation result. 请编写一个从控制台接受基本数学表达式的程序,并打印评估结果。 Example: If the follo…

每日一练:LeeCode-20. 有效的括号(简)【栈】

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有…

pinia--vue3中的状态管理

一、Pinia是什么? Pinia 是 Vue 的存储库,它允许跨组件/页面共享状态。实际上,pinia就是Vuex的升级版,官网也说过,为了尊重原作者,所以取名pinia,而没有取名Vuex,所以大家可以直接将…

Mybatis 日志配置

要查看MyBatis的日志语句,您可以在一个包、一个完全限定的映射器类名、一个命名空间或一个完全限定的语句名称上启用日志记录。 再次强调,如何做这取决于所使用的日志实现。我们将展示如何在SLF4J(Logback)中进行配置。配置日志服务只需添加一个或多个额…

zdppy_api框架快速入门

概述 zdppy_api是一款为了快速开发而生的,基于异步的,使用简单的Python后端API接口开发框架。 本框架的目标是让Python后端开发变得越来越简单,直到发现原来还可以更简单! 一切都是为了提高开发效率!!&…

标题:探索C语言中的While循环结构

各位少年: 在C语言编程中,循环是一种强大的工具,它允许我们重复执行一段代码,直到满足特定的条件为止。其中,while循环是一种常用的循环结构,它以其简洁的语法和灵活的应用场景赢得了程序员们的青睐。下面…

隧道代理HTTP工作原理:一场奇妙的网络魔法表演

嘿,小伙伴们!今天我们要一起探索一个有趣的话题——隧道代理HTTP的工作原理。这不是普通的表演,而是一场奇妙的网络魔法表演! 首先,让我们想象一下,网络世界就像一个大舞台,而我们每个人都是这…

RK3566 Android 11平台上适配YT8512C 100M PHY

RK3566代码之前适配的1000M IC RTL8211F , 现在需要在之前的基础上修改PHY IC 为裕泰的YT8512C ----------------------------------------------------------------------//将1000M 的配置关掉,改为100M 配置,查看RK3566 资料关于以太网的配置即可知道如何修改 #if…

Linux驱动开发简易流程

推荐视频: 正点原子【第四期】手把手教你学 Linux之驱动开发篇 小智-学长嵌入式Linux&Android底层开发入门教程 能力矩阵 基础能力矩阵 熟悉c/c、熟悉数据结构 熟悉linux系统,Shell脚本,Makefile/cmake/mk 文件IO、多线程、竞争、并发…

AutoSAR(基础入门篇)3.1-Autosar中RTE的概述

目录 一、RTE概述 1、什么是RTE 2、RTE的作用 二、RTE对Runnables的运行支撑 1、作为运行环境的主要功能点

Kubernetes 网络架构

大家好,我是升仔 Kubernetes 网络架构概览 Kubernetes 网络架构的设计理念是简化容器间的通信,确保 Pods 间可以无障碍通信,同时对外提供访问服务的机制。这一设计理念贯穿于整个网络架构。 Pod 网络模型 基本概念:在 Kuberne…

深度优先和广度优先

文章目录 前言一、深度和广度的区别二、代码演示1.准备数据,构造树2.深度优先遍历3.广度优先遍历 总结 前言 深度优先和广度优先的区别: 搜索方式不同 。深度优先搜索算法不全部保留结点,扩展完的结点从数据库中弹出删去;广度优先搜索算法需…

oracle-sga-shared_pool

shared pool 缓冲sql语句和执行计划 shared pool由三部分组成 free libray:缓存sql执行计划 row cathe :缓存数据字典 硬解析:1判断语法2判断对象是否存在3有没有权限4 从n个执行方案中选出最优解,生成执行计划,这一…

壮志酬筹>业务被裁>副业转正>收入回正。一个前黑马程序员老师的2023

从年初时的踌躇满志,到年中时整个业务线被砍。全职做前端训练营,四个多月的时间帮助100多名同学拿到了满意的offer,同时也让我的收入重归正轨。仅以这个视频记录我,一个普通程序员的 2023 。 视频版可直接访问 Hello,大…

Linux基础知识-命令

Linux的基础命令: 1.登录及其用户 sudo //使用超管权限(没登录之前); sudo useradd -m //创建一个xx用户; userdel (-r)(-f&a…

基于ElementUI二次封装弹窗组件

效果&#xff1a; 一、自定义内容类型弹窗 <!-- title&#xff1a;对话框的标题confirmLoading&#xff1a;当前是否处于提交中titleCenter&#xff1a;对话框标题居中方式footerCenter&#xff1a;底部按钮的对其方式visible&#xff1a;是否显示弹窗width&#xff1a;设置…

JavaScript:正则表达式

JavaScript&#xff1a;正则表达式 什么是正则表达式正则表达式语法定义正则表达式判断是否有匹配的字符串查找匹配的字符串 正则表达式匹配法则元字符边界符量词字符类 什么是正则表达式 正则表达式用于匹配字符串中字符的组合模式。 正则表达式会依据其自身语法&#xff0c;…