用户程序如何pin内存页?get_user_pages相关page buffer概念疑问
首先先澄清认知偏差:应用层的「页pin」操作本质是向内核声明指定内存页不允许被换出/回收,get_user_pages()是内核态(通常是驱动、内核子系统)调用的接口,用于获取用户态页的稳定内核态引用,并非由应用层直接调用。
应用层常用的pin内存接口有以下几种:
- 最通用的是
mlock(addr, len)系统调用,可锁定起始地址为addr、长度为len的连续地址范围对应的物理页,锁定后这些页不会被swap到磁盘,也不会被内核内存回收逻辑释放。如果需要锁定整个进程的所有内存页,可以用mlockall(flags)接口,flags支持指定是否锁定未来新分配的内存。 - 调用
mmap()分配内存时传入MAP_LOCKED标志,分配的内存会被自动锁定,等价于mmap()完成后立刻调用mlock()。 - 特殊场景下也可以通过
O_DIRECT打开文件做IO,内核会自动pin住对应IO的用户态内存页,不过这个是IO路径的隐式行为,不属于通用的内存pin操作。
注意:非特权进程可锁定的内存大小受RLIMIT_MEMLOCK资源限制,默认值通常为64KB,超过限制会调用失败,需要调整资源限制或者使用特权进程操作。
首先可以明确:这里的page buffer就是指块设备/文件对应的页缓存,不是被pin的物理页本身。
页被pin住处于IO过程中触发page buffer剥离的原因,和文件缓存的一致性逻辑相关:
如果被pin的页是文件-backed的页缓存(也就是属于某个文件的page cache副本),当内核调用get_user_pages()获取该页的引用用于DMA等IO操作时,如果是可写场景,内核会将该页从对应文件的地址空间(address_space)缓存树中移除,也就是所谓的「剥离」。这么做的目的是避免该页同时被两个逻辑路径修改:一边是DMA设备直接读写物理页内容,另一边是文件系统缓存逻辑对该页做截断、回写、更新等操作,两边不同步会导致严重的数据不一致问题。
这个行为也是早年get_user_pages()接口的核心问题之一:被剥离后的页和原文件缓存完全脱离,后续DMA写入的内容不会自动同步到文件缓存中,反过来对原文件的修改也不会反映到被pin的物理页上,会出现用户态可见的内容不一致问题。
内容的提问来源于stack exchange,提问作者Chan Kim

