内存管理-5-物理内存数据结构-4-struct address_space
基于msm-5.4
一、简介
在 Linux 内存管理里,struct address_space 可以理解为“一个可缓存、可映射对象在内核中的内存视图描述符”。
最常见的对象是文件 inode,对应 inode.i_mapping。它把这几个关键维度绑在一起:
(1) 页缓存管理(哪些页在 page cache 里).
(2) 用户态 mmap 映射关系(哪些 VMA 映射了它).
(3) 回写与错误传播(脏页写回、回写错误).
(4) 文件系统回调接口(a_ops)
所以它是文件系统层和内存管理层的核心桥梁结构。是文件页缓存与 mmap 映射的总控制块,既管页,也管映射,还负责把回写错误和文件系统操作连接起来。”
一个文件在内存中对应的所有物理页组成了一棵基数树。而一个文件在内存中具有唯一的 inode 结构标识,inode 结构中有该文件所属的设备及其标识符,因而,根据一个 inode 能够确定其对应的后备设备。为了将文件在物理内存中的页缓存和文件及其后备设备关联起来,linux内核引入了 address_space 结构体。可以说 address_space 结构体是将页缓存和文件系统关联起来的桥梁。
struct address_space 结构不但可以用于描述文件页缓存(如 file->f_mapping),还可以用做描述swapcache,如 struct address_space *swapper_spaces[];
二、成员介绍
struct address_space { //fs.h struct inode *host; struct xarray i_pages; gfp_t gfp_mask; atomic_t i_mmap_writable; #ifdef CONFIG_READ_ONLY_THP_FOR_FS atomic_t nr_thps; #endif struct rb_root_cached i_mmap; struct rw_semaphore i_mmap_rwsem; unsigned long nrpages; unsigned long nrexceptional; pgoff_t writeback_index; const struct address_space_operations *a_ops; unsigned long flags; errseq_t wb_err; spinlock_t private_lock; struct list_head private_list; void *private_data; } __attribute__((aligned(sizeof(long)))) __randomize_layout;
注: 此结构通常作为 struct inode.i_mapping 指向,它又指向 inode.i_data 成员,打开一个文件时创建的 struct file 对象,struct file.f_mapping 会继承 inode.i_mapping,这样就可以通过打开的文件访问页缓存了。多个进程打开同一个文件时,所有 file 对象的 file.f_mmaping 都会指向相同的 inode.i_mapping。
成员介绍:
1. host:
该 mapping 的拥有者,通常是 inode,也可能是 block_device。作用是让 VM 从 mapping 回到“上层对象”,例如拿到 inode 属性、超级块信息等。
2. i_pages:
页缓存容器, 使用xarray来存储所有的页缓存页。里面存的是普通 page cache 页,或 exceptional entry(例如 shadow 条目、DAX 特殊条目)。作用是按页索引快速查找缓存页,是 read/write/mmap 缓存命中的核心数据结构。
3. gfp_mask:
这个 mapping 分配页缓存页时允许使用的内存分配标志。用于约束分配行为,例如是否允许进入文件系统回收路径,避免递归死锁等。
4. i_mmap_writable:
可写共享映射计数(VM_SHARED 且可写相关语义)。用于判断该文件是否存在可写共享映射,支持“禁止新增可写映射”等控制逻辑(例如写保护相关场景).
5. nr_thps:
该 mapping 中 THP 页缓存计数(仅特定配置下存在)。用于统计与策略判断,主要给只读文件 THP 场景使用。
6. i_mmap:
映射该文件的 VMA 区间树(红黑树缓存根)。作用是当页失效、截断、回收、写保护变化时,内核可快速找到受影响的用户映射并进行页表级处理。管理所有映射该文件的进程虚拟地址区域。
7. i_mmap_rwsem:
保护 i_mmap 和 i_mmap_writable 的读写信号量。用于并发控制,确保 mmap 建立/拆除与遍历映射关系时一致性。
8. nrpages:
当前 mapping 中“普通 page cache 页条目数”。用于快速统计缓存规模,供回收和写回路径参考。
9. nrexceptional:
当前 mapping 中 exceptional entry 数量。典型包含:shadow entries(回收后留下的工作集提示)、DAX 相关条目。用于帮助内核区分“真实页缓存页”和“特殊占位条目”。
10. writeback_index:
写回扫描起始位置(页索引)。用于回写时从这个位置继续推进,避免每次都从头扫描,提高大文件回写公平性和效率。
回写起始索引,记录脏页写回的起始页号,避免重复刷盘。
11. a_ops:
address_space_operations 回调函数表。用于告诉 VM “这个 mapping 的页该如何读、写、失效、迁移、直接 IO”等。典型函数包括 readpage、writepage、writepages、write_begin、write_end、invalidatepage、releasepage、migratepage、swap_activate 等。
页缓存的操作函数集。
12. flags:
mapping 状态位集合,取值:
/* Bits in mapping->flags. */ enum mapping_flags { //pagemap.h AS_EIO = 0, /* IO error on async write */ AS_ENOSPC = 1, /* ENOSPC on async write */ AS_MM_ALL_LOCKS = 2, /* under mm_take_all_locks() */ AS_UNEVICTABLE = 3, /* e.g., ramdisk, SHM_LOCK */ AS_EXITING = 4, /* final truncate in progress */ /* writeback related tags are not used */ AS_NO_WRITEBACK_TAGS = 5, };
常见语义:
(1) 异步回写错误标记(如 EIO、ENOSPC 的历史兼容位).
(2) 是否不可回收(unevictable),使用函数如 mapping_unevictable()。这个是 address_space 级别的,不是页级别的。
(3) 是否正在退出(exiting).
(4) 是否使用写回标签等策略位.
13. wb_err:
基于 errseq 的回写错误序列状态。用于记录最近一次写回错误。让 fsync 等接口按“采样点之后是否出错”语义向用户态报告错误,这是现代内核报告写回错误的关键机制。
14. private_lock:
留给 mapping 拥有者使用的私有自旋锁。用于保护 private_list 和 private_data,具体语义由具体文件系统或子系统定义。
15. private_list:
留给拥有者挂接私有链表节点的链表头。用于扩展,常用于文件系统自己的脏元数据、缓冲头或其他关联对象的管理。
16. private_data:
留给拥有者的私有指针。也是扩展点,指向文件系统或设备自定义上下文。
三、struct address_space_operations
1. 结构体描述
struct address_space_operations { //fs.h int (*writepage)(struct page *page, struct writeback_control *wbc); int (*readpage)(struct file *, struct page *); int (*writepages)(struct address_space *, struct writeback_control *); int (*set_page_dirty)(struct page *page); int (*readpages)(struct file *filp, struct address_space *mapping, struct list_head *pages, unsigned nr_pages); int (*write_begin)(struct file *, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata); int (*write_end)(struct file *, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata); sector_t (*bmap)(struct address_space *, sector_t); void (*invalidatepage) (struct page *, unsigned int, unsigned int); int (*releasepage) (struct page *, gfp_t); void (*freepage)(struct page *); ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter); int (*migratepage) (struct address_space *, struct page *, struct page *, enum migrate_mode); bool (*isolate_page)(struct page *, isolate_mode_t); void (*putback_page)(struct page *); int (*launder_page) (struct page *); int (*is_partially_uptodate) (struct page *, unsigned long, unsigned long); void (*is_dirty_writeback) (struct page *, bool *, bool *); int (*error_remove_page)(struct address_space *, struct page *); int (*swap_activate)(struct swap_info_struct *sis, struct file *file, sector_t *span); void (*swap_deactivate)(struct file *file); };
address_space_operations 是文件系统提供给 VM 的“页缓存操作接口表”。VM 不关心你是 ext4、xfs 还是其他文件系统,它只通过这组回调完成:
(1) 把页读进 page cache.
(2) 把脏页写回存储.
(3) 处理截断、回收、迁移.
(4) 支持 swapfile 激活与停用.
可以把它看成:VM 到具体文件系统的数据面适配层。
这里按“读路径、写路径、回收迁移、swap 支持”分组,逐个解释它们的职责和典型调用场景。
2. 写回相关回调
2.1 writepage(page, wbc)
含义:回写单个脏页。
场景:回收脏页、后台回写线程扫描到脏页。
要点:常见返回0;有时可返回 AOP_WRITEPAGE_ACTIVATE,提示 VM 把页重新激活而不是继续回收。
2.2 writepages(mapping, wbc)
含义:批量回写该 mapping 的脏页。
场景:sync、fsync、后台脏页回写、内存回收触发集中写回。
要点:通常比逐页 writepage 更高效,可按文件系统策略聚合IO。
2.3 set_page_dirty(page)
含义:把页标脏(并维护文件系统所需的脏状态)。
场景:页内容被修改后。
要点:返回值表示这次是否真的把页从“非脏”变成“脏”。
调用路径:
set_page_dirty(page) //mm/page-writeback.c mapping->a_ops->set_page_dirty(page)
3. 读取与预读回调
3.1 readpage(file, page)
含义:把一个页从后备存储读入。
场景:缺页或缓存未命中。
要点:需要正确设置页的 uptodate、错误状态,并解锁页。
调用路径:
generic_file_buffered_read //mm/filemap.c mapping->a_ops->readpage(filp, page);
3.2 readpages(file, mapping, pages, nr_pages)
含义:批量预读。
场景:readahead。
要点:和 readpage 不同,它是“预取优化接口”,目标是提高顺序读吞吐。
调用路径:
read_pages //mm/readahead.c mapping->a_ops->readpages(filp, mapping, pages, nr_pages);
4. buffered write回调
这两个是 buffered write 路径最核心的一对回调。
4.1 write_begin(file, mapping, pos, len, flags, pagep, fsdata)
含义:开始一次 buffered write,准备目标页。
场景:用户写文件走 page cache 路径时。
要点:负责拿到并锁定页、处理块映射/空间预留等前置动作。
调用路径:
pagecache_write_begin //mm/filemap.c mapping->a_ops->write_begin(file, mapping, pos, len, flags, pagep, fsdata); generic_perform_write //mm/filemap.c mapping->a_ops->write_begin(file, mapping, pos, bytes, flags, &page, &fsdata);
(2) write_end(file, mapping, pos, len, copied, page, fsdata)
含义:结束一次 buffered write。
场景:拷贝数据到页之后。
要点:更新脏状态、i_size、元数据等;返回实际写入字节数或错误。
调用路径:
pagecache_write_end //mm/filemap.c mapping->a_ops->write_end(file, mapping, pos, len, copied, page, fsdata); generic_perform_write mapping->a_ops->write_end(file, mapping, pos, bytes, copied, page, fsdata);
5. 块映射与页生命周期回调
5.1 bmap(mapping, block)
含义:把文件逻辑块映射到设备物理块(历史接口)。
场景:主要给旧接口如 FIBMAP。
要点:现代路径使用减少,但仍可能被实现。
调用路径:
bmap(inode, block) //fs/inode.c inode->i_mapping->a_ops->bmap(inode->i_mapping, block);
5.2 invalidatepage(page, offset, length)
含义:页局部或全部失效时,释放对应私有状态。
场景:truncate、hole punch、失效页缓存。
要点:常见用于释放 buffer_head 等附属结构。
调用路径:
do_invalidatepage(page, offset, length) //mm/truncate.c page->mapping->a_ops->invalidatepage(page, offset, length);
5.3 releasepage(page, gfp)
含义:回收器想释放该页时,询问文件系统能否释放其私有数据。
场景:内存回收。
要点:返回 1 表示已释放可继续回收,返回 0 表示当前不能释放。
调用路径:
try_to_release_page(page, gfp_mask) //mm/filemap.c mapping->a_ops->releasepage(page, gfp_mask);
5.4 freepage(page)
含义:页即将被释放时的最终清理钩子。
场景:页从缓存移除并真正释放。
要点:清理文件系统在页上的残留私有资源。
6. 直接IO与迁移回调
6.1 direct_IO(iocb, iter)
含义:绕过 page cache 的直接 IO。
场景:O_DIRECT。
要点:由文件系统自行处理对齐、块映射和数据传输。
调用路径:
generic_file_read_iter(iocb, iter) //mm/filemap.c mapping->a_ops->direct_IO(iocb, iter); generic_file_direct_write(iocb, from) //mm/filemap.c mapping->a_ops->direct_IO(iocb, from);
5.2 migratepage(mapping, newpage, oldpage, mode)
含义:页面迁移时搬运页内容及其私有状态。
场景:NUMA 迁移、内存压缩/整理、内存热迁移。
要点:异步迁移模式下不能随意阻塞。
调用路径:
move_to_new_page(newpage, page, mode) //mm/migrate.c mapping->a_ops->migratepage(mapping, newpage, page, mode); move_to_new_page(newpage, page, mode) //mm/migrate.c mapping->a_ops->migratepage(mapping, newpage, page, mode);
6.3 isolate_page(page, isolate_mode) / putback_page(page)
含义:配合文件系统管理的特殊页隔离与放回。
场景:特定回收或迁移策略。
要点:不是所有文件系统都实现。
6.4 launder_page(page)
含义:在回收前“清洗”页(典型是确保脏状态处理完)。
场景:回收路径需要更强保证时。
要点:可触发写回或等待。
7. 页状态判断与错误处理回调
(1) is_partially_uptodate(page, from, count)
含义:页是否“部分有效”。
场景:子页粒度有效性判断(某些文件系统或网络文件系统场景)。
要点:优化部分读取和一致性判断。
(2) is_dirty_writeback(page, dirty, writeback)
含义:查询页的脏/回写状态(含文件系统私有状态)。
场景:VM 在通用标志之外需要 FS 辅助判断时。
要点:帮助 VM 决策回收与等待行为。
(3) error_remove_page(mapping, page)
含义:在错误场景下从缓存移除页。
场景:IO错误恢复、数据一致性异常处理。
要点:让文件系统介入决定如何安全移除或清理该页。
9. swapfile支持回调
9.1 swap_activate(sis, file, span)
含义:把普通文件激活为 swapfile。
场景:swapon 指向文件时。
要点:建立文件逻辑页到可交换后备块的映射关系,并返回可用跨度信息。
调用路径:
setup_swap_extents(sis, span) //mm/swapfile.c mapping->a_ops->swap_activate(sis, swap_file, span);
9.2 swap_deactivate(file)
含义:停用 swapfile。
场景:swapoff 时。
要点:清理激活期间建立的文件系统侧状态。
调用路径:
destroy_swap_extents(sis) //mm/swapfile.c mapping->a_ops->swap_deactivate(swap_file);
如果你读某个文件系统实现,按这个顺序最容易抓主干:先看 write_begin/write_end, 确认 buffered write 语义; 再看 readpage/readpages 确认读入和预读行为; 再看 writepage/writepages 确认回写策略; 最后看 invalidatepage/releasepage/migratepage 确认回收与迁移兼容性。
posted on 2026-04-02 17:27 Hello-World3 阅读(15) 评论(0) 收藏 举报
浙公网安备 33010602011771号