Linux 文件(3)

文章目录

  • 1. Linux下一切皆文件
  • 2. 文件缓冲区
    • 2.1 缓冲区是什么
    • 2.2 缓冲区的刷新策略
    • 2.3 为什么要有缓冲区
    • 2.4 一个理解缓冲区刷新的例子
  • 3. 标准错误

1. Linux下一切皆文件

在刚开始学习Linux的时候,我们就说Linux下一切皆文件——键盘是文件,显示器是文件,网卡是文件,硬盘也是文件。
可这到底该如何理解呢?为什么Linux下一切皆文件呢?

从操作系统的角度而言,操作系统必须要管理好各种硬件资源,所以必须先描述,再组织,而Linux下一切皆文件,所以描述硬件,同样使用struct file来进行描述。

而硬件又是如何被访问的呢?我们用户想要访问硬件,一定是通过进程进行访问的,而硬件在操作系统中以struct file的形式被描述,一切皆文件,进程访问硬件,其实就是在访问文件。

然而,有一个问题必须要考虑:不同的硬件必然具有不同的读写方式,都使用struct file进行封装,又如何区别不同的硬件之间的读写方式呢?

Linux中,使用函数指针来实现。在struct file结构体中,有一个这样的成员变量:

在这里插入图片描述

这是一个指针变量,指向一个结构体,这个结构体中包含的是硬件的操作方法集,即各种函数指针,指向读操作的函数,写操作的函数,重定位的函数等等

所以,虽然不同的硬件都是用struct file进行描述,但是依然能够通过不同的读写方式去操作不同的硬件。OS在为各个硬件创建struct file时,就自动为不同的硬件分配了其所对应的操作方法集。

所以,在进程的角度,只需要直接对文件进行操作,在进程看来,都是struct file,而不用去区分具体硬件之间的不同。

这就是Linux下一切皆文件的核心。站在进程的角度,一切都是struct file,一切都可以通过同一套文件操作接口进行操作,而不用去区分实际差别。

实际上,在进程角度的struct file,是Linux中所设计的虚拟文件系统,即VFS,Linux中封装了这么一层软件层后,开发者通过进程,只需要一套文件操作接口,就可以调度Linux中的大部分资源,这就是Linux内核设计的高明之处,是多态思想的重要体现

2. 文件缓冲区

Linux下在打开文件时,为相应文件创建struct file,而在struct file有三个核心点:文件属性,文件内核缓冲区和文件操作方法集。

文件内核缓冲区是什么呢?之前我们说,我们使用系统调用接口read或者write,不是直接从文件读或向文件写,而是向文件的内核缓冲区写,从文件的内核缓冲区读。那么接下来,我们就重点来谈一谈缓冲区这个概念。

2.1 缓冲区是什么

缓冲区本质上就是一段内存空间。
缓冲区通常会有三种:用户级缓冲区,语言级缓冲区和内核文件缓冲区。
用户级缓冲区,就是指开发者自己开辟并用于存储数据的一段空间;语言级缓冲区,是指各种编程语言在为我们封装文件时,内部所提供的缓冲区。

对于语言级缓冲区,我们以C语言为例。
C语言中,封装了FILE这个结构体,C语言的相关文件操作接口,都要设计到FILE。而在FILE中,除了会涉及到文件描述符,即fileno外,还会有一个输入缓冲区和一个输出缓冲区char* in_buffer 和 char* out_buffer。这两个缓冲区,就是语言级别的缓冲区。

实际上,我们使用C语言的相关文件接口进行读和写时,都是从语言级的缓冲区中读写,而不涉及到文件内核缓冲区。

我们以写文件为例。我们使用printf向标准输出中写,首先是将用户级缓冲区中的内容,写到语言级缓冲区中,而每一个缓冲区都会具有一定的刷新策略,当满足相应条件时,语言级缓冲区就会通过系统调用write,将其中的内容刷新到文件内核缓冲区中。就进程而言,将内容写到文件内核缓冲区后,即可认为成功写入,因为文件内核缓冲区的刷新,主要由操作系统管理(当然开发者也可以通过fsync系统调用刷新)

在这里插入图片描述

2.2 缓冲区的刷新策略

就语言级别的缓冲区而言,写入时的刷新策略主要有二种:

  • 行缓冲。在写入内容时遇到换行符即做刷新。否则,缓冲区写满再刷新。
  • 全缓冲。直到缓冲区写满后,缓冲区才做刷新。

另外,在进程退出时,也会刷新语言级别的缓冲区;当然,我们也可以使用fflush主动刷新语言级缓冲区。

就文件内核缓冲区而言,写入时的刷新策略也主要是行缓冲和全缓冲,但是文件内核缓冲区主要由操作系统进行管理,刷新策略实际会更加复杂,会涉及到一些动态刷新等。

另外,在文件关闭时,文件内核缓冲区也会被刷新;当然,我们也可以使用fsync系统调用,主动刷新文件内核缓冲区。

对于显示器文件而言,相关缓冲区一般是行缓冲;对于普通文件而言,相关缓冲区一般是全缓冲。

2.3 为什么要有缓冲区

为什么要有语言级别的缓冲区?
如果没有语言级别的缓冲区,直接从用户缓冲区向文件内核缓冲区中写,会频繁使用系统调用write,而系统调用的使用成本是很高的,消耗太大,会降低编程语言的运行效率,因此必须要有语言级别的缓冲区。

那为什么要有文件内核缓冲区呢?
如果没有文件内核缓冲区,即无法做到多次输入,一次刷新,就会频繁地进行磁盘级I/O,而磁盘级I/O的效率是很低的,那么这样,整个程序的运行效率也会下降。

总而言之,缓冲区的存在,是为了提高进程运行的效率,避免无意义的消耗。

2.4 一个理解缓冲区刷新的例子

在这里插入图片描述
来看上述代码重定向前与重定向后运行结果的不同:

在这里插入图片描述
为什么重定向前后的输出结果会不同呢?这就涉及到缓冲区刷新策略不同的问题了。

对于write而言,是直接向文件描述符为1的文件内核缓冲区中写;而对于printf,虽然默认也是向标准输出中写,但是它首先会将内容写到C语言自身设计的语言级缓冲区中。

未重定向前,write和printf都是最终写入到标准输出中,即显示器文件,而标准输出所对应的语言级缓冲区和文件内核缓冲区都是以行缓冲的形式进行刷新,所以 printf 输出的字符串,会被立刻由用户级缓冲区刷新到文件内核缓冲区,再刷新到显示器文件中;而write输出的字符串,则直接由文件内核缓冲区刷新到显示器文件中。多进程的创建并不影响整个过程。

重定向后,文件描述符1不再对应标准输出文件,而是对应普通文件,因此语言级缓冲区和文件内核缓冲区的刷新策略都变为全缓冲。多进程的创建对于直接写入到内核缓冲区中的write没有影响(从进程的角度,认为写入到内核缓冲区后,即完成写入),但对于写入语言级缓冲区的printf,此时就有影响了。

由于不再是行缓冲,因此printf写入语言缓冲区后,不会立刻刷新。而当创建多进程后,父子进程之间共享代码和数据,而当进程结束时,语言级缓冲区会被刷新,由于子进程会先于父进程结束,而刷新会对语言缓冲区做出更改,为了确保父子进程间的独立性,就会发生写时拷贝,这样子进程就不再与父进程共享同一个语言缓冲区,这也就意味着,子进程结束,刷新语言缓冲区后,父进程语言缓冲区中的内容并没有被刷新,这也就是为什么上述将打印内容重定向到文件后,会出现两个hello printf 的原因,本质还是因为发生了写时拷贝。

3. 标准错误

下面,我们来简单聊聊标准错误。

标准输入一般对应键盘文件,刷新策略为行缓冲;而标准输出和标准错误一般对应显示器文件,标准输出为行缓冲,标准错误则通常无缓冲,输出即刷新。

既然标准输出和标准错误都对应显示器文件,那为什么要专门区分它们呢?

区分标准输出(fd为1)和标准错误(fd为2),最主要还是为了区分输出流和错误流,即区分正确输出和错误输出。

我们来看下面的一个示例:

在这里插入图片描述
我们在命令行中,使用输出重定向来区分标准输出和标准错误。

在这里插入图片描述
这样,我们就区分出了标准输出和标准错误。
在上述代码中,值得一提的是,如果不指明具体进行重定向的文件描述符,默认是将1号文件描述符,即标准输出进行重定向。

如果不想区分标准输出和标准错误,而想将这两者全部重定向到某个文件中,我们可以在命令行中进行如下操作:

在这里插入图片描述
在上述命令行中,我们先将stdout重定向到log.txt中,再将stderr重定向到文件描述符1多对应的文件中,也就是log.txt中。

至于为什么log.txt中,数字的顺序与我们程序的逻辑不太符合,这个不用太在意,因为这个涉及到文件内核缓冲区更加复杂的刷新机制。

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

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

相关文章

STM32之串口通信蓝牙(BLE)

一、串口通信的原理与应用 通信的方式 处理器与外部设备之间或者处理器与处理器之间通信的方式分两种:串行通信和并行通信。 串行通信 传输原理:数据按位依次顺序传输(每一位占据固定的时间长度 MSB or LSB) 优点&#xff1a…

基于python的机器学习(七)—— 数据特征选择

目录 一、特征选择概念 二、特征选择的方法 2.1 过滤式特征选择 2.1.1 方差分析 2.1.2 相关系数 2.1.3 卡方检验 2.2 包裹式特征选择 2.2.1 递归特征消除 2.3 嵌入式特征选择 2.3.1 决策树特征重要性 一、特征选择概念 特征选择是机器学习非常重要的一个步骤&#x…

《AI工程技术栈》:三层结构解析,AI工程如何区别于ML工程与全栈工程

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

Redis数据库-消息队列

一、消息队列介绍 二、基于List结构模拟消息队列 总结: 三、基于PubSub实现消息队列 (1)PubSub介绍 PubSub是publish与subscribe两个单词的缩写,见明知意,PubSub就是发布与订阅的意思。 可以到Redis官网查看通配符的书写规则: …

归一化函数 & 激活函数

目录 Softmax函数 定义 输入输出 例子 总结 Layernorm 定义 输入输出 Sigmoid函数 定义 Tanh函数 定义 Relu函数 定义 Elu函数 定义 Gelu函数 定义 总结 Softmax函数 定义 softmax函数又称归一化指数函数,其作用是将一个 n 维的实值向量转换为…

使用 C# 入门深度学习:线性代数详细讲解

在深度学习的领域中,线性代数是基础数学工具之一。无论是神经网络的训练过程,还是数据的预处理和特征提取,线性代数的知识都无处不在。掌握线性代数的核心概念,对于理解和实现深度学习算法至关重要。在本篇文章中,我们…

【通用智能体】Serper API 详解:搜索引擎数据获取的核心工具

Serper API 详解:搜索引擎数据获取的核心工具 一、Serper API 的定义与核心功能二、技术架构与核心优势2.1 技术实现原理2.2 对比传统方案的突破性优势 三、典型应用场景与代码示例3.1 SEO 监控系统3.2 竞品广告分析 四、使用成本与配额策略五、开发者注意事项六、替…

CICD遇到npm error code EINTEGRITY的问题

场景 CICD编译时抛出npm error code EINTEGRITY的错误 npm error code EINTEGRITY npm error sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA integrity checksum failed when using sha512: wanted sha512-PlhdFcillOINfeV…

Android13 wifi设置国家码详解

Android13 wifi设置国家码详解 文章目录 Android13 wifi设置国家码详解一、前言二、设置wifi国家码相关代码1、adb或者串口也能设置和获取当前国家码(1)查询命令的方式(2)获取和设置国家码的示例 2、Java代码设置国家码3、获取当前…

c/c++的opencv高斯模糊

深入探索图像高斯模糊:原理、C/C实现与OpenCV应用 在图像处理的众多技术中,模糊(或平滑)是最为基础且不可或缺的一环。它广泛应用于降噪、图像预处理、特征提取前的平滑以及计算机图形学中的各种视觉效果。在高斯模糊&#xff08…

Java求职者面试:从Spring Boot到微服务的技术点解析

Java求职者面试:从Spring Boot到微服务的技术点解析 场景:互联网医疗-预约挂号系统 面试官: “小明,我们今天的场景是一个互联网医疗的预约挂号系统。我们需要支持高并发的用户预约操作,同时保证数据一致性和系统的高…

专业 YouTube SEO 方案:打造高排名视频的关键步骤

YouTube 是全球订阅量最高的社交媒体平台之一。YouTube 为发布创意视频内容和针对特定受众开展营销活动提供了无限可能,是任何品牌内容营销策略的重要组成部分。 但是,为了发展您的 YouTube 频道并消除噪音,优化您的视频内容以便可以在搜索结…

Java Collection(集合) 接口

Date: 2025-05-21 20:21:32 author: lijianzhan Java 集合框架提供了一组接口和类,以实现各种数据结构和算法。 以下是关于 Java 集合的核心内容说明: /*** Java Collection Framework 说明:** 在 Java 中,集合(Collec…

如何用ipmitool修改FRU信息?

如何用ipmitool修改FRU信息? FRU(Field Replaceable Unit,现场可更换单元)记录了服务器硬件的关键信息,如序列号、制造商、型号等。通过ipmitool修改FRU信息,常用于硬件维护、资产标签更新或调试场景。以下…

uniapp vue 开发微信小程序 分包梳理经验总结

嗨,我是小路。今天主要和大家分享的主题是“uniapp vue 开发微信小程序 分包梳理经验总结”。 在使用 UniAppvue框架开发微信小程序时,当项目比较大的时候,经常需要分包加载。它有助于控制主包的大小,从而提升小程序的启…

git合并多次commit提交

首先查看历史记录 git log 查看你想要合并的commit是哪些(注意:这里是逆序,最上的是最新提交) 找到当前想要合并的最后一个记录,复制该记录的下一个记录的 id(黄色部分commit id)&#xff0c…

系统架构设计(七):数据流图

定义 数据流图(Data Flow Diagram, DFD)是一种用于表示信息系统数据流转及处理过程的图形工具。 它反映系统功能及数据之间的关系,是结构化分析与设计的重要工具。 主要符号 符号说明描述举例方框外部实体(源或终点&#xff09…

MAUI与XAML交互:构建跨平台应用的关键技巧

文章目录 引言1. 代码隐藏文件关联1.1 XAML文件与代码隐藏文件的关系1.2 部分类机制1.3 InitializeComponent方法1.4 XAML命名空间映射 2. 元素名称与x:Name属性2.1 x:Name属性的作用2.2 命名规则与最佳实践2.3 x:Name与x:Reference的区别2.4 编译过程中的名称处理 3. 在代码中…

php://filter的trick

php://filter流最常见的用法就是文件包含读取文件,但是它不止可以用来读取文件,还可以和RCE,XXE,反序列化等进行组合利用 filter协议介绍 php://filter是php独有的一种协议,它是一种过滤器,可以作为一个中…

微信小程序开发中,请求数据列表,第一次请求10条,滑动到最低自动再请求10条,后面请求的10条怎么加到第一次请求的10条后面?

在微信小程序中实现分页加载数据列表,可通过以下步骤将后续请求的10条数据追加到首次加载的数据之后: 实现步骤及代码示例 定义页面数据与参数 在页面的 data 中初始化存储列表、页码、加载状态及是否有更多数据的标识: Page({data: {list…