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

标签 linux assembly arm64 page-fault sve

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

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

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

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

建议的行动方案是什么?

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

最佳答案

我在本地 WSL2(基本上是在 Hyper-V VM 中运行的真正 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

关于linux - 如何强制页面在下次访问时生成页面错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77209008/

相关文章:

c++ - 套接字发送调用被阻塞了这么久

linux - 列出带有 .bar 扩展名但不带有 .foo.bar 扩展名的目录中的所有文件

gcc - 为什么 gcc 在创建汇编代码时会这样做?

assembly - 我可以在每个内核的基础上编写汇编代码吗?

memory-management - 了解 64 位 Linux 上的 kmap

ios - asm ("trap") 在 64 位 iOS 设备上

assembly - 了解aarch64汇编函数调用,栈是如何操作的?

linux - 用于查找文件名中的日期比当前日期和时间更新的所有文件的 bash 脚本

linux - 终端:发送禁止操作转义序列或控制字符

assembly - 使用 gdb 和 objdump 进行调试