当前位置:首页>Linux>Linux inode 与 shmem 实现解析:内核如何管理文件与匿名内存

Linux inode 与 shmem 实现解析:内核如何管理文件与匿名内存

  • 2026-10-11 08:44:30
Linux inode 与 shmem 实现解析:内核如何管理文件与匿名内存
先看一个框架图,有疑问我们可以评论区讨论

第 1 章:inode 概念解析

1.1 inode 的作用与基本结构

在 Linux 文件系统中,inode(Index Node) 是文件系统管理文件的核心数据结构,它存储了文件的 元数据,包括权限、所有者、时间戳、链接计数、文件大小和指向数据块的指针。inode 不直接存储文件名,文件名由目录项(dentry)记录,指向相应 inode。

inode 实现了 文件系统的抽象与分层管理。无论是磁盘文件、字符设备还是匿名内存,都可以通过 inode 对象统一管理。inode 提供了3个关键功能:

  1. 唯一标识文件:每个 inode 在文件系统内唯一标识一个文件或目录。

  2. 元数据管理:包括访问权限、创建时间、修改时间、文件大小、块指针。

  3. 数据访问接口:通过 inode->i_op(inode_operations)结构提供读写、创建、删除等操作接口。

inode 的核心字段包括:

  • i_mode:文件类型和权限。

  • i_uid/i_gid:所有者 UID/GID。

  • i_size:文件大小(字节)。

  • i_atime、i_mtime、i_ctime:访问、修改和状态改变时间。

  • i_blocks:占用磁盘块数。

  • i_op:文件操作函数指针表。

inode 通过上面的这几个字段将文件系统、内核 VFS 层和具体存储设备连接起来,实现 统一文件抽象。

1.2 inode 生命周期

inode 的生命周期涉及 创建、使用、回收 三个阶段:

  1. 创建:当新文件被创建时,内核分配一个 inode 结构,并初始化元数据。

  2. 使用:进程通过 VFS 接口(如 open、read、write)访问文件,inode 被挂载到 dentry 缓存中,缓存命中后无需重复查找磁盘。

  3. 回收:当文件关闭、链接计数为 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) 结合管理文件数据:

  1. 页缓存:内核通过 struct page 管理内存页,缓存磁盘文件数据,提高 I/O 性能。

  2. inode->i_mapping:每个 inode 有一个 address_space,将页缓存与 inode 关联。

  3. 读写流程:

    • 读文件:先查页缓存 → 缓存未命中 → 从磁盘读取 → 更新缓存。

    • 写文件:更新页缓存 → 延迟写回磁盘(writeback)。

这种 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) 是内核实现匿名映射和进程间共享内存的关键机制:

  1. 内核创建一个 特殊 inode(struct inode) 表示 shmem 对象。

  2. inode 通过 i_mapping 关联到匿名页缓存(struct page)。

  3. 页缓存可被多个进程映射(mmap),实现共享内存功能。

shmem inode 将 文件 inode 逻辑和匿名页缓存结合,使共享内存可通过 VFS 和页缓存机制统一管理,并支持 swap-out,保证内存压力下系统稳定。

第 4 章:mmap 与匿名映射的实现

4.1 mmap 调用链解析

当用户进程调用 mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0) 执行的时候,在底层的执行主要是要执行创建和关联:

  1. 内核创建 vm_area_struct(VMA),表示进程虚拟地址空间一段区域。

  2. 对应 匿名 inode 或 shmem inode 创建或复用。

  3. 设置 i_mapping,用于关联页缓存。

  4. 返回虚拟地址指针给用户进程。

这个流程把匿名内存映射到内核页缓存,实现统一管理,同时保证内存分配和回收机制可复用。

4.2 页面缺失与回填

进程访问匿名 mmap 内存页时,在底层的执行主要是:

  1. CPU 触发页缺失(page fault)。

  2. 内核调用 handle_mm_fault():

    • 查找页缓存是否已有对应页(struct page)。

    • 如果没有,分配新页(from buddy allocator)。

    • 插入页缓存,更新 VMA 与 PTE(页表)。

这种机制实现 按需分配(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 链表连接。页缓存管理保证了:

  1. 快速访问:已经映射的页面可直接被访问,无需重复分配。

  2. 共享访问:多个进程 mmap 同一 shmem inode 时,共享同一组 struct page。

  3. 统一回收机制:参与内核 LRU 链表,支持 kswapd 和 direct reclaim 的回收策略。

shmem 页可以像磁盘文件页一样参与 延迟写回、回收和内存压力管理,从而让共享内存既高效又可控。

一点管理细节:

  • 标记状态:struct page 的 PG_locked、PG_dirty 等标记页状态,用于回收判断。

  • LRU 链表挂载:匿名页和 shmem 页挂载在 LRU 活跃/非活跃链表,提供优先回收策略。

  • 引用计数:多进程共享时,页引用计数 >1,不会被单进程释放。

这种设计保证共享内存页的安全访问,并通过统一的页回收机制降低系统内存压力。

5.2 swap-out 与内存压力协作

当系统内存紧张时,shmem 页可能被 swap-out 到交换区(swap space):

  1. 触发条件:

    • free pages < low watermark

    • kswapd 或 direct reclaim 被触发

    • 页引用计数为 1 且非活跃

  2. 回收流程:

    • 从 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 时:

  1. 调用 shmem_get_inode() 或 anon_inode_getfile() 创建 inode 对象。

  2. 初始化 i_mapping,连接 struct page 链表。

  3. inode 不挂载在传统文件系统,而是挂载在 虚拟 tmpfs 文件系统 或匿名空间。

    匿名 inode 的存在让内核可以用统一的 inode+页缓存机制管理匿名内存,简化内存回收逻辑,同时支持 mmap、fork 复制和共享内存映射。

6.2 引用计数与回收机制

匿名 inode 生命周期依赖 引用计数:

  1. VMA 引用:每个进程的 vm_area_struct 引用 inode,通过 mmap 管理。

  2. 页引用:每个 struct page 的引用计数与 inode 关联,保证页不会被过早释放。

  3. 释放条件:

    • 所有进程解除映射

    • 页引用计数归零

    • 页已被 swap 或回收

引用计数机制保证匿名 inode 页安全共享,同时在内存压力下能够被回收,不会破坏其他进程的访问。

匿名 inode 生命周期流程:

创建匿名 inode → 初始化 i_mapping       │ mmap 映射到进程 VMA       │访问 struct page → 引用计数 +1       │进程解除映射?       │是 → 页引用计数 -1       │计数归零 → 回收 inode 与 struct page

第 7 章:共享内存映射与进程同步

7.1 多进程共享机制

shmem 支持 多个进程 mmap 同一对象,共享同一组 struct page,实现高效进程间通信(IPC):

  1. 多个 VMA 指向同一 shmem inode

  2. 页缓存共享:struct page 引用计数 >1,确保多进程安全访问

  3. 同步策略:

    • 通过内存屏障(memory barrier)保证多进程访问顺序

    • 可以通过 futex 或信号量实现用户态同步

    这种设计将内核页缓存机制与 IPC 共享内存结合,使 Linux 内存管理层既高效又安全。

7.2 写时复制(COW)与 fork

当进程 fork 时:

  1. 父子进程共享匿名 inode 页缓存(struct page)

  2. 设置写时复制标记(COW),页引用计数 >1

  3. 子进程写入时,内核触发 page fault → 分配新页,复制父进程页内容。

这种机制保证 fork 高效:不需要立即复制整个进程内存,而是在写入时按需复制,节省大量物理内存。

共享内存与 COW 流程:

父子进程共享 VMA       │访问共享页 → 引用计数 +1       │写入页?       │是 → 触发 page fault       │分配新页 → 复制内容 → 更新 PTE
第 8 章:内核内存回收与 shmem 协作

8.1 kswapd 与 direct reclaim

shmem 页参与内核内存回收链路:

  1. kswapd 后台线程扫描各内存 zone LRU 链表,释放非活跃匿名页和 shmem 页。

  2. Direct reclaim:进程在分配内存失败时,主动触发回收。

  3. 回收流程:

    • 从 inactive list 选择冷页

    • swap-out 或直接释放 struct page

    • 更新 i_mapping 和 PTE

主要是保证内存压力下,shmem 页能被回收,避免 OOM,维持系统稳定。

8.2 内存压力下的页回收策略

内核对 shmem 页回收策略考虑:

  • 活跃页 vs 非活跃页:非活跃页优先回收

  • 共享引用计数:多进程共享页引用计数 >1,不立即释放

  • 脏页处理:需要写回 swap 或 tmpfs backing storage

这种回收策略兼顾共享内存安全和系统稳定性,使内核在高负载和多进程共享场景下仍能平稳运行。

我建了一个嵌入式Linux技术群,专门聊难题分析和求职面试,欢迎大家一起加入,共同解决工作中的疑难杂症问题

内存回收流程:

系统内存压力       │kswapd / direct reclaim       │选择 inactive shmem 页       │页引用计数 >1?       │是 → 跳过否 → 回收 struct page / swap-out

最新文章

随机文章