Linux查看一个文件的时候发生了什么?
概念辨析
整体的关系是这样的:
下面来逐个介绍图中的内容。
文件系统
- 磁盘文件系统:按照指定的格式和规则直接将数据存在磁盘中,Ext 2/3/4等等
- 拿到磁盘首先格式化为具体的磁盘文件系统,然后对磁盘分区。分区之后挂载到不同的操作系统目录(虚拟文件系统某一个节点),就可以使用了。
- 网络文件系统:类似于磁盘文件系统,也是特定场景特定格式的文件系统
- 虚拟文件系统:操作系统自己定义的规范,屏蔽不同磁盘文件系统的差异,提供统一接口给上层使用
虚拟文件系统
也就是vfs,看做是一棵树:
这个树上的节点就是我们常见的文件夹名字,虚拟目录树是Linux的一个规范,结构是稳定化的。至于是哪一个文件系统挂载到这个树上的哪个节点是可以灵活改变的!!!
df -h 查看虚拟目录树挂载的真正物理地址。磁盘分区(也就是文件系统。都按照一定的文件系统规则进行了格式化)挂载到VFS 树的不同目录节点,其中系统启动会将内核的镜像文件系统(图中的/dev/sda1)加载之后,挂载到虚拟节点 /boot 下,会将操作系统(图中的/dev/mapper/centos-root)的文件系统挂载到虚拟节点 / 下,所以其实是 /dev/sda1 的文件系统覆盖了 /dev/mapper/centos-root 对于虚拟节点 /boot 的挂载,可以自己手动去除虚拟节点 /boot 上文件系统/dev/sda1 的挂载,得到 /boot 就是空的目录了。
文件描述符
- 进程PCB
- 进程控制块,进程存在的唯一标识,包含进程的状态,优先级,打开文件列表,内存空间地址信息,CPU相关的寄存器值。
- 文件描述符数组:存放文件指针
- 系统为每一个进程维护了一个文件描述符表,表示该进程打开文件的记录表。
- 文件描述符数组大小限制默认1024,也就是一个进程默认打开1024个文件,可以使用
ulimit -SHn 65535
临时修改,也可以修改文件/etc/security/limits.conf
永久生效,在最后一行加入- nofile 65535
- 注意建立的每一个socket网络连接也是一个个文件,都会占用文件描述符
- 文件描述符FD:是一个非负整数,从 0 开始,就是文件描述符数组的索引。标识一个打开的文件。
- 默认情况,Linux会打开是三个文件:标准输入、标准输出、标准错误,分别使用0、1、2号文件描述符。
- FD是进程级别的,inode是系统级别的,多个进程指向同一个文件时候,FD可以相同或者不同,都借助FD数组的值,指向同一个inode,就可以找到磁盘上对应的文件数据块。
- 当进程打开(open)或者新建(create)文件时,内核会在该进程的文件列表中新增一个表项,同时返回一个文件描述符 —— 也就是新增表项的下标。
- 每个进程默认都有 3 个文件描述符:0 (stdin)、1 (stdout)、2 (stderr)。
- 打开文件表
- 打开文件表是系统级别的,多个进程共用。用来存放系统打开的文件对象。
- 文件对象:就是打开文件表中的每一项。
索引节点与文件数据块
- 虚拟节点 vnode
- v-node 是虚拟文件系统对应的文件节点,
- 索引节点 inode
- i-node是磁盘文件系统对应的文件节点。
- 文件数据块
- 通过上面两个节点就能找到最终的磁盘文件。
文件目录项dentry
只存在于内存中,不会持久化到磁盘中。表示的是文件夹和文件之间的关系。会记录文件名。信息来源就是硬盘中的目录文件索引节点和数据块。
上结论
- 进程启动
- 内核空间创建PCB进程控制块。PCB自己维护打开文件的文件描述符数组,默认情况,Linux会打开是三个文件:标准输入、标准输出、标准错误,分别使用0、1、2号文件描述符。
- 执行open方法打开文件
- 通过文件目录项找到对应的文件
- 内核空间申请file结构体,也就是打开文件表中创建一个entry
- FBarray[3] 也就是3号文件描述符对应文件指针指向此结构体
- 此文件结构体内部有指针指向v-node,就可以找到i-node,就可以找到磁盘上的数据
- 返回这个3号文件描述符
- 执行read方法读取数据
- 使用文件描述符读取数据,就是利用指针一直往下找知道找到数据块即可。
总结
上面涉及到的某些概念在学校的操作系统课程中不会讲解,但是在实际的操作系统中是真实存在的,理解整个流程,对于后续理解IO相关的知识有好处。下一篇就来看看和IO相关的知识:同步异步阻塞非阻塞的关系。