如何强制页面在下次访问时生成页面错误?

问题描述 投票:0回答:1

我正在尝试使用 SVE 开发一个例程。 SVE 提供了避免故障的加载,这些加载不会从内存中加载,否则访问时会导致故障。由于 CPU 不知道页面未映射或不可访问的原因,因此它无法区分会触发无效页面错误的内存和会触发主要/次要页面错误的内存(这通常对应用程序是透明的)。

因此,使用这些指令的 SVE 代码必须准备好指示杂散故障,并且如果毕竟需要数据,则必须使用非故障指令重试加载。例如,考虑一个对 NUL 终止字符串进行操作的例程。首次故障加载用于加载字符串块。如果仅在 NUL 字符之后才避免错误,则一切正常。但如果这种情况发生在 NUL 字符之前,我们必须使用传统加载指令重试加载,因为该字符串已被证明会跨入错误页面。

如果代码中存在此类“重试避免的故障”路径,则必须对其进行测试。然而,对我来说,如何在下次访问时准备一个页面错误(主要或次要页面错误)似乎并不明显。如果全零页面是可以接受的,则可能只映射一个新的匿名页面并利用内核的惰性页面分配。但是,并不能保证或记录这会产生预期的效果。

对于任意页面,

madvise
系统调用具有
MADV_PAGEOUT
选项,这看起来可能会产生所需的效果,但手册页没有记录效果是否立即发生,并指出它可能不会影响某些页。还不清楚该调用在没有交换空间的情况下是否有效。成功/失败似乎没有明确报告,因此不清楚单元测试是否可以依赖此调用。由于页面在运行时实际上并未取消映射,因此单元测试默默地通过是非常糟糕的。

建议的行动方案是什么?

也对其他操作系统(例如 FreeBSD)的响应感兴趣,特别是特定于硬件的方法,这些方法可能特定于 ARM,也可能不特定。

linux assembly arm64 page-fault sve
1个回答
1
投票

我在本地 WSL2(基本上是在 Hyper-V 虚拟机中运行的真正的 Linux 内核)和 godbolt.org 上尝试了以下代码:

#include <sys/mman.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>

int main(void)
{
    struct timespec tsa, tsb;
    int my_fd = open("/proc/self/exe", O_RDONLY);
    char* mapping = mmap(NULL, 8192, PROT_READ, MAP_SHARED, my_fd, 0);
    volatile int x = mapping[0] + mapping[4096];
    munmap(mapping + 4096, 4096);
    mmap(mapping + 4096, 4096, PROT_READ, MAP_SHARED | MAP_ANON, 0, 0);
    volatile int y = mapping[4096];
    munmap(mapping + 4096, 4096);
    mmap(mapping + 4096, 4095, PROT_READ, MAP_SHARED, my_fd, 4096);
    clock_gettime(CLOCK_MONOTONIC, &tsa);  // prefetch clock_gettime
    clock_gettime(CLOCK_MONOTONIC, &tsa);
    volatile int x2 = mapping[0];
    clock_gettime(CLOCK_MONOTONIC, &tsb);
    printf("first page (no fault): %d ns\n", (tsb.tv_nsec + 1000000000 - tsa.tv_nsec) % 999999999);
    clock_gettime(CLOCK_MONOTONIC, &tsa);
    volatile int y2 = mapping[4096];
    clock_gettime(CLOCK_MONOTONIC, &tsb);
    printf("second page (fault?): %d ns\n", (tsb.tv_nsec + 1000000000 - tsa.tv_nsec) % 999999999);
}

目标是创建一个双页映射,并启动文件系统缓存(在

/proc/self/exe
上可能很热),然后它将用其他内容替换第二页并恢复自映射。第一次访问第二页非常慢,这表明文件系统缓存的重新映射发生在页面错误处理程序中第一次使用时,而不是在 mmap 调用上。

godbolt 的输出看起来像这样:

first page (no fault): 76 ns
second page (fault?): 1032 ns
© www.soinside.com 2019 - 2024. All rights reserved.