【Linux 文件系统全景解读:从 printf 到磁盘的完整旅程】

发布时间:2026/5/27 0:17:56

【Linux 文件系统全景解读:从 printf 到磁盘的完整旅程】 文件描述符、重定向、缓冲区、inode、软硬链接——一条线串起文件 IO 的整个脉络文章目录前言第一站一个最简单的程序发生了什么第二站文件描述符与三个默认打开的文件第三站重定向的本质第四站缓冲区——为什么有时数据不立刻显示第五站Linux 下“一切皆文件”第六站磁盘上的文件系统6.1 文件 内容 属性6.2 文件系统的布局ext 系列6.3 查找文件的过程6.4 为什么删除文件比拷贝快得多6.5 从根目录开始逐级查找6.6 dentry 缓存内存中的目录树第七站分区、inode 编号与挂载7.1 分区是文件系统的载体7.2 挂载把分区挂到目录树上第八站软链接与硬链接8.1 软链接符号链接8.2 硬链接8.3 硬链接数与.、..8.4 软硬链接完整对比表总结整条主线回顾前言Hello,大家好我是小J。这篇文章我会沿着一条主线带你走完一个文件从被进程打开、写入数据、到最终存储在磁盘上的完整过程看完之后你会搞懂· printf 到底把数据写到了哪里· 重定向 为什么能让输出去文件· 缓冲区是什么为什么有时候数据没立刻显示· 磁盘上的文件是怎么组织的为什么删除比拷贝快得多· 软链接和硬链接的本质区别是什么准备好了吗我们从最简单的 C 程序开始。第一站一个最简单的程序发生了什么#includestdio.hintmain(){printf(hello\n);return0;}当你运行这个程序printf 输出到屏幕。但屏幕不是 C 语言的一部分是操作系统管理的硬件。所以 printf 必须借助操作系统。核心事实 1C 语言的 printf、fopen、fclose 等函数底层都调用了操作系统的系统调用如 write、open、close。库函数只是方便我们使用的“包装器”。核心事实 2操作系统不认 FILE*只认文件描述符一个非负整数。C 语言的 FILE 结构体内部封装了文件描述符。第二站文件描述符与三个默认打开的文件每个进程启动时操作系统会自动打开三个文件流文件描述符对应设备stdin0键盘stdout1显示器stderr2显示器在进程的 task_struct 中有一个指针数组 fd_array[]下标就是文件描述符。下标 0、1、2 已经被占用了。当你调用 open(“file.txt”, O_RDONLY)系统会分配当前可用的最小文件描述符通常是 3并在 fd_array[3] 中记录这个文件的信息。 所以文件描述符只是一个下标内核通过它找到对应的文件。第三站重定向的本质我们经常用 ./a.out out.txt 把输出重定向到文件。这是怎么做到的·正常情况下printf 往文件描述符 1stdout写数据而文件描述符 1 指向显示器。·重定向时shell 先 close(1)然后 open(“out.txt”, …)。因为文件描述符 1 刚好被关闭新打开的 out.txt 会获得最小的可用 fd也就是 1。· 此后printf 仍然往 fd1 写但 1 已经指向 out.txt 了。结论重定向的本质是修改文件描述符下标指向的目标不改变程序代码。 比喻你打电话给“1号键”快捷联系人。本来 1 号键是妈妈重定向就是把 1 号键改成爸爸。你仍然按 1 号键但接通的人变了。第四站缓冲区——为什么有时数据不立刻显示试一下这个代码#includestdio.h#includeunistd.hintmain(){printf(hello);sleep(2);return0;}你发现 hello 不会立刻显示而是等 2 秒后才出现。为什么因为printf 先把数据放到了缓冲区内存中的一块区域缓冲区满了或遇到 \n 或程序结束时才调用系统调用 write 真正输出。为什么要有缓冲区· 系统调用比较慢涉及用户态→内核态切换· 攒一批数据一次性写入效率远高于写一次就调用一次 比喻你有一堆快递要发。每次一件就跑去邮局系统调用很累攒到 10 件一起去缓冲区效率高。C 语言标准库的缓冲策略场景缓冲类型标准输出到终端行缓冲遇到\n刷新标准输出重定向到文件全缓冲缓冲区满才刷新标准错误无缓冲立即输出所以重定向到文件时printf 不带 \n 可能一直不显示。第五站Linux 下“一切皆文件”键盘、显示器、磁盘、网卡……在 Linux 中都被抽象成文件。每个设备驱动提供 open、read、write 等标准接口内核用统一的 struct file 表示。这就是为什么你可以用 read 从键盘读用 write 向屏幕写——它们都是文件。 类比USB 接口统一了各种外设。无论插鼠标还是 U 盘同一个插口。第六站磁盘上的文件系统以上讨论的都是打开的文件在内存中。但文件大部分时间躺在磁盘上。磁盘上的文件是如何组织的6.1 文件 内容 属性部分存储位置说明内容数据块Data Blocks文件的实际数据属性inode索引节点文件大小、权限、时间戳、数据块指针等每个文件有一个唯一的 inode 编号。文件名不在 inode 中而是存在目录文件的数据块里。为什么这样设计这主要是为了解耦和灵活性一个文件可以有多个名字硬链接如果文件名直接存在 inode 里一个文件就只能有一个名字。而将文件名存在目录项中允许同一个 inode 被多个目录项即多个文件名指向这就是硬链接的实现基础。便于文件重命名和移动重命名或移动文件在同一文件系统内时只需修改目录文件数据块中的条目inode 本身包含文件大小、权限、数据块位置等关键属性完全不变操作非常高效。目录结构独立于文件数据目录本质上也是一个文件它的数据块里存储的是“文件名 → inode 编号”的映射表。这种设计让目录管理创建、删除、遍历和文件内容管理分离结构更清晰。简单来说inode 是文件的“身份证”存储文件的核心元数据目录是“电话簿”存储文件名到身份证号inode编号的映射。这种分离让文件系统更灵活、高效。 比喻inode 是身份证固定信息数据块是你的家实际住所文件名是别人叫你时用的昵称。6.2 文件系统的布局ext 系列一个分区被划分为多个块组Block Group每个块组包含组件作用Super Block整个文件系统的元信息块大小、总 inode 数等GDT块组描述符表记录每个块组的位置inode Bitmap位图标记哪些 inode 已使用Block Bitmap位图标记哪些数据块已使用inode Table存放 inode 的数组Data Blocks存放文件内容的块6.3 查找文件的过程要访问 /home/user/a.txt从根目录 / 的 inode 找到它的数据块在里面查找 home 对应的 inode进入 home 的 inode找到数据块查找 user 对应的 inode进入 user 的 inode找到数据块查找 a.txt 对应的 inode得到 a.txt 的 inode读取其数据块指针找到实际内容️ 就像根据地址找一户人家先到小区根目录再找楼栋home再找单元user最后找门牌号a.txt。6.4 为什么删除文件比拷贝快得多·拷贝需要分配新的 inode 和数据块还要逐块复制数据。·删除只需要在 inode Bitmap 和 Block Bitmap 中把对应位从 1 改成 0标记为空闲数据本身并不被擦除。⚡ 这就是删除几乎瞬间完成的原因只是在“地图”上把这块地标记为无人使用而不是把地铲平。6.5 从根目录开始逐级查找我们说过要访问 /home/user/a.txt必须从根目录 / 开始逐级解析问题来了每次访问文件都要这样从磁盘读取目录数据岂不是效率极低尤其是频繁访问的目录如 /usr/bin想知道操作系统是怎么解决的吗继续往下看吧。6.6 dentry 缓存内存中的目录树操作系统会把历史访问过的目录路径在内存中构建成一棵多叉树这棵树被称为目录项缓存dentry cache内核中使用 struct dentry 结构体表示每个目录项。概念说明struct dentry目录项结构体记录目录名与 inode 的映射关系以及父目录、子目录的指针dentry cache内核中缓存的目录树位于内存中避免反复读磁盘效果· 第一次访问某个文件时需要从磁盘加载路径上的所有目录较慢· 之后再次访问同一路径直接从内存中的 dentry 树获取极快 这就是为什么 ls 第二次执行比第一次快——目录已经被缓存了。第七站分区、inode 编号与挂载7.1 分区是文件系统的载体一个磁盘可以分成多个分区每个分区独立格式化成一个文件系统如 ext4,这部分内容我后续会发文章详细讲讲。在每个分区内部inode 编号和数据块编号是唯一的但不同分区之间可以重复比如分区 A 有 inode 100分区 B 也可以有 inode 100。7.2 挂载把分区挂到目录树上问题访问文件时系统怎么知道这个文件属于哪个分区答案通过挂载mount。挂载就是把一个分区关联到目录树中的某个目录。当你访问该目录及其子目录时实际上是在访问这个分区。例如mount/dev/sda1 /home之后所有 /home 下的文件都存储在 /dev/sda1 分区中。 比喻分区就像一块硬盘物理区域挂载就是给这块硬盘贴上一个门牌号目录。你走进这个门牌号就进入了这块硬盘。第八站软链接与硬链接8.1 软链接符号链接ln-starget link_name· 是一个独立的文件有自己的 inode· 文件内容里存储目标文件的路径· 类似于 Windows 的快捷方式· 可以跨分区· 原文件删除后软链接失效断链8.2 硬链接lntarget link_name· 不是独立文件没有新 inode· 只是在目录中增加一条文件名 → 原 inode 的映射· 原 inode 有一个硬链接计数每增加一个硬链接计数 1· 只有当计数变为 0 时文件才真正被删除· 不能跨分区因为 inode 编号只在分区内唯一举例echohellofile.txtlnfile.txt hardln-sfile.txt softrmfile.txtcathard# 还能输出 hellocatsoft# 报错No such file or directory8.3 硬链接数与.、…每个 inode 有一个硬链接计数link count表示有多少个文件名指向这个 inode。当你创建一个文件时硬链接数为 1。echohellofile.txtls-lfile.txt# 输出中第二列就是硬链接数通常为 1特殊目录的硬链接· 每个目录除了根目录都有一个 . 条目指向自己因此硬链接数至少为 2父目录的链接 自身的 .· 每个目录还有一个 … 条目指向父目录增加父目录的硬链接数所以你用 ls -l 看一个目录时硬链接数往往大于 2包含子目录中的 … 也会增加父目录的计数。 理解硬链接数就像一个门牌号的数量。一个房间inode可以有多个门牌号文件名。rm 只是撤掉一个门牌号只有当最后一个门牌号被撤掉房间才真正被拆除。8.4 软硬链接完整对比表特性软链接硬链接独立 inode✅ 是❌ 否内容存储目标路径无仅映射跨分区✅ 能❌ 不能inode 号只在分区内有效原文件删除后失效断链仍然有效只要还有其它硬链接可链接目录一般可以需权限通常不允许避免循环链接数影响不增加目标 inode 的链接计数增加目标 inode 的链接计数典型用途快捷方式、版本切换文件备份、别名对应 Linux 命令ln -s target linkln target link总结整条主线回顾用户程序调用 printf库函数数据进入 C 标准库的缓冲区条件触发缓冲区满/换行/程序结束→ 调用系统调用 write系统调用根据文件描述符找到内核中打开的文件结构如果文件描述符指向显示器数据最终输出到屏幕如果重定向过指向普通文件普通文件的数据最终通过文件系统写入磁盘磁盘上的文件通过 inode 和目录项组织删除只改位图希望这篇文章能让你有所收获有不理解的地方发在评论区我会尽己所能的解答。

相关新闻