【Linux 系统】打开文件和文件系统

文章目录

  • 前言
  • [1. 物理内存和文件系统的交互](#1. 物理内存和文件系统的交互)
  • [2. 打开一个文件的过程](#2. 打开一个文件的过程)

前言

在 Linux 系统中尽管我们对文件系统有所了解,我们仍然对一个文件的打开过程存在一些疑问。这篇文章,就是用来帮助小编自己大致来梳理一下文件系统的一些流程。如何打开一个文件,底层操作系统到底干了什么。

1. 物理内存和文件系统的交互

磁盘的扇区 大小一般都是4KB,这是硬件支持的。同时我们一般的内存空间也是以 4KB 为一个页框被作为一个数据交互的最小单位 。这里有非常多的考量:IO效率的考虑局部性原理......考虑。我们这里不多探讨。

页框和页帧的概念比较接近,多用于描述物理内存;页面多用于描述虚拟内存。

操作系统必然需要对内存进行管理 。操作系统管理内存的方式必然是:"先描述,再组织"。

简单来看,系统描述内存空间就可以这样

c 复制代码
//每一个页框的描述
struct page
{
	long long start;
	long long end;
	//...
};

//组织
struct page m[1048576];

简单来看:我们访问内存就是访问描述内存空间的数组对应的内容。

交互

在操纵系统启动的时候就需要把文件系统中的诸多信息加载到内存中。例如,一个非常关键的字段:super block。这个是文件系统的管理机制,所以操作系统需要将这样加载起来的超级块管理起来。

同时操作系统还会缓存一些 inode 。将一些文件的 inode 信息在内存中缓存起来,这样可以避免后期在进行 IO 请求的时候还需要进行多余的于磁盘进行IO交互获取文件的 inode。

同时操作系统还会缓存一些 dentry 。这个就是目录项的内容 ,里面存放的内容就是一些 inode 编号和文件名的映射关系!

2. 打开一个文件的过程

简单来说,当你在 Linux 中执行 open("/home/user/test.txt", O_RDWR) 这样的系统调用时,内核会

  1. 解析路径:从根目录 / 开始,逐级查找到 test.txt 对应的 inode。

  2. 权限检查:检查当前进程是否有权限打开这个文件。

  3. 创建内核数据结构:在内存中为这次"打开"操作创建相关的内核数据结构和文件描述符。

  4. 返回文件描述符:将一个整数返回给应用程序,后续的读、写等操作都通过这个描述符进行。


整个过程可以分为以下几个关键阶段

  • 阶段一:用户空间发起系统调用

    1. 系统调用 :上层打开文件最后都会使用系统调用 open

    2. 触发软中断open 函数会将系统调用号、文件路径、打开标志等参数放入特定的寄存器。(准备从用户态转换到内核态

    3. 执行 syscall 指令:执行 syscall 指令,导致 CPU 从用户态切换到内核态。控制权交给内核中一个预定义的入口点。

  • 阶段二:内核虚拟文件系统处理

    1. 路径查找 :这是最复杂的步骤之一。内核从当前进程的根目录 (通常是 /)或当前工作目录开始,逐分量解析路径 /home/user/test.txt。(如果是相对路径,仍然是同样的道理)
      • 首先查找根目录 /inode

      • / 的目录项(dentry)缓存中查找名为 home 的条目,找到其对应的 inode 编号,找 inode缓存,如果不存在就进行磁盘IO读取信息,然后对权限进行检查。如果权限合理,我们就可以继续拿着 home的 inode 找到其对应的 dentry缓存(缓存没有命中就 IO 加载)...... 直到找到对应文件的 inode 编号

        注意:为了提高效率,内核维护了一个目录项缓存(dentry cache),如果路径的某一部分已经在缓存中,就可以跳过磁盘 I/O,极大地加速查找过程。

  • 阶段三:内核数据结构准备

    1. 构建目标文件的 inode对象 。内核获取目标文件的 inode编号之后,还是会先检查 inode缓存中是否有对应的内核数据结构。如果没有就从磁盘进行加载,在内核中创建一个 inode对象,并放入 inode缓存中(未来极有可能会再次访问到这个文件)。同时内核还会为用户维护一个文件页缓冲区,这个文件页缓冲区可以理解为存储的是一个个 struct page 结构体。上层写入的内容可以写到文件的页缓冲区中。后面再写入文件的时候,可以统一写入到文件中。

    2. 构建 struct file。同时分配文件描述符,在当前进程的文件描述符表中,找到一个空闲的最小编号的槽位,将新创建的 struct file 对象的指针填入该槽位。这个槽位的索引号就是返回给用户空间的文件描述符。

  • 阶段四:返回用户空间

    1. 系统调用返回:内核完成所有工作后,将文件描述符放入结果寄存器)。

    2. 切换回用户态:内核执行特殊的指令(如 sysret),将 CPU 从内核态切换回用户态。

    3. 系统调用返回:从寄存器中取出结果,将其作为 open() 函数的返回值传递给应用程序。


路径解析循环 通过 通过 拒绝 拒绝 从缓存或磁盘
加载该目录的node对象 对路径中的每个目录
获取其node编号 检查对目录的
执行权限 读取目录内容
查找下一分量 获得下一项的node编号 系统调用开始
open('/home/user/test.txt', flags) 路径解析
逐分量查找 循环直至获得
目标文件的node编号 从缓存或磁盘
加载目标文件的node对象 检查对目标文件的
请求权限(r/w) 创建file对象
分配文件描述符 返回文件描述符(fd) 返回错误 EACCES

  • 总结:

    打开一个文件的过程,本质上是内核为应用程序访问文件建立一条"连接通道"的过程。

    最后如下图:

相关推荐
DeeplyMind3 小时前
第二章:模块的编译与运行-7 Loading and Unloading Modules
linux·驱动开发
---学无止境---4 小时前
Linux中驱动程序通过fasync异步通知应用程序的实现
linux
cccyi74 小时前
Linux 进程间通信机制详解
linux·进程通信
北京迅为4 小时前
【北京迅为】iTOP-4412精英版使用手册-第三十五章 WEB控制LED
linux·嵌入式硬件·嵌入式·4412
让我们一起加油好吗4 小时前
【C++】封装红黑树模拟实现 set 和 map
linux·c++·set·map·红黑树
sg_knight4 小时前
Spring Cloud与RabbitMQ深度集成:从入门到生产级实战
java·spring boot·spring·spring cloud·消息队列·rabbitmq·stream
暴富奥利奥4 小时前
完成docker方式的ros环境配置
linux·学习·docker·容器
秃头菜狗4 小时前
十四、运行经典案例 wordcount
大数据·linux·hadoop
ManageEngineITSM4 小时前
IT 服务自动化的时代:让效率与体验共进
运维·数据库·人工智能·自动化·itsm·工单系统