Skip to content

文件元数据与索引节点

2026 大纲 四(一)2 文件元数据和索引节点(inode)

那些跟着文件本体走的信息,装在哪

上一节把文件的属性分成了两拨:跟着名字走的(文件名、标识符) 和跟着本体走的(类型、位置、大小、权限、时间戳、链接计数)。

这一节讲后一拨装在哪。装它们的那个结构,最初的名字叫 FCB(文件控制块)—— 它是文件存在的唯一标志,含基本信息、存取控制信息、使用信息三类字段。 FCB 的有序集合就是文件目录,一个 FCB 就是一个目录项

问题出在"一个 FCB 就是一个目录项"这句话上。

想一想按路径找文件时到底在干什么:从根目录开始,一级一级地把目录文件读进内存, 逐条比对文件名。这个过程从头到尾只用到文件名一个字段—— 权限、大小、时间戳、块指针在比较文件名时一概用不上, 却被一起搬进了内存,白白挤占每个盘块能装的目录项数。

盘块就那么大。目录项越胖,一个盘块装的目录项越少, 同样一次磁盘 I/O 能比对的文件就越少,检索就越慢。

于是把 FCB 拆开:目录项只留 (文件名, inode 号),其余全部搬到 inode 里去。 表项瘦下来,一次 I/O 覆盖的文件数就按表项大小之比放大。 代价是命中之后要多读一次 inode 所在的盘块——只算检索那一段会高估收益。

这一节剩下的内容,基本都是从"inode 里装什么、不装什么"这一个问题上长出来的。 它里面不放文件名,也不放自己的编号,两处"不放"各自有很硬的理由, 而且各自推出一串结论。

一、文件控制块 FCB

字段类别包含信息
基本信息文件名、文件物理位置、文件逻辑结构、文件物理结构
存取控制信息文件所有者、访问权限(读/写/执行)
使用信息创建时间、最后修改时间、最后访问时间、当前使用状态

二、索引节点 inode

不拆分时,一个目录就是一串完整 FCB,检索一个文件名要把目录文件的盘块一块一块读进内存逐个比较文件名。Unix 的做法是把 FCB 劈成两半:目录项只保留 (文件名, inode 号)——检索时用得上的东西;索引节点 inode 装其余全部元数据——只有命中之后才需要的东西。inode 就像房产证,记录了房子的面积、位置、权限,但不包含家具(数据),也不包含门牌号(文件名)。

c
// inode 的典型结构(简化)
struct inode {
    uint16_t  i_mode;      // 文件类型与权限 (rwxrwxrwx)
    uint16_t  i_uid;       // 文件所有者 ID
    uint32_t  i_size;      // 文件大小(字节)
    uint32_t  i_atime;     // 最后访问时间:读了内容就更新
    uint32_t  i_mtime;     // 最后修改时间:改了内容才更新
    uint32_t  i_ctime;     // inode 修改时间:改了元数据就更新
    uint16_t  i_links;     // 硬链接计数
    uint32_t  i_blocks;    // 占用的磁盘块数
    uint32_t  i_direct[12];    // 12 个直接块指针
    uint32_t  i_indirect;      // 一次间接块指针
    uint32_t  i_double_indirect; // 二次间接块指针
    uint32_t  i_triple_indirect; // 三次间接块指针
};

注意里面没有文件名字段,也没有"我是几号 inode"这个字段——这两处缺席各自有原因,分别在第四、五节讲。

拆分到底省下多少次磁盘 I/O:一组参数下的逐步换算(想看清"表项变小"怎么落到 I/O 次数上时展开)

设盘块大小 4 KB。不拆分时一个完整 FCB 占 128 B;拆分后目录项只存 (文件名, inode 号) 共 32 B。某目录下有 1024 个文件,目录内容顺序存放、按名顺序查找。

第 1 步:算每个盘块能装几个表项。 目录是按盘块读进内存的,一次 I/O 能带回多少个候选名字,完全由"块大小 ÷ 表项大小"决定——这就是拆分真正起作用的地方

  • 不拆分:4096÷128=32 个 FCB/块
  • 拆分后:4096÷32=128 个目录项/块

第 2 步:算目录文件占多少个盘块。 表项缩到 14,块数也就缩到 14

  • 不拆分:1024÷32=32
  • 拆分后:1024÷128=8

第 3 步:算平均要读入几个盘块。 顺序查找命中位置均匀分布时,最好情况第 1 块就命中、最坏情况读完第 N 块,平均取两端之和的一半 N+12

  • 不拆分:32+12=16.5
  • 拆分后:8+12=4.5

第 4 步:别忘了拆分多出来的那一次。 拆分后拿到的是 inode 号,还要再读一次 inode 所在的盘块才能得到大小、权限、块指针:4.5+1=5.5 次。拆分把一次 I/O 拆成了"检索"和"取元数据"两段,只算检索段会高估收益。

结论16.55.5,降为原来的 13;若只比较目录检索本身则是 16.54.5目录里文件越多,N 越大,那个固定的 +1 越可以忽略,收益越接近表项大小之比 12832=4 倍。

三、磁盘 inode 与内存 inode

磁盘 inode 存放在磁盘的 inode 区域中,是 inode 的持久化形式;文件被打开时读入内存成为内存 inode,并增加一些管理字段:

附加字段说明为什么磁盘上不需要
inode 编号内存中要显式记录磁盘上由存放位置隐含,见第五节
引用计数当前有多少进程打开了该文件关机后没有"正在打开"这回事
状态标志是否被修改过(脏位)、是否被锁定脏位描述的是"内存与磁盘不一致",磁盘那份就是基准
设备号该 inode 所属的文件系统/设备磁盘 inode 本来就躺在那个设备上

四、inode 里为什么不放文件名

因为文件名不属于文件本体,它属于"某个目录里的某个入口"。把名字留在目录项里,带来四条后果:

  • 一个 inode 可以对应多个文件名——多个目录项写同一个 inode 号即可,inode 本身一个字节都不用改,这就是硬链接
  • 删除一个文件名只是删除一个目录项,链接计数减 1
  • 只有当链接计数减为 0 且没有进程打开该文件时,才真正释放 inode 和数据块
  • 改名、移动文件全部只动目录项,inode 原封不动

五、inode 号怎么映射到磁盘位置

文件系统在格式化时就把 inode 区划成一段连续的、定长表项的数组,于是 inode 号就是这个数组的下标

inode 的字节地址=inode 区起始地址+inode 号×inode 大小

换算成"第几块 + 块内偏移"(设 inode 号从 0 起):

每块 inode 数=块大小inode 大小,块号=起始块号+inode 号每块 inode 数,块内偏移=(inode 号mod每块 inode 数)×inode 大小

举个数:inode 区从第 8 块开始,块大小 4 KB,每个 inode 128 B。则每块装 4096/128=32 个 inode;第 200 号 inode 在第 8+200/32=14 块,块内偏移 (200mod32)×128=1024 B,即字节地址 14×4096+1024=58368

考点速记

  1. FCB 是文件存在的唯一标志,含基本信息 / 存取控制信息 / 使用信息三类字段;FCB 的有序集合就是文件目录,一个 FCB 就是一个目录项。⚠️文件的访问控制信息就存在这里(拆分后落在 inode 一侧),不在文件分配表、不在用户口令表、不在系统注册表。
  2. 拆分 FCB 的动机:目录检索从头到尾只用到文件名一个字段,其余字段却被一起搬进内存、白白挤占每个盘块能装的目录项数。拆后目录项只留 (文件名, inode 号)
  3. 拆分省的是目录检索的磁盘 I/O——表项越小,一次 I/O 覆盖的文件越多。顺序查找、命中位置均匀分布时平均读入盘块数为 N+12N 为目录文件占的盘块数);⚠️ 拆分后还要多读一次 inode 所在的盘块,只算检索段会高估收益。
  4. 三个时间戳的分界(判据:"被动过的是正文还是档案"):i_atime 了内容就变;i_mtime 改内容才变;i_ctimeinode 本身任何字段就变。⇒ 改内容必然同时改 mtime 与 ctime,只改权限则只有 ctime 变
  5. 链接计数 ≠ 引用计数链接计数数的是有多少个目录项指向它,存在磁盘 inode 里,关机仍有效;引用计数数的是有多少个进程开着它,只在内存 inode 里,关机即消失。
  6. 回收 inode 与数据块要求两个计数同时为 0。所以"删掉了文件但空间没释放"是完全正常的中间状态——名字没了但还有进程开着,要等最后一个 close
  7. inode 里不放文件名:名字属于"某个目录里的某个入口",不属于文件本体。留在目录项里的代价是零,收益是一个 inode 可挂多个名字(硬链接)、改名与移动完全不动 inode
  8. inode 里也不放自己的编号:编号由位置唯一确定——inode 区是一段连续的、定长表项的数组地址=起始地址+inode 号×inode 大小。读进内存后脱离了原来的位置,才必须补一个编号字段。
  9. 这条公式直接推出三件事:① inode 号全分区唯一(它是数组下标)② inode 总数在格式化时定死(数组长度事后加不了)③ 于是会出现"磁盘还有大把空间,却创建不了新文件"。
  10. 直接地址项个数有教材分歧:UNIX System V 共 13 个地址项、其中 10 个直接;Linux ext2 共 15 个、其中 12 个直接做题以题目给出的个数为准。

这一节在真题里被考过的形式

file-concepts 这个标签下挂了 27 道题,覆盖整个 file 章大半篇幅, 所以本页下方的练习区渲染的题远比本节内容宽,这是正常的—— 其中考物理结构的在文件的物理结构讲、 考打开关闭的在文件的操作讲、考硬链接的在 硬链接和软链接讲。真正落在本节的有两道, 而且都在考"这个信息该放哪"

  • 问文件访问控制信息存储的合理位置(2009-30)。答文件控制块。⚠️ 另三个选项各代表一种典型误解:文件分配表只管数据块的链接关系、不管权限;用户口令表管的是"这个人是谁"而不是"这个人能对这个文件做什么";系统注册表是 Windows 的配置数据库,与文件系统无关。判据回到上一节——权限跟着文件本体走
  • 问新建文件时文件系统不会做的事(2025-29)。答在目录文件中写入 F 的访问权限信息。⚠️ 另三项都要做:初始化 F 的 inode、在目录文件中写入 F 的 inode 号、增加一条 F 的目录项。这道题就是速记第七条的反面——目录项只有 (文件名, inode 号) 两样东西,权限根本没有位置可放

复习优先级必须拿满,且两道题共用同一条判据。 把"目录项只有文件名和 inode 号、 其余全在 inode"这句话记死,这两道题都是送分。第五、六条(两个计数的分工) 在文件的操作硬链接和软链接 还要各用一次,在这里先理解清楚。第八、九条那条地址公式虽然没直接考过, 但它是"inode 用完了就建不了文件"这个现象的唯一解释,值得记。

易错:认为文件的访问权限信息存在目录项里。目录项只有文件名和 inode 号,权限在 inode。

易错:把访问控制信息安在文件分配表上。FAT 只管数据块的链接关系。

易错:把链接计数和引用计数当成一回事。链接计数数目录项、在磁盘上;引用计数数进程、在内存里

易错:认为链接计数减到 0 就立刻回收空间。要两个计数同时为 0——还有进程开着就得等它 close

易错:认为只改文件权限时 i_mtime 也会变。只有 i_ctime 变;i_mtime 只在改内容时变。

易错:认为 inode 里存着自己的编号。编号由它在 inode 数组里的位置隐含;只有读进内存后才补一个编号字段。

易错:认为磁盘有空间就一定能创建新文件。inode 总数在格式化时定死,用完就建不了。

易错:按记忆中的"10 个直接地址项"去套题。教材有 13 项/15 项两套口径,一律以题目给出的为准

教材出处
  • 汤小丹《计算机操作系统》印刷版 p233–p234(7.3.1 文件控制块和索引结点):FCB 含基本信息、存取控制信息、使用信息三类;索引结点的引入动机是"在检索目录文件的过程中,只用到了文件名","查找一个目录项,平均需要调入盘块 (N+1)/2 次";Unix 目录项占 16 字节(14 字节文件名 + 2 字节 i 结点指针),使平均启动磁盘次数减少到原来的 1/4。
  • 同书 p234 列出磁盘索引结点的字段(文件主标识符、文件类型、存取权限、13 个地址项、文件长度、文件连接计数、三个存取时间),以及内存索引结点额外增加的索引结点编号、状态、访问计数

相关知识

文件的基本概念文件的操作硬链接和软链接文件系统的全局结构文件的物理结构

真题练习