【Linux内核系列】:进入文件系统的世界

🔥 本文专栏:Linux
🌸作者主页:努力努力再努力wz

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

那么从本篇文章开始就要进入文件系统的学习了,那么之前的内容主要围绕的是进程的相关概念以及进程控制有关的系统调用接口的介绍,以及最后结合之前所学的知识以及系统调用接口自己用c语言简单实现了一个shell外壳程序,当然这个shell的外壳程序由于之后的文件以及信号的知识还没学,所以实现的shell外壳程序是一个阉割版,有很多功能是缺失的,那么我们在往后的学习中,可以逐渐在我们之前完成的shell外壳的基础上去做相应功能的完善,那么进程是我们Linux操作系统学习上的一道大山,那么我们已经顺利跨越,而今天包括之后讲的文件系统则是第二道大山,那么越往后走,我们对于Linux操作系统的内核就了解的越深入,那么废话不多说,进入我们文件系统的学习

★★★ 本文前置知识
文件权限
进程的概念

重识文件

那么文件这个话题,从我的第一篇Linux文章开始就已经谈论过,而我们应该对于Linux操作系统有一个基本的共识,那么就是我们Linux上的一切事物,都可以一文件的视角来看待,那么我们的各种软件也就是进程,他们都有自己对应的文件,包括最底层的硬件,那么在上层也有自己的文件的映射,比如显示器以及键盘都有自己对应的文件

那么对于这各种类型的文件,那么我们可以按照一个标准来将这所有的文件分为两类来看待,那么就是按照状态来分类,那么我们可以将文件分为打开的文件和未被打开的文件,那么大学的c语言的课程学习中,那么我们就认识了我们可以在代码层面上调用我们的诸如fopen库函数来打开一个文件,然后用fread库函数来读取一个文件的内容

那么我们要打开一个文件,我们就得在代码层面上取调用相应的库函数来打开相应的一个文件,那么我们既然是在代码层面上来实现打开一个文件,所以也就意味着我们打开一个文件只能是通过我们的进程来打开,因为得要运行相应的代码来打开文件,那么运行相应的代码意味着创建一个进程,那么也就说明了打开一个文件的对象一定是我们的进程来打开的,而我们知道进程的元数据是加载到内存中的,那么它要打开一个文件,那么必定是要访问该文件相关的数据,比如要进行读取或者进行写入,那么要访问该文件的数据,那么首先就得将该文件的各种元数据加载到内存上,才能方便进程快速访问获取到文件的相关数据

那么一个进程可以打开不只有一个文件,理论上单个进程与打开的文件的数目比可以是1:n的,而每一个进程都可以打开文件,那么也就意味着我们内存会加载很多文件的相关数据,所以操作系统就必然要管理内存中这些庞大的文件数据,那么它管理的方式也就是我们最熟悉的先描述,再组织的方式,也就是它会为每一个打开的文件建模,也就是定义一个file结构体,每一个打开的文件都会有各自对应的file结构体,那么这些file结构体内部就会封装关于该文件的相关属性,比如文件指针以及文件的偏移量等等,那么每一个file结构体之间就通过一个指针进行串联形成一个双链表的数据结构从而组织起这些文件,那么操作系统管理我们的文件系统就转换为对双链表该数据结构的增删改查即可


我们知道我们整个计算机体系结构是一个层级的结构,最底层是我们的硬件往上依次是我们的驱动层然后操作系统而最上层则是我们的用户,而我们用户要访问底层的相关数据,那么按照这个计算机的层级结构,必然得从上往下贯穿我们的操作系统,那么我们的库函数比如fopen以及fread函数之所以能够访问文件的相关数据,那么它们的内部实现必然就得封装了操作系统提供的系统调用接口,所以我们要在Linux系统上能够访问以及获取和修改文件的数据,那么我们必然就得掌握操作系统提供的相关的系统调用接口
在这里插入图片描述

那么我们对于文件有一个重新的认识之后,那么我们再来认识一下文件操作相关的系统调用接口

文件操作的系统调用接口

1.open系统调用接口

那么我们要对一个文件进行写入或者读取等行为的时候,那么在进行这些行为之前,首先第一步就是我们得打开这个文件,那么就如同你要使用电脑,那么你得先给电脑开机你才能进行比如打游戏或者写博客之类的活动

而这里我们打开一个文件的系统调用接口就是我们的open系统调用接口,那么在介绍我们open系统调用接口怎么使用之前,我们得先认识一下open系统调用接口的一个原理,那么首先open系统调用接口的一个参数就是一个字符指针,那么指向一个字符串,那么该字符串就是我们要打开的目标文件的文件名,而我们的文件系统是按照一棵多叉树的数据结构来组织的,那么我们要寻找定位到我们的目标文件,那么就得从这个多叉树的根节点往下遍历,而多叉树的根节点也就是对应文件系统的根目录,那么定位到目标文件在文件系统中的位置后,下一步便是将其在外部设别也就是磁盘中的元数据加载到内存中,然后创建该文件所对应的file结构体,那么这个过程其实和我们创建一个进程是十分类似的

而我们知道我们打开一个文件肯定是有目的的,也就是你打开该文件所要进行的行为,而open接口的第二个参数就是你要对该文件进行的操作,那么该参数是一个整形,那么该整形的二进制序列中的每一个二进制位就代表着其中一个模式或者行为,比如第一个二进制位为1,那么就代表读模式,第二个二进制位为1就代表写模式,而这一组二进制位对应的整形通常被定义成了宏定义在<fcntl.h>头文件中,那么在编译的预处理阶段会替换成相应的整形,那么一下就是常见的open的宏定义:

  • O_RDONLY:以只读模式打开文件。

  • O_WRONLY:以只写模式打开文件。

  • O_RDWR:以读写模式打开文件。

  • O_CREAT:如果文件不存在则创建文件。需要指定权限参数。

  • O_EXCL:与 O_CREAT 一起使用时,确保文件不存在。如果文件已存在,则 open 调用失败。

  • O_TRUNC:如果文件已存在并且成功打开为写入模式(O_WRONLY 或 O_RDWR),则清空文件。

  • O_APPEND:以追加模式打开文件。写入的数据会被追加到文件末尾。

那么我们这个第二个参数也就是我们打开该文件要进行的行为,那么获取到宏定义之后就会与我们该打开的文件的权限进行检查,那么如果我们该用户不具备对该文件有相应操作的权限,那么它会open失败并且返回一个小于0的值,那么如果满足该权限的话,那么意味着我们可以在open函数之后可以进行相应的操作比如读或者写操作,并且打开之后我们对该文件的操作将不在会重复去检查权限,也就意味着权限的检查只会在open接口调用时进行,那么之后都不会在进行检查,因为会存在效率问题,并且在我们进行open函数还会根据我们的模式对我们的文件的属性进行一定的初始化,比如写模式,那么它会一般将我们该文件的内容清空,并且将文件偏移量给置于文本开始,而追加模式则是从当前文件偏移量所保存的读写位置开始进行

其次我们对于打开的文件进行的操作可能不只有一个,那么我们可以进行结合多个模式来指定对该文件的多个行为,因为我们的模式是对应一组二进序列中特定的二进制位,所以我们可以通过或运算来进行多个模式,例:

O_RDWR | O_CREAT, S_IRUSR | S_IWUSR

​ O_WRONLY | O_CREAT | O_APPEND


而我们的open还有第三个参数,那么如果说我们打开的目标文件不存在,那么我们open则会在我们当前进程的工作目录下创建该文件,那么我们就可以通过第三个参数来初始化该文件的一个权限,而我们知道文件的三个角色也就是拥有者和所属组和其他的权限可以用三个8进制数字来表述,所以我们第三个参数就是传递一个8进制序列


而关于open接口的返回值,那么如果我们因为权限等问题open失败会返回一个小于0的返回值通常是-1,而open调用成功所返回的返回值,那么该返回值可不仅仅是意味着返回成功,它还意味着返回了该进程的文件描述符

那么这里就得引入文件描述符的概念:

我们知道我们一个进程可以打开n个文件,所以我们进程也得管理自己打开的文件,所以在进程对应的task_struct结构体里就有一个字段struct files_struct * files,它是一个指针,指向的是一个指针数组,那么该指针数组也有一个专业术语就是文件描述表,本质就是一个指针数组,那么这个指针数组的每一个元素就指向一个打开的文件的file结构体,而数组的下标就是文件描述符,那么我们该进程每次打开一个进程,那么它会从数组的起始位置也就是下表为0的位置往后线形扫描,看是否有可分配的空位置,那么有,则将该位置的指针指向该文件所对应的file结构体,并且返回该位置的数组下标
在这里插入图片描述
而这里我们每一个进程在被创建的时候,会默认打开三个文件,也就是键盘文件,和显示器文件,和保存错误信息的显示器文件,那么他们会默认在数组的下标为0,1,2这三个位置,所以我们经常说我们编写的c语言文可执行文件会默认打开三个输入输出流,那么这三个输入输出流本质上就是这三个文件,那么任何语言都是在操作系统的上层,那么必定他们都会直接或者间接的打开这三个文件,所以进程之后每打开一个文件都是从下标为3位置处线性往后扫描分配。

-open

头文件:<fcntl.h>

函数原型:

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

2.write系统调用接口

那么我们一旦调用了open系统调用接口,那么它会返回一个文件描述符,那么这个文件描述符就能够让后续的系统调用接口来使用,那么我们的write就是写操作的系统调用接口,那么它的第一个参数是我们的文件描述符,那么第二个参数则是一个指针,指向要一个我们要写入的数据,第三个参数则是写入的内容的长度,那么这里要注意的如果我们写入的是字符串,那么通常在我们的c语言的规定下,我们的字符串末尾必须带有一个\0来标记字符串结尾,但是我们在向文件写入的时候,不用显示的去写入一个\0,因为\0是c语言为了便于string类的库函数的工作从而这样规定的,但是它不是一个有效信息,所以我们可以不用加\0,所以在写入字符串不用显示的在末尾在添加\0

那么write的返回值则是我们实际写入的长度,那么如果调用失败,那么write会返回-1

-write

头文件:<unistd.h>

函数原型:

size_t write(int fd, const void *buf, size_t count);

3.read系统调用接口

那么read系统调用接口则是用来读取一个文件的内容

那么read系统调用接口的第一个参数则是一个文件描述符,那么它会获取到文件描述符然后利用该进程的文件描述符到指针数组的对应位置去访问到file结构体得到该文件的文件偏移量,然后该文件偏移量所保存的位置往后来进行读取,那么读取的长度则是第三个参数是一个无符号整形的参数n,那么它会往后读取n个单位的长度,然后将其保存到一个任意类型的数组中,也就是read的第二个参数,因为文件的内容本质就是一个二进制序列,那么我们read本质则是是获取到该数据对应的二进制序列,那么至于怎么看待解析这个二进制序列,那么则具体看你保存的数组的数据类型

-write

头文件:<unistd.h>

函数原型:

size_t read(int fd, void *buf, size_t count);

4.close系统调用接口

那么我们既然有文件的打开,那么相应的我们就一定对应有文件的关闭,那么close系统调用接口就是关闭一个打开的文件,那么知道我们进程打开的所有文件,那么他们都会被保存带文件描述表当中,那么文件描述表本质上就是一个指针数组,那么其指向一个打开的文件的file结构体,那么我们关闭一个文件,那么就是将该文件在指针数组对应的下标位置的指针给置空,而该打开的文件的file结构体中还有一个属性,那么便是引用计数count,那么我们将该文件对应的指针数组置空之后,我们还为将该文件的引用计数给减一,因为一个文件它与进程的关系可不是一对一的,而是多对多的关系,我们每一个进程可以打开n个文件,那么每一个文件同理也可能被n个进程所打开,那么其中的引用计数count就是记录其被多少个进程所打开,那么一旦引用计数count为0,那么我们操作系统便会释放该文件的资源,其中就包括它在内存当中的元数据以及对应的file结构体

所以我们的close的参数就是一个在文件描述表中范围内指针不为空的数组下标,那么它就会关闭该下标对应的文件,关闭成功返回值为0,失败返回值为-1

-close

头文件:<unistd.h>

函数原型:

int close(int fd);

结语

那么这就是本篇文章的全部内容了,那么其中详细解析了文件的概念,以及与文件操作相关的几个系统调用接口,那么下一篇文章我还会在补充几个系统调用接口,那么有了文件知识储备的基础以及文件的系统调用接口,那么我们就可以重新来认识输出以及输入重定向,并且来完善我们的shell外壳程序

那么我会持续更新,希望你多多关注,如果本篇文章有帮组到你,那么还请三连加关注哦,你的支持就是我创作的最大的动力!在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CentOS 7.9 安装 ClickHouse 文档

1. 环境准备 确保系统为 CentOS 7.9&#xff0c;并已安装 Docker。如果未安装 Docker&#xff0c;请先安装 Docker。 安装 Docker # 卸载旧版本 Docker&#xff08;如果有&#xff09; sudo yum remove -y docker docker-client docker-client-latest docker-common docker-…

(链表 删除链表的倒数第N个结点)leetcode 19

设空结点指向head便于插入和删除结点 考虑特殊情况 head结点被删除 a结点仅用来测试长度&#xff0c;找到目标结点的位置 b结点为空结点指向head返回值 cur用来删除目标值&#xff08;特殊情况 目标值为head 这时curb) 则开始就将cur初始化为b开始遍历 /*** Definition fo…

电力杆塔倾斜监测装置:守护电网安全的智能卫士

​ ​电力杆塔作为电力传输的重要支撑结构&#xff0c;其安全性直接关系到电网的稳定运行和电力供应的可靠性。然而&#xff0c;由于自然环境的复杂性和外部因素的影响&#xff0c;杆塔倾斜、倒塌等问题时有发生&#xff0c;给电力系统带来了巨大的安全隐患。为了应对这一挑…

【单片机项目】电源如何扩展、电源模块、电池模块如何接线

一、前言 解决2个关键问题&#xff1a; 【1】如果项目编号小于172之前的项目。 可能会遇到电源模块不够接&#xff0c;需要扩展电源的问题。 【2】如果项目编号是大于 172之后项目&#xff0c;部分项目用到了稳压电源模块或者是电池模块。 这篇文章单独讲解一下如何接线。 …

NO.28十六届蓝桥杯备战|string|insert|find|substr|关系运算|stoi|stol|stod|stof|to_string(C++)

insert 如果我们需要在字符串中间的某个位置插⼊⼀个字符串&#xff0c;得掌握⼀个函数就是insert string& insert (size_t pos, const string& str); //pos位置前⾯插⼊⼀个string字符串 string& insert (size_t pos, const char* s); //pos位置前⾯插⼊⼀个…

贪心算法一

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是贪心算法&#xff0c;并且掌握贪心算法。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! >…

逐梦DBA:MySQL的编码设置

一、MySQL的编码设置 1.1 默认插入中文数据存在的问题 1.1.1 在 MySQL5.7 版本&#xff0c;默认在安装成功后存在中文乱码的问题 1. 通过 show create table xxx查看可以发现默认的字符集&#xff1a; 2. show variables like character_%;查看编码命令发现默认为拉丁 如果我…

Windows 图形显示驱动开发-WDDM 3.2-GPU-P 设备上的实时迁移(一)

本文介绍了通过 SR-IOV(单根 I/O 虚拟化)分区虚拟化的异构计算设备(GPU、NPU 等)实时迁移的功能设计。 通过 WDDM 和 MCDM 驱动程序模型支持分区的设备现已成为我们虚拟化产品不可或缺的一部分。 因此&#xff0c;必须支持实时迁移并帮助我们的虚拟化抽象实现最大程度的可靠性&…

张驰咨询:用六西格玛重构动力电池行业的BOM成本逻辑

在动力电池行业&#xff0c;BOM&#xff08;物料清单&#xff09;成本每降低1%&#xff0c;都可能改写企业的利润曲线。某头部企业的三元锂电池BOM成本曾较行业标杆高出11%&#xff0c;单电芯利润率被压缩至3%的生死线。然而&#xff0c;通过张驰咨询的六西格玛方法论&#xff…

Java 大视界 -- Java 大数据在智能政务公共服务资源优化配置中的应用(118)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

单例模式的五种实现方式

1、饿汉式 ①实现&#xff1a;在类加载的时候就初始化实例 ②优点&#xff1a;线程安全 ③缺点&#xff1a;实例在类加载的时候创建&#xff0c;可能会浪费资源 //饿汉式 public class EagerSingleton{private EagerSingleton(){} //私有构造方法private static EagerSingle…

WPS工具栏添加Mathtype加载项

问题描述&#xff1a; 分别安装好WPS和MathType之后&#xff0c;WPS工具栏没直接显示MathType工具&#xff0c;或者是前期使用正常&#xff0c;由于WPS更新之后MathType工具消失&#xff0c;如下图 解决办法 将文件“MathType Commands 2016.dotm”和“MathPage.wll”从Matht…

从开源大模型工具Ollama存在安全隐患思考企业级大模型应用如何严守安全红线

近日&#xff0c;国家网络安全通报中心通报大模型工具Ollama默认配置存在未授权访问与模型窃取等安全隐患&#xff0c;引发了广泛关注。Ollama作为一款开源的大模型管理工具&#xff0c;在为用户提供便捷的同时&#xff0c;却因缺乏有效的安全管控机制&#xff0c;存在数据泄露…

战略合作升级 | 大势智慧携手广西地测院,共绘智慧测绘新蓝图

2月26日&#xff0c;武汉大势智慧科技有限公司&#xff08;以下简称“大势智慧”&#xff09;与广西壮族自治区地理信息测绘院&#xff08;以下简称“广西地测院”&#xff09;在南宁举行战略合作升级签约仪式暨技术交流座谈会。 大势智慧董事长黄先锋与广西地测院党委书记、院…

MCU-SDRAM-W9825G6KH的存储单元

ARM-M7的Memory架构&#xff1a; 在Cortex-M7中&#xff0c;存储器一共有4GB的地址空间&#xff0c;4GB的地址空间又被划分为8个区域块&#xff0c;每个块有512M的内存。 Note&#xff1a;4GB的地址空间为 0x0000 0000 - 0xFFFF FFFF&#xff0c;可寻址的512M的地址空间为 0x00…

DeepSeek-R1国产化系统gpu驱动+cuda+ollama+webui可视化离线私有化部署

1.概述 网上大部分教程都是在线部署&#xff0c;完全离线私有化部署的文章不多&#xff0c;本文介绍从GPU驱动、cuda、ollama、deepseek模型和open webui等完全离线安装几个方面&#xff0c;让小白0基础也可以私有化部署大模型deepseek-R1。 我使用的设备是银河麒麟V10操作系统…

【蓝桥杯】每天一题,理解逻辑(3/90)【Leetcode 快乐数】

闲话系列&#xff1a;每日一题&#xff0c;秃头有我&#xff0c;Hello&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;,我是IF‘Maxue&#xff0c;欢迎大佬们来参观我写的蓝桥杯系列&#xff0c;我好久没有更新博客了&#xff0c;因为up猪我寒假用自己的劳动换了…

飞机大战lua迷你世界脚本

-- 迷你世界飞机大战 v1.2 -- 星空露珠工作室制作 -- 最后更新&#xff1a;2024年1月 ----------------------------- -- 迷你世界API适配配置 ----------------------------- local UI { BASE_ID 7477478487091949474-22856, -- UI界面ID ELEMENTS { BG 1, -- 背景 BTN_LE…

AI绘画软件Stable Diffusion详解教程(6):文生图、提示词细说与绘图案例

文生图即以文字描述来生成图像&#xff0c;这是目前所有AI绘画软件的基本功能之一。要想画一副好的图片&#xff0c;除了选择好的模型&#xff0c;在文生图中&#xff0c;提示词特别关键。 一、什么是提示词&#xff08;Prompt&#xff09; 提示词又称创意、关键词、咒语、ca…

C++编程:进阶阶段—4.1封装

C面向对象的三大特性&#xff1a;封装、继承、多态 具有相同性质的对象&#xff0c;抽象为类 4.1 封装 封装的意义&#xff1a;将属性和行为作为一个整体&#xff0c;表现生活中的事物&#xff0c;并将属性和行为加以权限控制。 4.1.1 类的定义及实例化对象 语法&#xff…