You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用madvise(..., MADV_DONTNEED)会触发段错误吗?如何识别合法内存段?

关于madvise(*, *, MADV_DONTNEED)的问题及解答

问题描述

  1. madvise(*, *, MADV_DONTNEED)是否属于纯建议性操作?
  2. 尝试扫描/proc/self/maps并对所有条目调用madvise(addr, size, MADV_DONTNEED)时出现段错误,有没有简便方法判断哪些内存段可以安全调用该操作?

复现场景

运行以下代码:

#include <stdio.h>
#include <sys/mman.h>

int main(void) {
    FILE *fp;
    char line[1024];

    fp = fopen("/proc/self/maps", "r");
    if (fp == NULL) {
        perror("Failed to open maps file");
        return 1;
    }

    while (fgets(line, sizeof(line), fp)) {
        char addr[32], perms[8], offset[32], dev[32], inode[32], path[256];
        unsigned long start, end;

        path[0] = '\0';
        sscanf(line, "%lx-%lx %s %s %s %s %[^\n]", &start, &end, perms, offset, dev, inode, path);

        int ret = madvise((void*)start, end - start, MADV_DONTNEED);
        if(ret == 0)
            printf("  OK\n");
        else
            printf("  Error\n");
        fflush(stdout);
    }

    fclose(fp);
}

输出结果:

OK
  OK
  OK
  OK
Segmentation fault

解答

1. MADV_DONTNEED是不是纯建议性操作?

不完全是。虽然madvise的多数标记是建议性的,但Linux下的MADV_DONTNEED会直接触发内核释放对应内存页的物理映射:

  • 匿名页会被直接清零并释放物理内存;
  • 文件映射的缓存页会被丢弃,后续访问会重新从文件读取。
    不过它不会修改虚拟地址空间的权限,进程后续访问该地址时会自动重新分配物理页。但如果操作的是内核专属内存区域,会触发权限错误甚至段错误。

2. 为什么会出现段错误?

/proc/self/maps包含了进程的所有虚拟内存段,其中包括内核管理的特殊段(比如[vdso]、[vsyscall]、[vvar]),这些属于内核空间,用户进程没有权限对其调用madvise,强行操作就会触发段错误。

3. 如何判断可操作的内存段?

可以通过/proc/self/maps或/proc/self/smaps的字段筛选:

  • 权限检查:/proc/self/maps的第二列是权限字段,确保段拥有r(读)权限——madvise操作至少需要读权限才能执行;
  • 排除内核特殊段:跳过名称为[vdso]、[vsyscall]、[vvar]的段;
  • 进阶筛选(可选):查看/proc/self/smaps的VmFlags字段,排除带有locked、kernel标记的段。

修改后的安全代码示例

在调用madvise前加入判断逻辑:

#include <stdio.h>
#include <sys/mman.h>
#include <string.h>

int main(void) {
    FILE *fp;
    char line[1024];

    fp = fopen("/proc/self/maps", "r");
    if (fp == NULL) {
        perror("Failed to open maps file");
        return 1;
    }

    while (fgets(line, sizeof(line), fp)) {
        char addr[32], perms[8], offset[32], dev[32], inode[32], path[256];
        unsigned long start, end;

        path[0] = '\0';
        sscanf(line, "%lx-%lx %s %s %s %s %[^\n]", &start, &end, perms, offset, dev, inode, path);

        // 跳过内核特殊段
        if (strstr(path, "[vdso]") != NULL || strstr(path, "[vsyscall]") != NULL || strstr(path, "[vvar]") != NULL) {
            printf("  Skip kernel segment\n");
            continue;
        }

        // 检查是否有读权限
        if (strchr(perms, 'r') == NULL) {
            printf("  Skip no-read segment\n");
            continue;
        }

        int ret = madvise((void*)start, end - start, MADV_DONTNEED);
        if(ret == 0)
            printf("  OK\n");
        else
            printf("  Error\n");
        fflush(stdout);
    }

    fclose(fp);
    return 0;
}

内容的提问来源于stack exchange,提问作者MWB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 07:03:19