Linux 内核 address_space与页缓存

address_space------ 它是内核页缓存(Page Cache) 的核心管理结构体,也是连接「文件系统」「虚拟内存」「物理内存」的关键枢纽,所有文件读写、tmpfs 数据存储、mmap 文件映射都绕不开它,是内核内存管理的「隐形核心」。

address_space 是内核页缓存的「总管家」 :每个可缓存的对象(普通文件 /tmpfs 文件 / 块设备)都对应一个address_space,负责管理该对象的所有页缓存物理页、脏页、映射关系;

核心作用:统一管理「文件数据 <-> 物理页」的映射关系,提供页缓存的分配 / 查找 / 脏页管理 / 写回等核心接口,是文件系统与内存管理的「桥梁」;

核心关联

  • struct page:每个页缓存物理页通过page->mapping指向所属的address_space
  • inode:普通文件 /tmpfs 文件的inode->i_mapping指向其address_space
  • 与 VMA:映射文件的 VMA 通过vm_file->f_mapping关联到address_space,缺页异常时从这里获取页缓存;
  • 与 tmpfs:tmpfs 的文件数据完全存储在address_space管理的页缓存中,address_space是其数据存储的核心;

核心接口find_get_page()(查找页缓存)、page_cache_alloc()(分配页缓存)、mark_page_dirty()(标记脏页)、writeback_inode()(脏页写回)。

address_space核心数据结构

核心字段解析

字段 核心作用 关联知识点
host 指向所属的inode(文件 / 设备),是address_space与文件系统的核心关联 tmpfs_inode_info/ext4_inode_info
i_pages 用 xarray(高效数组)存储「文件偏移(页号)→ struct page」的映射,是页缓存的核心存储 物理页管理(struct page)
i_mmap 存储所有映射该address_space的 VMA(带 VM_SHARED 标志),实现多进程共享映射 VMA/mmap 共享内存
i_mmap_cnt 共享映射的 VMA 数量,计数为 0 时可安全释放页缓存 内存回收 /kswapd
a_ops 页缓存的操作集,不同文件系统(ext4/tmpfs)实现不同的读写逻辑 tmpfs/ext4 文件系统实现
tree_lock 保护 i_pages 的自旋锁,避免多线程并发操作页缓存的竞态条件 内核同步 / 自旋锁

页缓存

页缓存(Page Cache) 是连接「文件数据」和「物理内存」的核心,而address_space就是页缓存的「管理中枢」,其核心定位可总结为:

address_space = 页缓存的「索引 + 操作中心」

  1. 「索引」:通过i_pages维护「文件偏移→物理页」的映射,快速查找任意偏移对应的页缓存;
  2. 「操作中心」:通过a_ops提供页缓存的读 / 写 / 脏页标记 / 写回等标准化操作,适配不同文件系统(ext4/tmpfs/ 块设备)。

页缓存的基本概念

页缓存是 Linux 内核基于物理内存 实现的文件数据缓存层 ,以4KB 物理页 为单位缓存磁盘文件的内容,是内核对所有文件 IO 的统一抽象 ;普通文件 IO (read/write) 和 mmap 文件映射的底层都通过页缓存交互,主缺页异常仅在文件页未缓存时触发

页缓存的核心作用是将磁盘 IO 转化为内存 IO,减少主缺页次数和磁盘访问,提升系统整体性能

页缓存是内核为提升文件读写性能设计的「内存缓存层」,核心规则:

  • 读取文件时,内核先从页缓存(address_spacei_pages)查找数据,命中则直接返回,未命中则从磁盘加载到页缓存后返回;
  • 写入文件时,内核先将数据写入页缓存,标记为「脏页」,再由内核线程(pdflush/kswapd)异步写回磁盘;
  • 所有页缓存物理页都由address_space统一管理,每个文件 / 设备对应唯一的address_space

Linux 内核的核心设计思想对所有磁盘文件的访问,都必须经过页缓存,内核不允许进程直接访问磁盘 。无论是read()/write()的普通文件 IO,还是mmap()的文件映射,最终都是对页缓存的读写,磁盘仅作为页缓存的持久化后端

页缓存(Page Cache,也叫文件页缓存)是 Linux 内核在物理内存 中开辟的一块缓存区域,专门用于缓存磁盘文件的内容和元数据(如 inode、目录项) ;

页缓存核心特性:

  • 缓存单位 :以 Linux 最小物理页单位4KB为基本缓存单元,无论文件大小多少,都按页切分缓存;
  • 数据来源:磁盘文件的内容,仅当页缓存中无对应数据时,才从磁盘读取(触发主缺页 / 磁盘 IO);
  • 统一抽象:是内核对所有文件系统(ext4/xfs/btrfs)和块设备的统一缓存层,上层 IO 操作无需关心底层存储;
  • 按需加载 :采用懒加载策略,仅当进程访问文件的某一页时,才将该页从磁盘加载到页缓存,不一次性加载整个文件;
  • 持久化保障 :内核通过刷盘机制将页缓存中的脏页(修改后未同步到磁盘)异步 / 同步写入磁盘,保证数据一致性;
  • 全局共享:页缓存属于内核全局资源,所有进程共享同一页缓存,一个进程加载的文件页,其他进程可直接复用,避免重复磁盘 IO。

页缓存的核心价值

页缓存是 Linux 系统性能的核心支柱 ,没有页缓存,文件 IO 的性能会下降1000 倍以上(磁盘 IO vs 内存 IO),核心价值体现在 3 点:

  1. 将磁盘 IO 转化为内存 IO :磁盘的随机寻道时间≈10ms,内存访问时间≈10ns,差距100 万倍,页缓存让绝大多数文件访问都在内存中完成;
  2. 减少主缺页异常次数 :mmap 文件映射时,若文件页已在页缓存中,仅触发次缺页异常 (无磁盘 IO),否则触发主缺页异常(磁盘 IO);
  3. 实现进程间数据共享:所有进程共享页缓存,一个进程加载的文件页,其他进程可直接复用,避免重复的磁盘读取和物理内存占用。

页缓存的内核管理机制

核心数据结构(页缓存的管理载体)

Linux 内核通过以下 3 个核心数据结构,实现对页缓存的高效管理,所有结构都存储在物理内存中:

页描述符:struct page

这是 Linux 内核管理每一个物理页 的核心结构,页缓存中的每一个缓存页,都对应一个struct page,核心字段:

  • mapping:指向该页所属的地址空间 (struct address_space),关联到具体的文件;
  • index:该页在文件中的偏移页号(如文件的第 10 个 4KB 页,index=10);
  • flags:页的状态标志,核心标志:
    • PG_uptodate:页缓存中的数据与磁盘一致(干净页);
    • PG_dirty:页缓存中的数据被修改,未同步到磁盘(脏页);
    • PG_locked:页被锁定(如正在从磁盘加载数据,防止并发修改);
  • count:页的引用计数,记录当前使用该页的进程数 / 内核模块数。

struct page物理页的唯一标识,页缓存、进程虚拟内存、物理内存管理都基于该结构。

地址空间:struct address_space

这是文件与页缓存的核心关联结构 ,每个文件的 inode(struct inode)中都包含一个i_mapping字段,指向该文件的address_space,核心作用是将文件的偏移量映射到页缓存的物理页,核心字段:

  • page_tree红黑树 ,以「文件偏移页号 (index)」为键,存储该文件的所有缓存页(struct page),实现缓存页的快速查找(时间复杂度 O (logN));
  • nrpages:该文件在页缓存中的缓存页数
  • ops:页缓存的操作函数集struct address_space_operations),包含缓存页的读取、写入、刷盘等核心操作;
  • dirty_pages:该文件的脏页链表,存储所有被修改但未刷盘的缓存页。

核心作用:通过address_space,内核能快速根据文件 + 文件偏移,找到对应的页缓存物理页,无需遍历整个页缓存。

索引节点:struct inode

文件的元数据载体,每个文件对应一个唯一的 inode,核心与页缓存相关的字段:

  • i_mapping:指向该文件的address_space,是文件与页缓存的唯一关联入口
  • i_atime/i_mtime/i_ctime:文件的访问 / 修改 / 状态改变时间;
  • i_size:文件大小,用于限制页缓存的最大偏移。

页缓存的核心操作(查 / 加 / 删)

内核对页缓存的所有操作,都围绕「根据文件 + 偏移查找缓存页」展开,核心有 3 个操作,是文件 IO 和 mmap 映射的底层基础:

查找缓存页:find_get_page(mapping, index)
  • 入参:mapping= 文件的address_spaceindex= 文件的偏移页号;
  • 逻辑:遍历mapping的红黑树page_tree,根据index查找对应的struct page
  • 结果:找到则返回struct page并将引用计数 + 1,未找到则返回 NULL。
添加缓存页:add_to_page_cache(page, mapping, index)
  • 入参:page= 分配的物理页描述符,mapping= 文件的address_spaceindex= 文件的偏移页号;
  • 逻辑:将page插入到mapping的红黑树page_tree中,设置page->mappingpage->index,初始化页状态;
  • 核心:添加前会通过伙伴系统分配空闲的物理页,用于存储从磁盘读取的文件数据。
删除缓存页:delete_from_page_cache(page)
  • 入参:page= 要删除的缓存页描述符;
  • 逻辑:将pagemapping的红黑树page_tree中移除,清空page->mappingpage->index,减少引用计数;
  • 触发场景:物理内存不足时,内核回收页缓存的干净页;文件被删除 / 截断时,删除对应的缓存页。

页缓存与文件 IO 的核心交互

普通文件 IO(read()/write()系统调用)是用户态访问文件的最常用方式,其底层完全基于页缓存实现,全程无直接的磁盘访问,所有操作都是对页缓存的读写。

read () 系统调用底层流程(读文件 = 读页缓存)

核心逻辑:先查页缓存,有则直接读,无则从磁盘加载到页缓存后再读 ,仅当缓存未命中时触发磁盘 IO + 主缺页异常,命中则仅内存操作,流程如下(共 6 步):

步骤 1:用户态调用 read (),内核接收参数

进程调用read(fd, buf, count),触发系统调用进入内核态,内核通过文件描述符fd找到对应的文件 inode ,进而获取文件的address_spaceinode->i_mapping),并计算要读取的文件偏移页号 (index)页内偏移

步骤 2:内核查找页缓存(find_get_page)

内核遍历该文件address_space的红黑树,根据文件偏移页号查找对应的页缓存物理页:

  • 缓存命中 :找到对应的struct page,直接进入步骤 5(读页缓存);
  • 缓存未命中 :未找到对应的struct page,进入步骤 3(加载磁盘数据到页缓存)。
步骤 3:内核分配物理页,添加到页缓存

内核通过伙伴系统 分配一块空闲的 4KB 物理页,调用add_to_page_cache()将该物理页添加到文件的address_space红黑树中,设置页状态为PG_locked(锁定,防止并发修改)。

步骤 4:内核发起磁盘 IO,加载数据到页缓存

内核根据文件的偏移量,计算对应的磁盘物理块 ,向块设备层发起磁盘读 IO 请求,将磁盘文件的对应页数据加载到刚分配的物理页中:

  • IO 完成后,内核将页状态标记为PG_uptodate(数据与磁盘一致,干净页),并解除锁定(清除PG_locked);
  • 此步骤是唯一的磁盘 IO 操作 ,也是read()性能瓶颈的唯一来源。
步骤 5:内核将页缓存数据拷贝到用户态缓冲区

内核将页缓存物理页中的数据,拷贝到用户态传入的 buf 缓冲区(用户态虚拟地址),完成数据读取:

  • 若读取的字节数跨多个物理页,内核会重复步骤 2~5,直到读取完所有数据;
  • 此步骤是唯一的数据拷贝操作(页缓存→用户态缓冲区)。
步骤 6:内核返回用户态,read () 调用完成

内核将实际读取的字节数返回给用户态进程,read()系统调用完成,进程可直接访问buf中的数据。

核心结论:read () 的本质是「页缓存的查找 + 数据拷贝」,磁盘 IO 仅在缓存未命中时触发,且数据会被缓存到页缓存,后续对该文件的读取会直接命中缓存。

write () 系统调用底层流程(写文件 = 写页缓存)

核心逻辑:先查页缓存,有则直接写,无则分配物理页并添加到页缓存后再写写操作仅修改页缓存,不立即同步到磁盘 ,被修改的页会被标记为脏页,由内核异步刷盘,流程如下(共 6 步):

步骤 1:用户态调用 write (),内核接收参数

进程调用write(fd, buf, count),进入内核态,内核通过fd找到文件 inode 和address_space,计算写入的文件偏移页号和页内偏移。

步骤 2:内核查找 / 创建页缓存

内核通过find_get_page()查找对应偏移的页缓存:

  • 缓存命中 :找到对应的struct page,进入步骤 4(写页缓存);
  • 缓存未命中 :分配物理页,调用add_to_page_cache()添加到页缓存,标记为PG_locked,进入步骤 3
步骤 3:(缓存未命中时)加载磁盘数据到页缓存

若写入的区域是文件的已有区域 (非文件尾部追加),内核会发起磁盘 IO,将该页的原始数据从磁盘加载到页缓存(保证数据完整性);若为文件尾部追加,则无需加载磁盘数据,直接初始化物理页为 0 即可。

步骤 4:内核将用户态数据拷贝到页缓存

内核将用户态buf中的数据,拷贝到页缓存的物理页中(内核态→内存操作),完成写入:

  • 若写入跨多个物理页,重复步骤 2~4
  • 此步骤是唯一的数据拷贝操作(用户态缓冲区→页缓存)。
步骤 5:内核标记页缓存为脏页,解除锁定

内核将该页的状态标志设置为PG_dirty(脏页,修改后未同步到磁盘),并清除PG_locked标志;

核心 :此时数据仅存在于页缓存中,未写入磁盘,write () 调用至此已完成,无需等待磁盘 IO。

步骤 6:内核返回用户态,write () 调用完成

内核将实际写入的字节数返回给用户态进程,write () 调用完成,进程可继续执行其他逻辑,无需等待磁盘刷盘

核心结论:write () 的本质是「页缓存的查找 + 数据拷贝 + 标记脏页」 ,全程无同步磁盘 IO,这是 write () 高性能的核心原因;磁盘刷盘由内核异步完成,不阻塞用户态进程。

普通文件 IO 的核心性能瓶颈

普通文件 IO(read/write)的性能瓶颈主要来自两个方面,也是后续优化的核心:

  1. 数据拷贝 :read 有「页缓存→用户态」一次拷贝,write 有「用户态→页缓存」一次拷贝,两次拷贝是 mmap 比普通 IO 快的核心原因;
  2. 磁盘 IO:仅当页缓存未命中时触发,主缺页异常也由此产生,磁盘 IO 的耗时远大于内存操作和数据拷贝。

页缓存与 mmap 文件映射的核心交互

mmap 的本质是将文件的页缓存直接映射到进程的虚拟地址空间,进程可直接访问页缓存,无需数据拷贝,这是 mmap 比 read/write 快的根本原因。

mmap 文件映射与页缓存的绑定关系

mmap 文件映射的全程,始终与页缓存深度绑定,无页缓存则无 mmap 的零拷贝,核心绑定逻辑:

  • mmap 系统调用阶段 :内核为进程创建 VMA(虚拟内存区域),并将 VMA 与文件的address_space(页缓存)关联,不分配物理内存,不加载磁盘数据
  • 首次访问映射地址阶段 :触发缺页异常 ,内核通过 VMA 找到文件的address_space,执行页缓存的查找 / 加载逻辑;
  • 正常访问阶段 :进程的虚拟地址直接映射到文件的页缓存物理页,读写操作直接作用于页缓存,无任何数据拷贝。

核心结论:mmap 文件映射 = 进程虚拟地址空间 ↔ 文件页缓存物理页 的直接映射,页缓存是 mmap 的唯一数据载体。

mmap 文件映射的读流程(与缺页异常协同)

mmap 读文件的核心是缺页异常 + 页缓存命中 / 未命中 ,全程零拷贝,流程如下:

  1. 进程调用mmap(),内核创建 VMA 并关联文件的address_space,返回虚拟地址(仅分配虚拟地址,无物理内存);
  2. 进程首次访问 映射虚拟地址,触发缺页异常 ,进入内核do_page_fault()
  3. 内核通过 VMA 找到文件的address_space,调用find_get_page()查找页缓存:
    • 缓存命中 :触发次缺页异常,内核直接将该页缓存的物理页映射到进程虚拟地址,建立页表,无磁盘 IO;
    • 缓存未命中 :触发主缺页异常,内核分配物理页→添加到页缓存→发起磁盘 IO 加载数据→建立页表映射;
  4. 缺页异常处理完成,进程返回用户态,直接访问虚拟地址即可读取页缓存数据,无任何数据拷贝。

mmap 文件映射的写流程(MAP_SHARED/MAP_PRIVATE 区别)

mmap 写文件的流程依赖flags参数(MAP_SHARED/MAP_PRIVATE),但无论哪种,写操作都直接作用于页缓存 ,核心区别是是否将脏页同步到磁盘 / 其他进程

MAP_SHARED(共享映射):写页缓存 = 写文件
  1. 进程写入映射虚拟地址,直接修改共享的页缓存物理页
  2. 内核将该页标记为PG_dirty(脏页),与普通 write () 的脏页管理逻辑一致,由内核异步刷盘到磁盘;
  3. 所有映射该文件的进程,都能立即看到页缓存的修改(共享特性);
  4. 核心:无数据拷贝,修改直接同步到页缓存,最终异步刷盘到磁盘
MAP_PRIVATE(私有映射):写页缓存触发 COW,不影响原文件
  1. 进程写入映射虚拟地址,触发写时复制 (COW) 缺页异常(页表项为只读);
  2. 内核在缺页异常中,为该进程分配新的物理页 ,将原页缓存的数据拷贝到新物理页,并建立新的页表映射;
  3. 进程后续的写操作,仅修改自己的私有物理页,不会标记原页缓存为脏页,也不会同步到磁盘 / 其他进程;
  4. 核心:仅首次写触发 COW 拷贝,后续写无拷贝,不影响原文件的页缓存

address_space 的核心接口

address_space 提供了标准化的页缓存操作接口,是内核文件系统开发的核心 API,以下是最常用的接口;

页缓存查找 / 分配

接口 作用
find_get_page(mapping, index) address_spacei_pages查找指定页号的页缓存,返回struct page*
find_lock_page(mapping, index) 查找并锁定页缓存(避免并发修改)
page_cache_alloc(mapping) address_space分配一页页缓存(物理连续)
page_cache_release(page) 释放页缓存的引用计数,计数为 0 时可回收

脏页管理

接口 作用
mark_page_dirty(page) 将页缓存标记为脏页,加入address_space的脏页链表
mapping_set_dirty(mapping) 标记整个address_space为脏(有脏页)
writeback_inode(inode) inode关联的address_space的脏页异步写回磁盘 / Swap
sync_inode(inode) 同步写回address_space的所有脏页(阻塞直到完成)

页缓存插入 / 删除

接口 作用
add_to_page_cache(page, mapping, index, gfp) 将页缓存插入address_spacei_pages
remove_from_page_cache(page) address_spacei_pages删除页缓存
truncate_inode_pages(mapping, offset) 截断address_space中指定偏移后的所有页缓存

VMA 关联操作

接口 作用
vma_interval_tree_insert(vma, &mapping->i_mmap) 将 VMA 插入address_space的 i_mmap 红黑树
vma_interval_tree_remove(vma, &mapping->i_mmap) address_space删除 VMA
mapping_mapped(mapping) 判断address_space是否有 VMA 映射(i_mmap_cnt>0

address_space 管理页缓存的核心流程

步骤 1:文件打开(open("/home/test", O_RDWR)

  1. 内核找到文件的inode(ext4_inode_info),初始化其i_mapping字段,指向该文件的address_space
  2. 初始化address_spacehost为该inodea_opsext4_aops(ext4 的页缓存操作集),i_pages为空(无页缓存);

步骤 2:文件读取(read(fd, buf, 4096)

  1. 内核调用address_spacereadpage方法(ext4_readpage);
  2. 计算读取偏移对应的「页号」(如偏移 0 对应页号 0,4096 对应页号 1);
  3. 调用find_get_page(mapping, page_index)i_pages查找页缓存:
    • 命中(页缓存存在):直接将页缓存数据拷贝到用户态 buf;
    • 未命中(缺页):调用page_cache_alloc()分配物理页(伙伴系统),从磁盘读取数据到该页,将「页号→page」插入i_pages,再拷贝数据到用户态 buf;

步骤 3:文件写入(write(fd, buf, 4096)

  1. 内核调用address_spacewritepage方法(ext4_writepage);
  2. 分配页缓存物理页(若不存在),将用户态 buf 数据拷贝到该页;
  3. 调用mark_page_dirty(page)标记页为脏页,更新address_space的脏页链表;
  4. 内核线程pdflush异步遍历address_space的脏页链表,将脏页写回磁盘,写回完成后清除脏页标记;

步骤 4:文件关闭(close(fd)

  1. 若文件无引用(i_count=0),内核仍保留address_space和页缓存(供后续复用);
  2. 当系统物理内存不足时,kswapd回收address_space的冷页缓存(未被访问的页),释放物理页到伙伴系统。

address_space 与 VMA/mmap

当你调用mmap()映射文件时,address_space是连接「VMA」和「页缓存」的关键,完整链路:

核心流程(mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)

  1. 内核执行do_mmap(),新建vm_area_struct,其vm_file指向文件的struct file
  2. struct filef_mapping指向文件的address_space(即inode->i_mapping);
  3. 进程首次访问映射地址时,触发缺页异常do_page_fault()
  4. 内核在缺页处理中:
    • 通过VMA->vm_file->f_mapping找到address_space
    • 调用find_get_page(mapping, page_index)查找页缓存(文件偏移 = VMA 偏移);
    • 若页缓存不存在,从磁盘加载到页缓存;
    • 将页缓存物理页映射到进程的虚拟地址空间(建立页表);
  5. 多进程共享映射 :多个进程映射同一个文件时,共享同一个address_space的页缓存,进程写入数据会实时同步到页缓存,其他进程可立即读取 ------ 这是mmap共享内存的核心实现。

address_space 与 tmpfs

tmpfs 的文件数据完全存储在address_space管理的页缓存中,这是 tmpfs 与普通磁盘文件系统的核心区别:

tmpfs 的address_space 特殊之处

  1. address_spacea_opstmpfs_aops(tmpfs 的页缓存操作集),其readpage/writepage方法无磁盘 IO
    • tmpfs_readpage:直接从i_pages的页缓存读取数据(内存→内存);
    • tmpfs_writepage:直接将数据写入页缓存,标记脏页后由kswapd换出到 Swap(而非写回磁盘);
  2. tmpfs 的address_space无磁盘关联,i_pages的页缓存就是数据的「实际存储层」(而非缓存层);
  3. 当进程映射 tmpfs 文件时,缺页异常会直接从address_space的页缓存映射物理页,无需磁盘加载,性能远高于映射磁盘文件。

tmpfs 文件写入的核心流程

write("/dev/shm/test", buf, 4096)):

  1. 内核调用tmpfs_writepage,分配物理页(伙伴系统),将数据写入页缓存;
  2. 标记页为脏页,加入address_space的脏页链表;
  3. 若系统物理内存不足,kswapd将该脏页换出到 Swap,更新address_spacei_pages(记录页的 Swap 位置);
  4. 进程读取该数据时,若页被换出,内核从 Swap 换入物理页,更新i_pages,再返回数据。

address_spacestruct page

每个页缓存物理页的struct page有两个核心字段与address_space关联:

  • page->mapping:指向该页所属的address_space(若为页缓存页);
  • page->index:该页对应的「文件偏移页号」(如偏移 4096 对应 index=1);

核心规则:

  1. 普通页缓存页(ext4 文件):mapping= 文件的address_spaceindex= 文件偏移页号;
  2. tmpfs 页缓存页:mapping=tmpfs 文件的address_spaceindex= 文件偏移页号;
  3. 匿名页(堆 / 栈 /mmap 匿名映射):mapping=NULL(无文件关联),index无意义;
  4. 内核态页(kmalloc/vmalloc):mapping=NULL,index无意义。

address_space 与内存回收(kswapd/OOM)

address_space 是内核内存回收的核心依据,kswapd回收页缓存的流程:

  1. 遍历系统中所有address_spacei_pages,按「访问频率」排序(活跃页 / 非活跃页);
  2. 优先回收「非活跃、非脏」的页缓存(如很久未访问的 ext4 文件页),释放物理页到伙伴系统;
  3. 若回收不足,回收「非活跃、脏」的 tmpfs 页缓存,将其换出到 Swap,更新address_spacei_pages
  4. 最后才考虑回收匿名页(堆 / 栈),避免影响进程运行。

address_space 的典型应用场景

普通文件系统(ext4/xfs)

  • address_space 作为页缓存的管理中枢,实现「磁盘文件→内存缓存」的高效映射;
  • 核心价值:提升文件读写性能,减少磁盘 IO 次数(热点文件数据常驻内存)。

tmpfs(内存文件系统)

  • address_space 是 tmpfs 的「数据存储层」,其i_pages管理的页缓存就是 tmpfs 文件的实际数据;
  • tmpfs_aopsreadpage/writepage无磁盘 IO,仅操作内存 / Swap,这是 tmpfs 高性能的核心。

块设备(如 /dev/sda1)

  • 块设备的inode(bdev_inode)关联address_space,管理块设备的页缓存;
  • 用于块设备的直接读写(如dd if=/dev/sda1 of=/tmp/test),提升块设备 IO 性能。

进程间共享内存(mmap 映射文件 /tmpfs)

  • 多个进程映射同一个文件时,共享同一个address_space的页缓存;
  • 进程写入数据到映射地址,会同步到address_space的页缓存,其他进程可实时读取 ------ 这是共享内存通信的核心实现。

内核页缓存回收(系统内存不足时)

  • kswapd 通过遍历address_spacei_pages,回收冷页缓存,释放物理内存;
  • 优先回收普通文件的页缓存(可从磁盘重新加载),其次回收 tmpfs 的页缓存(换出到 Swap),最后回收匿名页。

address_spacevm_area_struct 的核心区别

address_spacevm_area_struct是内核内存管理的两个核心结构体,容易混淆,核心区别如下:

维度 address_space vm_area_struct (VMA)
核心定位 页缓存的「总管家」(文件 / 设备维度) 进程虚拟地址的「分区管家」(进程维度)
管理对象 物理页(页缓存),关联「文件偏移→物理页」 虚拟地址区间,关联「虚拟地址→物理页」
所属层级 内核态(文件系统 / 内存管理) 进程私有(用户态虚拟地址)
核心关联 inode/struct page强关联 mm_struct/struct file强关联
共享性 全局共享(所有进程可访问同一个address_space 进程私有(每个进程有独立的 VMA)
核心操作 页缓存的查找 / 分配 / 脏页管理 / 写回 虚拟地址映射 / 缺页异常 / 权限检查
典型场景 文件读写 /tmpfs 数据存储 / 页缓存回收 mmap 映射 / 堆 / 栈 / 代码段管理

核心联动总结

VMA 是「进程视角」的虚拟地址管理,address_space 是「文件视角」的页缓存管理;当进程通过mmap映射文件时,VMA 通过vm_file->f_mapping关联到address_space,实现「进程虚拟地址→文件页缓存→物理页」的完整映射。

相关推荐
yuanmenghao2 小时前
Linux 配置Samba,Windows登录,开机自动映射登录
linux·运维·windows·操作系统·samba
秋天枫叶352 小时前
【k8s集群Docker + cri-dockerd】服务器重启或关机后 apiserver/controller/scheduler 无法自动恢复
linux·运维·服务器·容器·kubernetes·bug
松涛和鸣2 小时前
59、 IMX6ULL按键驱动开发:轮询到中断的实现
linux·服务器·c语言·arm开发·数据库·驱动开发
LcVong2 小时前
C# 基于MemoryMappedFile实现进程间通信(服务端+客户端完整范例)
linux·服务器·c#
河码匠2 小时前
namespace 网络命名空间、使用网络命名空间实现虚拟路由
linux·网络
真的想上岸啊2 小时前
3、用SSH方式登录板子
linux
开开心心就好2 小时前
打印机驱动搜索下载工具,自动识别手动搜
java·linux·开发语言·网络·stm32·物联网·电脑
新兴AI民工3 小时前
【Linux内核十一】进程管理模块:stop调度器(一)
linux·服务器·linux内核
刘叨叨趣味运维3 小时前
Linux发行版选择指南:找到你的最佳拍档
linux