第 1 章:inode 概念解析
1.1 inode 的作用与基本结构
在 Linux 文件系统中,inode(Index Node) 是文件系统管理文件的核心数据结构,它存储了文件的 元数据,包括权限、所有者、时间戳、链接计数、文件大小和指向数据块的指针。inode 不直接存储文件名,文件名由目录项(dentry)记录,指向相应 inode。
inode 实现了 文件系统的抽象与分层管理。无论是磁盘文件、字符设备还是匿名内存,都可以通过 inode 对象统一管理。inode 提供了3个关键功能:
唯一标识文件:每个 inode 在文件系统内唯一标识一个文件或目录。
元数据管理:包括访问权限、创建时间、修改时间、文件大小、块指针。
数据访问接口:通过 inode->i_op(inode_operations)结构提供读写、创建、删除等操作接口。
inode 的核心字段包括:
inode 通过上面的这几个字段将文件系统、内核 VFS 层和具体存储设备连接起来,实现 统一文件抽象。
1.2 inode 生命周期
inode 的生命周期涉及 创建、使用、回收 三个阶段:
创建:当新文件被创建时,内核分配一个 inode 结构,并初始化元数据。
使用:进程通过 VFS 接口(如 open、read、write)访问文件,inode 被挂载到 dentry 缓存中,缓存命中后无需重复查找磁盘。
回收:当文件关闭、链接计数为 0 且引用计数为 0 时,内核将 inode 释放回 inode 缓存池。
inode 生命周期的管理依赖 inode cache(icache),通常实现为 SLAB/SLUB 缓存对象,以提高频繁访问文件的效率。
inode 执行的流程图:
创建 inode │ ▼挂载到 dentry / VFS │使用读写操作 │ ▼引用计数归零? │ ▼释放 inode → 缓存回收
第 2 章:VFS 与 inode 操作
2.1 VFS 层对 inode 的抽象
VFS(Virtual File System) 为 inode 提供统一的操作接口,隐藏具体文件系统实现差异。inode 通过 inode_operations 提供函数指针表,例如:
lookup:查找目录项对应 inode
create:创建文件
unlink:删除文件
getattr:获取文件属性
setattr:设置文件属性
这种抽象使内核能够以统一方式管理不同类型文件,包括磁盘文件、设备文件、管道文件甚至匿名内存。
2.2 inode 与页缓存
VFS 层会将 inode 与 页缓存(Page Cache) 结合管理文件数据:
页缓存:内核通过 struct page 管理内存页,缓存磁盘文件数据,提高 I/O 性能。
inode->i_mapping:每个 inode 有一个 address_space,将页缓存与 inode 关联。
读写流程:
这种 inode+页缓存架构实现 内存 I/O 层次优化,让文件访问速度比直接磁盘操作快数十倍。
第 3 章:匿名内存与 shmem
3.1 区别于磁盘文件
匿名内存(anonymous memory) 是不对应任何磁盘文件的内存区域,主要用于:
进程堆(heap)
进程栈(stack)
mmap 分配的匿名页
与普通文件不同,匿名页没有 backing storage(磁盘副本),其 inode 虚拟化为 shmem inode 或匿名 inode,用于页缓存和内存回收管理。
内核会通过虚拟 inode 对匿名内存进行统一管理,使其可以参与 页回收、写回、交换(swap),和普通文件页同等管理。
3.2 shmem inode 的角色
shmem(shared memory) 是内核实现匿名映射和进程间共享内存的关键机制:
内核创建一个 特殊 inode(struct inode) 表示 shmem 对象。
inode 通过 i_mapping 关联到匿名页缓存(struct page)。
页缓存可被多个进程映射(mmap),实现共享内存功能。
shmem inode 将 文件 inode 逻辑和匿名页缓存结合,使共享内存可通过 VFS 和页缓存机制统一管理,并支持 swap-out,保证内存压力下系统稳定。
第 4 章:mmap 与匿名映射的实现
4.1 mmap 调用链解析
当用户进程调用 mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0) 执行的时候,在底层的执行主要是要执行创建和关联:
内核创建 vm_area_struct(VMA),表示进程虚拟地址空间一段区域。
对应 匿名 inode 或 shmem inode 创建或复用。
设置 i_mapping,用于关联页缓存。
返回虚拟地址指针给用户进程。
这个流程把匿名内存映射到内核页缓存,实现统一管理,同时保证内存分配和回收机制可复用。
4.2 页面缺失与回填
进程访问匿名 mmap 内存页时,在底层的执行主要是:
CPU 触发页缺失(page fault)。
内核调用 handle_mm_fault():
这种机制实现 按需分配(demand paging)能够节约内存,提高系统性能,同时保证进程可以使用连续虚拟地址空间。
页缺失流程图:
进程访问匿名内存 │触发 page fault ▼handle_mm_fault() │缓存存在? ──► 返回页 │不存在? ▼分配 struct page → 插入 i_mapping │ ▼更新页表 → 进程继续访问
第 5 章:shmem 页缓存回收与 swap-out
5.1 shmem 页缓存管理
在 Linux 内核中,shmem 对象(共享内存或匿名映射)的数据页通过 页缓存(page cache)管理。每个 shmem inode 对应一个 i_mapping,与 struct page 链表连接。页缓存管理保证了:
快速访问:已经映射的页面可直接被访问,无需重复分配。
共享访问:多个进程 mmap 同一 shmem inode 时,共享同一组 struct page。
统一回收机制:参与内核 LRU 链表,支持 kswapd 和 direct reclaim 的回收策略。
shmem 页可以像磁盘文件页一样参与 延迟写回、回收和内存压力管理,从而让共享内存既高效又可控。
一点管理细节:
标记状态:struct page 的 PG_locked、PG_dirty 等标记页状态,用于回收判断。
LRU 链表挂载:匿名页和 shmem 页挂载在 LRU 活跃/非活跃链表,提供优先回收策略。
引用计数:多进程共享时,页引用计数 >1,不会被单进程释放。
这种设计保证共享内存页的安全访问,并通过统一的页回收机制降低系统内存压力。
5.2 swap-out 与内存压力协作
当系统内存紧张时,shmem 页可能被 swap-out 到交换区(swap space):
触发条件:
回收流程:
从 LRU 链表选择冷页(inactive list)。
如果页是 dirty,则写入 swap 或 tmpfs backing storage。
页回收后,更新 page->mapping 和 PTE,使后续访问触发 page fault。
swap-out主要是实现 内存压力下的动态内存调度:保证了共享内存可访问,同时也能让系统内存能够持续的使用,防止 OOM 情况发生。
shmem swap-out流程:
系统内存紧张 │触发 kswapd / direct reclaim │从 LRU 链表选择冷页 │页 dirty? ──► 写回 swap │回收 struct page → 更新 i_mapping │下次访问触发 page fault → 重新分配
第 6 章:匿名 inode 生命周期与引用计数
6.1 匿名 inode 的创建与管理
匿名 inode 是内核表示非文件映射对象的抽象,例如:
进程堆(heap)
匿名 mmap 区域
tmpfs/shmem 对象
内核在创建匿名 inode 时:
调用 shmem_get_inode() 或 anon_inode_getfile() 创建 inode 对象。
初始化 i_mapping,连接 struct page 链表。
inode 不挂载在传统文件系统,而是挂载在 虚拟 tmpfs 文件系统 或匿名空间。
匿名 inode 的存在让内核可以用统一的 inode+页缓存机制管理匿名内存,简化内存回收逻辑,同时支持 mmap、fork 复制和共享内存映射。
6.2 引用计数与回收机制
匿名 inode 生命周期依赖 引用计数:
VMA 引用:每个进程的 vm_area_struct 引用 inode,通过 mmap 管理。
页引用:每个 struct page 的引用计数与 inode 关联,保证页不会被过早释放。
释放条件:
所有进程解除映射
页引用计数归零
页已被 swap 或回收
引用计数机制保证匿名 inode 页安全共享,同时在内存压力下能够被回收,不会破坏其他进程的访问。
匿名 inode 生命周期流程:
创建匿名 inode → 初始化 i_mapping │ mmap 映射到进程 VMA │访问 struct page → 引用计数 +1 │进程解除映射? │是 → 页引用计数 -1 │计数归零 → 回收 inode 与 struct page
第 7 章:共享内存映射与进程同步
7.1 多进程共享机制
shmem 支持 多个进程 mmap 同一对象,共享同一组 struct page,实现高效进程间通信(IPC):
多个 VMA 指向同一 shmem inode
页缓存共享:struct page 引用计数 >1,确保多进程安全访问
同步策略:
这种设计将内核页缓存机制与 IPC 共享内存结合,使 Linux 内存管理层既高效又安全。
7.2 写时复制(COW)与 fork
当进程 fork 时:
父子进程共享匿名 inode 页缓存(struct page)
设置写时复制标记(COW),页引用计数 >1
子进程写入时,内核触发 page fault → 分配新页,复制父进程页内容。
这种机制保证 fork 高效:不需要立即复制整个进程内存,而是在写入时按需复制,节省大量物理内存。
共享内存与 COW 流程:
父子进程共享 VMA │访问共享页 → 引用计数 +1 │写入页? │是 → 触发 page fault │分配新页 → 复制内容 → 更新 PTE
第 8 章:内核内存回收与 shmem 协作
8.1 kswapd 与 direct reclaim
shmem 页参与内核内存回收链路:
kswapd 后台线程扫描各内存 zone LRU 链表,释放非活跃匿名页和 shmem 页。
Direct reclaim:进程在分配内存失败时,主动触发回收。
回收流程:
主要是保证内存压力下,shmem 页能被回收,避免 OOM,维持系统稳定。
8.2 内存压力下的页回收策略
内核对 shmem 页回收策略考虑:
这种回收策略兼顾共享内存安全和系统稳定性,使内核在高负载和多进程共享场景下仍能平稳运行。
我建了一个嵌入式Linux技术群,专门聊难题分析和求职面试,欢迎大家一起加入,共同解决工作中的疑难杂症问题内存回收流程:
系统内存压力 │kswapd / direct reclaim │选择 inactive shmem 页 │页引用计数 >1? │是 → 跳过否 → 回收 struct page / swap-out