文件底层的深入理解(1)

简介: 文件底层的深入理解(1)

一、文件学习的简单铺垫

       我们都知道,文件等于文件内容加上文件属性。访问文件之前都得先通过进程才能打开相应的文件,一个进程可以打开多个文件。修改文件,都是通过执行代码的方式完成修改。要对文件进行修改(或其他操作),文件就必须事先被加载到内存当中。从前面的叙述我们可以知道,在一定的时间段内,系统可能呢存在多个进程,也一定会存在多个被打开的文件,操作系统通过先描述再组织的方式对这些文件进行统一的管理。所以,在操作系统内部,一定会存在描述被打开文件的结构体,并用其定义对象。没有被打开的文件就存储在磁盘当中。

二、对文件路径产生与由来的理解

       从上面的铺垫可以看出,文件与进程一定是密切相关的,下面我启动一个进程,在这个进程中创建了一个文件并向其中写入内容:

并获得该进程的pid:

       在proc目录中找到该进程对应的所有属性,其中就可以看到该进程所处的目录。进程在打开时,会自动记录它当前所处的目录,所以在该进程中创建的文件如果不带路径,那么自然而然地就被创建在了进程所在的目录中。这就是文件路径的产生与由来。

三、文件的系统调用接口

       我们都知道,上层的语言是无法直接访问底层的硬件的,要访问硬件必须通过操作系统,所以操作系统就必须提供所谓的系统调用函数来供用户使用。那为什么我们看不到系统调用函数呢?原因是因为在语言层面上已经封装了系统调用函数。就比如说fopen函数就封装了open函数。下面我会以open函数对系统调用函数做一些简单的介绍。

open函数

       我们可以看到open函数中有flags这个参数,其实flags这个参数你可以把它看成32个比特位,其中每一个比特位代表不同的权限(类似于位图),所以我们就可以通过与或操作对flags设置不同的值,进而设置文件的读写权限。

       mode是权限的意思,如果一个文件事先还没有被创建出来,就必须在创建的时候通过设置mode指明它的权限。

       O_WRONLY表示只写权限位(注意这里的只写与C语言中的w权限不一样,O_WRONLY并不会将原有的文件内容清空再写入,而是直接在文件的开头进行覆盖式写入,如果要将原有的文件内容清空再写入需要再或上O_TRUNC),O_CREAT表示文件不存在就创建权限位,0666表示我设置的权限位(也会受umask掩码的约束),如果一个文件本来就已经存在了,在打开的时候就不用传递权限了。这样也可以创建出一个文件。

四、文件描述符详解

       文件描述符就是指的上面代码中open函数返回的fd这个int类型的值。在C语言中,描述文件的FILE类型其实就是一个结构体,该结构体中封装了特定的fd。在系统当中,一定会存在多个被打开的文件,操作系统通过先描述再组织的方式(创建出struct file结构体)对这些文件进行统一的管理,而一个进程也可能会打开多个文件,所以在描述进程的task_struct结构体中就存在一个指针struct files_struct* files,这个指针指向一个结构体,在进程创建的时候这个结构体就被创建出来了,在这个结构体中包含了很多字段,其中就有一个struct file* fd_array数组(也叫做文件描述符表),保存了该进程所打开的所有文件的地址,这样,该数组的数组下标就和文件的地址产生了一一对应的关系,而这里的数组下标就是fd。

       所以我们打开文件的本质就是先在磁盘当中找到这个文件,然后将其内容和属性加载到内存当中,创建出struct file结构体,将结构体内的属性、方法、缓冲区全部初始化,再将struct file结构体链入到操作系统管理被打开的struct file结构体链表中,再将struct file结构体的地址填入到对应进程中的struct file* fd_array数组,最后将数组下标返回给用户。最后我们总结一下:其实文件描述符就是数组下标。正如下面这个图所示:

       所以现在我们就可以大致说一下,向文件中写入内容(write函数),本质就是通过fd找到对应的struct file结构体,将数据写入struct file结构体对应的缓冲区中,再由操作系统帮我们把数据写入磁盘当中。从文件中读内容(read函数),本质就是通过fd找到对应的struct file结构体,从该结构体的缓冲区中读取数据,如果缓冲区中没有对应的数据,操作系统就会从磁盘中加载对应的数据到缓冲区中,再将数据根据上层给的地址拷贝到内存当中,文件的内容就被读到了。上面所述内容在操作系统源代码中也是可以得到验证的。

五、总结:理解一切皆文件

       各个硬件在底层的读写方法肯定都是不一样的,在创建struct file结构体时,需要创建read和write两个函数指针,在调用不同的硬件时,只需要让函数指针指向不同的函数,返回的是都是read或write方法,从而屏蔽了底层硬件上的差异,使得linux下一切都可以当成文件来看待。如图所示:

相关文章
|
存储 C语言 C++
66 C++ - 流的概念和流类库的结构
66 C++ - 流的概念和流类库的结构
78 0
|
运维 负载均衡 网络协议
从底层技术来看,GSLB 究竟难在哪儿
本文作者吕宏利来自硅谷的SRE,有着多年的国内外大型互联网公司运维开发经验,专注于分布式系统设计、监控、容量规划,数据中心技术以及生产环境的最佳实践。在本文中他将他将向读者介绍什么是GSLB,以及实现细节和维护方法。
7839 0
|
2月前
|
索引 Python
列表作为序列型对象都支持哪些操作,它们在底层是怎么实现的?
列表作为序列型对象都支持哪些操作,它们在底层是怎么实现的?
50 4
|
2月前
|
存储 索引 Python
字典是怎么实现的,它的底层结构长什么样子?
字典是怎么实现的,它的底层结构长什么样子?
63 2
|
7月前
|
SQL 存储 关系型数据库
sql数据库的相关概念与底层介绍
sql数据库的相关概念与底层介绍
89 0
|
6月前
|
存储 SQL 算法
链表:一种灵活的数据存储方式
链表:一种灵活的数据存储方式
|
6月前
|
机器学习/深度学习 人工智能 分布式计算
PAI底层支持多种计算框架
PAI底层支持多种计算框架:
122 0
|
7月前
|
存储 安全 Linux
C++文件格式深度解析:从底层结构到关键特性
C++文件格式深度解析:从底层结构到关键特性
426 3
C++文件格式深度解析:从底层结构到关键特性
|
7月前
|
C语言 C++
文件底层的理解之缓冲区
文件底层的理解之缓冲区
|
7月前
|
存储 NoSQL MongoDB
请解释一下文档存储数据库的工作原理,并提供一个使用文档存储数据库的实际应用场景。
请解释一下文档存储数据库的工作原理,并提供一个使用文档存储数据库的实际应用场景。
125 0