从硬件角度理解“Linux下一切皆文件“,详解用户级缓冲区

目录

前言

一、从硬件角度理解"Linux下一切皆文件"

从理解硬件是种“文件”到其他系统资源的抽象

二、缓冲区

1.缓冲区介绍

2.缓冲区的刷新策略

3.用户级缓冲区

这个用户级缓冲区在哪呢?

解释关于fork再加重定向“>”后数据会打印两份的原因

4.内核缓冲区简介

总结



前言

"Linux下一切皆文件",这是Linux的一个基本设置理念同时也是Linux的设计哲学所在。

这篇博客,笔者首先总结一下我自学习Linux以来,到目前自己对“Linux下一切皆文件”的感悟和理解,其次再讨论Linux中的缓冲区机制。


提示:以下是本篇文章正文内容,下面案例可供参考

一、从硬件角度理解"Linux下一切皆文件"

首先需要再次明确Linux操作系统的主要目的或者作用:

对上,方便用户使用——为用户提供稳定的、高效的、安全的使用环境。

对下,管理好计算机繁杂的软硬件资源;

其次需要明确的是文件无外乎由两部分构成:内容和属性

内容决定文件“是什么”(数据含义)。

属性决定文件的“如何用”(权限、存储、类型)。

比如一个普通文件:

他的内容是文本、二进制数据;

他的属性是文件名、权限、大小、时间戳等。

我们可以通过write、read等修改文件内容,也可以用chmod函数修改文件的权限等属性。

初识Linux:常见指令介绍,文件权限的更改,以及粘滞位的理解-CSDN博客

那么思维发散一下,我们能否将这些硬件的自身状态、操作方法抽象为“内容+属性”,并用统一的接口修改这些硬件呢?

已知的是Linux似乎正是将系统资源(如硬件设备等)抽象为文件,提供统一的文件操作接口(openreadwriteclose等)。使得无论操作对象是普通文件、目录、设备,用户都可以通过相同的文件系统与之交互。

从理解硬件是种“文件”到其他系统资源的抽象

对于计算机上诸多的硬件资源,我目前认为操作系统通过:先整理,再管理的方法管理这些硬件。

所谓先整理,在管理。这是笔者从进程PCB的创建受到的启发。OS为方便管理不同的进程会为其创建PCB,其中包含着进程的所有属性信息,那管理硬件是不是也可以通过创建某种数据结构来实现管理呢?

假设OS为方便管理各种硬件资源会为其创建某种数据结构——这里想象成某种结构体struct file,该结构体中记录着该硬件的各种属性信息和行为函数——即IO操作。

通过对冯诺依曼体系结构的抽象,将计算机抽象为存储器和其他。这个其他中包括cpu和各种硬件设备,这么划分的原因是这些硬件都要与内存进行IO操作。我们不妨暂将所有硬件的IO操作抽象为两个函数read( )和write( )。

通过上述两点,创建一个struct file结构体,其中有着各硬件的状态信息和函数行为:

struct file
{//内容int type;int status;……//属性int (*write)();//函数指针int (*read)();……
}

那么将每个硬件file实例化(与多态有些相似),再通过一个数据结构如链表将这些硬件的struct对象管理起来,如链表。

综上,当站在上层视角来看,这些硬件都是一种统一的数据,其中有着“内容+属性”,这不就是一种抽象的“文件”吗这些个文件提供统一的文件操作接口(write、read、open、close等),无论操作对象是键盘、鼠标还是其他什么硬件,用户都可以通过相同的接口与之交互。

将上述思想和方法发散到其他系统资源,同样通过“先整理,再管理”的思想,这或许是理解“Linux下一切皆文件”的思路之一吧。

问:磁盘等硬件有输入输出好理解,那如显示器等硬件不是只有输入或者只有输出吗?

答:虽然如显示器等设备没有输入操作,我们只需将其struct内部的函数指针置为NULL即可。

以上是笔者目前对“Linux下一切皆文件”的理解,若笔者有错误的认识或者读者有更深的理解,还请读者不吝赐教,在评论区中一起讨论。

二、缓冲区

1.缓冲区介绍

1)什么是缓冲区

缓冲区本质上就是一段内存。

2)为什么要有缓冲区

磁盘等存储设备物理I/O效率极低,通过引入缓冲区将多次小数据操作合并为大数据操作,从而节省数据IO时间,提升性能。

2.缓冲区的刷新策略

通过以下代码观察缓冲区:

在程序sleep的十秒之间printf不会打印,等sleep结束后才会打印。

注意printf没有带\n。

  1 #include<stdio.h>2 #include<unistd.h>3 int main()4 {5     printf("hello Linux");//注意没带\n6     sleep(10);                                                                                                                                           7     return 0;8 }

但在printf和sleep之间添加了fflsh(stdout)后,printf会立即打印。

下面是缓冲区的三种刷新策略。

1)立即刷新——无缓冲

2)行刷新——行缓冲

3)缓冲区满——全缓冲(效率最高)

有两种特殊情况缓冲区会立即刷新:

①用户强制刷新(如上述的fflush函数);

②程序退出——这也是为什么在某些集成开发环境下(如vs2022)程序时得等一会才能在控制台上看到打印结果。

3.用户级缓冲区

引子——观察下列代码在bash不同指令下的执行情况:

  1 #include<stdio.h>2 #include<unistd.h>3 #include<string.h>4 int main()5 {6     printf("hello Linux\n");//注意没带\n7     fprintf(stdout,"hello fprintf\n");8     fputs("hello fpurs",stdout);9     const char *str="hello writie\n";10     write(stdout->_fileno,str,strlen(str));11 12     fork();                                                                                                                                              13     return 0;14 }

执行 . / test:

执行. / test > log.txt

可以发现. / test > log.txt比. / test多打印了几行,这多打印的全是C标准库提供的函数。

这个用户级缓冲区在哪呢?

通过观察stdout的类型,我们可以推导出FILE中不仅有文件描述符,还存在缓冲区,所有当我们想要主动刷新缓冲区时,fflush传入的是FILE*指针。

解释关于fork再加重定向“>”后数据会打印两份的原因

①没有进行>时,我们看到打印了四条数据。stdout默认采用的是行刷新,在进行fork之前三条C函数已经将数据打印到显示器上了,FILE中不在存有相应数据了;

如果我们进行了>,写入的文件不再是显示器,而是普通文件,采用的刷新策略也不再是行缓冲而是全缓冲,而这三条C打印显然不能填满缓冲区,于是数据就没有被刷新。fork函数之后紧接着就是程序退出,故当fork创建子进程后,无论父子进程谁先退出都必定会发生写时拷贝(缓冲区刷新就是修改),因此父子进程分别向log.txt中打印了数据。

至于write,他是linux系统调用,不属于C,且write用的是fd文件描述符没有使用FILE结构体,所以C提供的缓冲区中就不考虑write,因此无论那种情况write都只打印了一次。

为什么stdout标准输出默认采用行缓冲?

有关文件描述符的解释,参看:Linux中有关文件操作的系统接口,文件描述符,重定向的介绍-CSDN博客

有关fork函数和写时拷贝的解释,参看:

Linux环境下的进程创建-fork函数的使用与写时拷贝, 进程退出exit和_exit的区别,以及进程等待waitpid和status数据的提取方法-CSDN博客

4.内核缓冲区简介

1)内核缓冲区在相应文件的file_struct中。流是文件的特殊或者说流是文件的一种高级抽象。

file_struct与task_struct(PCB)一样都是内核级数据结构,在task_struct中有着指向file_struct的指针。

2)内核缓冲区的刷新策略完全由OS自主决定;

3)完整的数据刷新过程:

4)fsync函数:强制将内核缓冲区中的数据刷入磁盘。


总结

笔者水平浅薄,对于上述内容难免有疏忽疑错,还请读者多多指处。

希望本文对你有所帮助

读完点赞,手留余香~

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

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

相关文章

车道线检测----CLRERNet

CLRerNet&#xff1a;利用LaneIoU提升车道检测置信度 摘要 车道标检测在自动驾驶和驾驶辅助系统中至关重要。现代深度车道检测方法在车道检测基准测试中表现出色。通过初步的预言机实验&#xff0c;我们首次拆解车道表示组件以确定研究方向。我们表明&#xff0c;正确的车道位…

ML307R 的 USB Vendor ID (VID):0x2ECC ML307R 的 USB Product ID (PID):0x3012

可以的&#xff0c;在文档的「Table 3. VID、PID查询表」中明确指出&#xff1a; ML307R 的 USB Vendor ID (VID)&#xff1a;0x2ECCML307R 的 USB Product ID (PID)&#xff1a;0x3012 你可以将这对 VID/PID 加到 Linux 的 option 驱动中&#xff0c;比如&#xff1a; ech…

论信息系统项目的范围管理

论信息系统项目的范围管理 前言一、规划范围管理&#xff0c;收集需求二、定义范围三、创建工作分解结构四、确认范围五、控制范围 前言 为了应对烟草零售客户数量大幅度增长所带来的问题&#xff0c;切实履行控烟履约的相关要求&#xff0c;同时也为了响应国务院“放管服”政策…

MongoDB与PostgreSQL两个数据库的特点详细对比

MongoDB 和 PostgreSQL 是两种不同类型的数据库&#xff0c;分别属于 ​​NoSQL&#xff08;文档型&#xff09;​​ 和 ​​关系型&#xff08;SQL&#xff09;​​ 数据库。它们在数据模型、查询语言、扩展性、事务支持等方面有显著差异。以下是详细对比&#xff1a; ​​1. …

计算机网络:什么是电磁波以及有什么危害?

电磁波详解 电磁波(Electromagnetic Wave)是由电场和磁场相互激发、在空间中传播的能量形式。它既是现代通信的基石(如手机、Wi-Fi、卫星信号),也是自然界中光、热辐射等现象的本质。以下从定义、产生、特性、分类及应用全面解析: 一、电磁波的本质 1. 核心定义 电场与…

如何使用 Solana Yellowstone gRPC 重新连接和重放插槽

Yellowstone gRPC 是一个功能强大、可用于生产环境且经过实战检验的工具&#xff0c;用于流式传输实时的 Solana 数据。但在实际条件下&#xff0c;网络中断或服务器重启可能导致连接中断。如果没有适当的重连策略&#xff0c;你的应用程序可能会错过区块链的关键更新。 为了防…

foxmail - foxmail 启用超大附件提示密码与帐号不匹配

foxmail 启用超大附件提示密码与帐号不匹配 问题描述 在 foxmail 客户端中&#xff0c;启用超大附件功能&#xff0c;输入了正确的账号&#xff08;邮箱&#xff09;与密码&#xff0c;但是提示密码与帐号不匹配 处理策略 找到 foxmail 客户端目录/Global 目录下的 domain.i…

MySQL 事务(一)

文章目录 CURD不加控制&#xff0c;会有什么问题CURD满足什么属性&#xff0c;能解决上述问题&#xff1f;什么是事务为什么要有事务事务的版本支持了解事务的提交方式 事务常见操作方式研究并发场景事务的正常操作事务的非正常情况的案例结论事务操作的注意事项 CURD不加控制&…

CSS面试题汇总

在前端开发领域&#xff0c;CSS 是一项不可或缺的技术。无论是页面布局、样式设计还是动画效果&#xff0c;CSS 都扮演着重要的角色。因此&#xff0c;在前端面试中&#xff0c;CSS 相关的知识点往往是面试官重点考察的内容。为了帮助大家更好地准备面试&#xff0c;本文汇总了…

Java 后端给前端传Long值,精度丢失的问题与解决

为什么后端 Long 类型 ID 要转为 String&#xff1f; 在前后端分离的开发中&#xff0c;Java 后端通常使用 Long 类型作为主键 ID&#xff08;如雪花算法生成的 ID&#xff09;。但如果直接将 Long 返回给前端&#xff0c;可能会导致前端精度丢失的问题&#xff0c;特别是在 J…

对称二叉树的判定:双端队列的精妙应用

一、题目解析 题目描述 给定一个二叉树&#xff0c;检查它是否是镜像对称的。例如&#xff0c;二叉树 [1,2,2,3,4,4,3] 是对称的&#xff1a; 1/ \2 2/ \ / \ 3 4 4 3而 [1,2,2,null,3,null,3] 则不是镜像对称的&#xff1a; 1/ \2 2\ \3 3问题本质 判断一棵二叉…

C#数组与集合

&#x1f9e0; 一、数组&#xff08;Array&#xff09; 1. 定义和初始化数组 // 定义并初始化数组 int[] numbers new int[5]; // 默认值为 0// 声明并赋值 string[] names { "Tom", "Jerry", "Bob" };// 使用 new 初始化 double[] scores …

本地部署Scratch在线编辑器

1、说明 由于在GitHub上没有找到Scratch源码&#xff0c;所以只能编写脚本下载官网相关资源&#xff0c;然后在本地部署。 如果你找到了Scratch源码&#xff0c;请自行编译部署&#xff0c;可忽略以下操作。 项目结构&#xff1a;scratch.mit.edu |-- chunks | |-- fetch-w…

Gmsh 读取自定义轮廓并划分网格:深入解析与实践指南

一、Gmsh 简介 (一)Gmsh 是什么 Gmsh 是一款功能强大的开源有限元网格生成器,广泛应用于工程仿真、数值模拟以及计算机图形学等领域。它为用户提供了从几何建模到网格划分的一整套解决方案,能够有效处理复杂几何形状,生成高质量的二维和三维网格,满足多种数值方法的需求…

Elabscience 精准识别 CD4+ T 细胞|大鼠源单克隆抗体 GK1.5,适配小鼠样本的流式优选方案

内容概要 CD4 T细胞在免疫调节、自身免疫疾病及肿瘤免疫治疗中发挥关键作用。Elabscience推出的APC Anti-Mouse CD4 Antibody (GK1.5)&#xff08;货号&#xff1a;E-AB-F1097E&#xff09;是一款高特异性、低背景的流式抗体&#xff0c;专为小鼠CD4 T细胞亚群检测优化设计。该…

【RabbitMQ】应用问题、仲裁队列(Raft算法)和HAProxy负载均衡

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【中间件】企业级中间件剖析 一、幂等性保障 什么是幂等性&#xff1f; 幂等性是指对一个系统进行重复调用&#xff08;相同参数&#xff09;&#xff0c;无论同一操作执行多少次&#xff0c;这些请求…

51 单片机头文件 reg51.h 和 reg52.h 详解

51 单片机头文件详解 51 单片机的头文件reg51.h和reg52.h是开发中非常重要的文件,它们定义了单片机的特殊功能寄存器 (SFR) 和位地址。以下是对这两个头文件的详细解析: 1. 头文件概述 reg51.h:针对标准 8051 单片机(4KB ROM, 128B RAM) reg52.h:针对增强型 8052 单片…

前端的面试笔记——JavaScript篇(二)

一、instanceof 在 JavaScript 里&#xff0c;instanceof 是一个相当实用的运算符&#xff0c;它的主要功能是检查某个对象是否属于特定构造函数的实例。这里需要明确的是&#xff0c;判断的依据并非对象的类型&#xff0c;而是其原型链。下面为你详细介绍它的用法和特点&…

”一维前缀和“算法原理及模板

前缀和&#xff0c;就是通过一种方法来求出数组中某个连续区间的元素的和的办法。我们通常先预处理出来一个前缀和数组&#xff0c;然后把数组中进行元素填充后再进行后续使用。 我们通过一道模板题或许能更加理解其意思。 现在的问题就是&#xff1a;如果我们用暴力枚举来记录…

5.13/14 linux安装centos及一些操作命令随记

一、环境准备 VMware Workstation版本选择建议 CentOS 7 ISO镜像下载指引 虚拟机硬件配置建议&#xff08;内存/处理器/磁盘空间&#xff09; 二、系统基础命令 一、环境准备 1.VMware Workstation版本选择建议 版本选择依据 选择VMware Workstation的版本时&#xff0c…