假设,对于一个进程,我知道它有一个位于虚拟地址的页面0x42424242
。我想手动将其交换到交换区域。从用户空间来看,我认为这很难做到。我没有找到任何相关命令来强制内核执行此操作。这是否意味着我必须破解内核并实现此功能?
答案1
看着https://stackoverflow.com/questions/578137/can-i-tell-linux-not-to-swap-out-a-particular-processes-memory,特别是你可能想检查madvise
系统调用及其建议争论MADV_DONTNEED
:http://man7.org/linux/man-pages/man2/madvise.2.html。
以下是《Linux 编程接口》一书第 1055 页的引文:
MADV_DONTNEED 调用进程不再需要此区域中的页面驻留在内存中。此标志的确切效果因 UNIX 实现而异。... Linux:
对于 MAP_PRIVATE 区域,映射的页面被明确丢弃,这意味着对页面的修改将丢失。虚拟内存地址范围仍然可访问,但每次访问每个页面时都将导致页面错误,重新初始化页面,使用映射文件的内容,或者在匿名映射的情况下使用零。
对于 MAP_SHARED 区域,内核可能在某些情况下丢弃已修改的页面,具体取决于架构(在 x86 上不会发生)。
当然,内核可以完全忽略这个“提示”。
答案2
有一个核心算法来决定它,我将尝试使用通用术语来举例说明。
即使文件已被 mmapped,访问时也只会从磁盘读取。很有可能 0x42424242 从未被访问过,具体取决于驻留在那里的数据类型。
假设 0x42424xxx 页面已被访问,页面错误处理程序应该从磁盘检索它并相应地设置内核结构,除此之外,它还应该设置页面最后访问相当于 RDSTC 的输出,并且该页面的 CPU 页表条目“A”位被重置(在 Linux 中调用 pte_mkold())。
在给定的时间之后,内核轮询页表中是否设置了位“A”的页面,如果是,则更新该页面页面最后访问并将“A”重置回 0,让 CPU 在页面访问时重新启用它。请注意,由于其他地址的读/写/执行,CPU 发出了内存写入。
每当有人调用 malloc() 时,内核算法就会寻找已知最旧的页面,然后释放或换出它,这取决于该页面的脏位标志是否为,首先可以合理地假设这些页面可能来自早于您自己的进程。
Tl;dr - 映射一个大文件然后只读取少量页面是一种常规做法,用户空间程序不应该担心它。