Linux - struct file与缓冲区

news2024/11/14 10:55:48

                                                ​​​​​​​             ​​​​​​​ 

                                               感谢各位 点赞 收藏 评论 三连支持

                                               本文章收录于专栏【Linux系统编程】   

                                                     ❀希望能对大家有所帮助❀

                                                       本文章由 风君子吖 原创

        ​​​​​​​        ​​​​​​​        ​​​​​​​               

前言

对于文件,之前我们已经铺垫的大量的知识,知道了C语言提供的文件接口底层是如何实现,知道了系统提供的文件接口函数如何使用,了解了内核对于打开的文件的体系结构,明白了重定向的原理,而本章内容主要是为之前的知识做一个更详细的解释和补充。

struct file

上篇文章我们详细讲解了内核是如何对打开的文件进行管理

686e7a6cb580421e881210a8887ee7cc.png

 而struct file 也主要是存储文件的相关属性和其他的一些内容(比如说文件的IO函数),我们这里也是主要详细讲解这个结构体内部存储的一种我们几乎之前没有用到过的东西 -- 函数指针!

首先,要提出这么一个问题,对于每个硬件,每个硬件的IO方式(函数)是否都是一样的?那要是不一样,Linux又如何通过我们上图的这种体系结构来做到 “万物皆文件”?

那么不同的硬件对应着不同的IO函数,我们可以通过怎样的方式来实现,要知道,这可不是C++,在C++中,类有继承和多态,可以做到不同的对象具有不同的形态。但是这些写操作系统的大佬们就想到了如何用C语言这种面向过程的语言做到了面向对象,通过函数指针来实现,在C语言的结构体中包含一个函数。

 这就说明,从很久以前,那些C语言的大佬们早就想到了面向对象,于是就有了C++。

缓冲区

对于缓冲区,我们之前也接触过,也使用过fflush(C语言库)。

但是我们始终不知道缓冲区为什么存在,存在的意义是什么,如何存在?

先来说说缓冲区为什么而存在(缓冲区的作用):

在之前的我们写的代码有过许多例子,比如说使用printf函数,如果不在字符串末尾加上\n,就无法及时输出结果,只能等待程序运行完才会输出结果,这就是因为缓冲区没有刷新。

对于缓冲区,我们的第一印象就是,他只是用来临时存储一段数据,等到缓冲区刷新时,再给对应的硬件(文件)进行写操作,那么为什么要这么麻烦呢?我就不能直接给硬件进行写操作吗?这不仅仅在某些场景会显得很奇怪,而且我们直接对硬件进行写不是更方便、更直观吗? 

因为你只是在用户层的角度去思考,而对于操作系统,为了提高效率,为了尽量减少与硬件的交互,就可以通过缓冲区来完成。 因为从之前我们学习冯洛伊曼体系来看,硬件的响应速度相对于内存的速度是很缓慢的,有大量的交互就意味着需要大量的时间来等待硬件响应,而缓冲区就是一次性打包一连串的数据给硬件以减少对硬件的交互数量。

而缓冲区也有不同的刷新策略,而最常见的几种刷新策略就是

1.行刷新  :这种刷新策略我们见过的最多的就是printf,对显示器的缓冲区的刷新策略

2.满刷新 :这种刷新策略就是等到缓冲区满了再自动刷新,我们像磁盘文件进行写就是这种刷新策略。

3.用户强制刷新: 这种刷新策略一般是用户调用刷新缓冲区接口来进行刷新,比如fflush。

先来看下面这段代码

 运行结果

[fengjunzi@VM-4-2-centos lesson14_fd]$ ./myfile 
Hello world
hello fprintf
hello write

 输出重定向结果

[fengjunzi@VM-4-2-centos lesson14_fd]$ ./myfile > log.txt 
[fengjunzi@VM-4-2-centos lesson14_fd]$ cat log.txt 
hello write
Hello world
hello fprintf
Hello world
hello fprintf

运行结果没什么问题,但是我们的输出重定向结果却是这样的?hello write不仅先打印出来了,而且其他的两个竟然打印了两次?

这就是因为缓冲区的缘故,先来看看导致的问题可能是哪些? 

1.在代码结束时使用fork创建子进程

2.write是系统接口,printf和fprintf是C语言库提供的接口

首先,对正常运行的过程进行逐步分析:

1.调用printf并且有\n,根据显示器的刷新策略,行刷新遇到\n,直接刷新缓冲区并打印到屏幕上。

2.fprintf与printf同理。

3.调用write系统接口函数给fd为1的文件(显示器)写入数据。

4.调用fork 创建子进程,但是由于以上的函数都已经完成且缓冲区都刷新完毕,所以子进程没有干事。

再对输出重定向的过程进行逐步分析:
1. 输出重定向要先调用dup2 函数,对fd = 1 的文件(显示器)进行被拷贝覆盖为log.txt文件的内容。

2.此时的缓冲区刷新策略不再是行刷新,而是全刷新。

3. 调用printf,但因为是全刷新策略,printf的内容被保存在缓冲区中,没有被刷新,也就没有被写到log.txt文件中。

4.调用fprintf与printf同理。

5.调用write系统接口函数给fd为1的文件(log.txt)写入数据。

6.调用fork 创建子进程,这个时候由于缓冲区有内容,且在子父进程main函数结束时,刷新缓冲区,而在刷新缓存区的时候就会发生 写时拷贝,就会出现子父进程给log.txt分别打印一次数据的现象。

通过这种分析,大家是否明了了呢?并且我们可以从以上结果推断

1. 因为发生的是写时拷贝,所以这个缓冲区必然是C语言库的,而且还是存储在FILE中。

2. write因为是系统接口,没有像printf和fprintf一样要经过FILE的缓冲区,所以会被先写入到log.txt中。需要注意的是,内核也有自己的缓冲区,但是这里不作分析。

总结

本章内容 对之前的内容进行了一个详细的细节补充和缓冲区的概念补充,下篇文章我会针对我们所有所学知识来完成一个自己的FILE和加入到我们自己实现的easy_shell中

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

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

相关文章

DTS迁移Oracle至DM与MySQL至DM

目录 DTS迁移Oracle至DM... 3 一、前期准备... 3 二、DTS迁移... 4 1、新建工程... 5 2、新建迁移... 6 3、填写源库信息,使用指定驱动并自定义URL连接... 7 4、填写目标库信息... 8 5、填写迁移选项... 8 6、指定迁移模式及模式对象... 9 7、执行迁移...…

http长连接与会话保持

"我们半推半就的人生,没有和你一样被眷顾的未来!" 一、Http长连接 (1) 为什么需要长连接 如上展示的是一个常规得并不能再常规的http服务,从本地拉取远端linux上的本地文件上传至浏览器上,经过浏览器的渲染展示成如今的样子。唔&a…

【实战】体验训练Geneface

一.环境 conda activate geneface export PYTHONPATH./ CUDA_VISIBLE_DEVICES0 python tasks/run.py --configegs/datasets/lrs3/lm3d_syncnet.yaml --exp_namelrs3/syncnet 训练这篇出过的一些奇奇怪怪的问题基本上都记录在【环境搭建】40系一些奇奇怪怪的环境问题_weixin_50…

aigc - 文化衫设计

团队要用aigc设计个文化衫,就是给些提示词,然后让ai自动生成能够包含这些提示词的文化衫出来 二、第二版 思路:收集了30多张文化衫,然后用两种方式生成提升词:一个是自然语言描述这件t-short,一个是全名词…

IDEA常用插件Top18

前言:精心推荐给大家的一些日常开发中最常用的IDEA效率插件。 1、Alibaba Java Coding Guidelines代码规范检查工具 Alibaba Java Coding Guidelines ,阿里的一款强大的代码规范检查工具,可以让自己写出易读性更高的代码,可以让团队代码风格…

Keepalived+LVS

这里写目录标题 一、Keepalived及其工作原理1.1Keepalived体系主要模块及其作用1.3VRRP (虚拟路由冗余协议) 二、keepalived实验部署主服务器:备用服务器配置节点 一配置节点二 三、测试 一、Keepalived及其工作原理 Keepalived 是一个基于V…

Linux编译器 gcc、g++

绪论 你热爱生命吗?那幺别浪费时间,由于时间是组成生命的材料。——富兰克林 。 本篇文章写了主要写了Linux下编译器,以及编译器是如何实现编译的过程。 话不多说安全带系好,发车啦(建议电脑观看)。 附&…

2.30 守护进程(1) 2.31 守护进程(2)

2.30 守护进程(1) 终端 echo $$//查看当前终端的的pid tty//查看当前终端设备控制终端可以操作某一个进程。 进程组 会话 进程组、会话、控制终端之间的关系 find/2 查看2重定向到dev/null设备上,|管道(创建子进程&#xff09…

RK3588平台开发系列讲解(USB篇)USB Device端口组合配置过程

文章目录 一、configfs二、configfs 配置过程2.1、使能相关的宏2.2、挂载configfs2.3、创建名为g1的usb复合设备2.4、配置PID和VID2.5、创建并配置strings子目录2.6、创建configuration和字符串2.7、创建functions2.8、将functions和configuration关联起来2.9、绑定到UDC,使能…

数据的存储练习题 -- (解题思路+代码)

目录 前言 知识补充 有符号和无符号的区别 练习一 练习二 练习三 练习四 练习五 练习六 练习七 前言 书接上回,我们学习了整形数据在内存中是怎么存储的。本篇我们就利用这些知识来做一些练习题目,把这些知识很好的消化掉。 知识补充 对…

简单的TCP网络程序·单进程

目录 文件1:tcpServer.cc 文件2:tcpServer.hpp 1.提出日志概念 -- 在后续完善 日志格式 -- 暂定简单的打印功能 2.创建套接字 SOCK_STREAM -- socket参数 3.bind自己的套接字 4.设置socket 为监听状态 * 新接口1:listen 函数1&…

IIC协议总结

IIC(Inter-Integrated Circuit) ,简单说就是IC(芯片)之间通信的总线。所谓总线,就是各个器件都并联到一组公共的线路上,然后共用这条线路来传输数据。总线的英文名为BUS,这是个形象的名字&#…

chatgpt赋能python:Python如何取三位数的每一位

Python如何取三位数的每一位 作为一门广泛应用的编程语言,Python在数值相关处理方面也十分得心应手。取三位数的每一位是一个常见的需求,那么在Python中该如何实现呢? 1. 数字字符串切片 Python中的字符串有索引和切片功能,我们…

chatgpt赋能python:Python如何命名输出图片名字

Python 如何命名输出图片名字 Python 是一种非常流行的编程语言。它非常灵活,可以用于各种应用场景。其中一个应用是图像处理。在 Python 中,我们可以使用不同的库来读取和处理图像。但是,当处理大量图片时,给每个图片命名会变得…

UDS关于0x37服务退出传输学习笔记

1.服务说明 客户端使用此服务来终止客户端和服务器之间的数据传输(上传或下载)。 2.请求消息 2.1请求消息子功能参数$Level(LEV_)定义 此服务不使用子函数参数。 2.2请求消息数据参数定义 transferRequestParameterRecord&a…

C++技能 - 详解const的几种使用【再也不迷糊了,干货还是蛮多的】

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 C技能系列 期待你的关注哦!!! 生活就是上帝发给你的一张手牌,无论多烂,你都得拿着。 Life is god give you a hand, no matter ho…

RK3588平台开发系列讲解(以太网篇)MDIO底层驱动

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、mdiobus总线二、mdiobus的注册沉淀、分享、成长,让自己和他人都能有所收获!😄 一、mdiobus总线 mdio bus是mdio管里phy寄存器的总线,此总线非设备驱动模型之总线。mdiobus在内核中用mii_bus结构体描述,mii_b…

基于51单片机的电子钟Protues仿真设计

一、设计背景 电子钟是指利用数字电路或单片机等现代电子技术来实现时间计量和显示的钟表。相较于传统机械钟、石英钟等时钟,电子钟具有精度高、音响小、易于制造和调节等优点,同时也由于其美观大方的外观设计而成为了家居装饰中不可或缺的一部分。 其…

初探react中使用MongoDB

MongoDB介绍与安装 什么是MongoDB 来自于英文单词“Humongous”,中文含义表示“庞大”面向文档存储的开源数据库由C编写,支持多种语言连接 为什么要用MongoDB 性能好(内存计算)大规模数据存储(可拓展性&#xff09…

Cracking C++(9): 编译选项的设置

文章目录 1. 目的2. 生成 hello 可执行程序的过程3. 编译选项4. overlook 项目简介4.1 基本用法4.2 典型例子&#xff1a; 函数缺少返回值 1. 目的 对应到 hackingcpp 网站上 Hello World 这一节内容的笔记和个人的拓展。 2. 生成 hello 可执行程序的过程 #include <iost…