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

如何查询mmap MAP_SHARED内存页被多少个进程映射?

查询共享内存页的映射进程数

一、POSIX可移植方案(需自行维护)

POSIX标准没有提供直接查询内核中共享内存页映射进程数的API——这类页表引用计数属于系统实现细节,不在POSIX规范覆盖范围内。如果要做可移植的实现,只能自己在共享内存里维护引用计数:

  • 在共享内存区域的开头预留一块空间,用原子操作(比如atomic_fetch_add/atomic_fetch_sub)来管理计数:进程成功映射后原子加1,退出前(可以用atexit注册回调)原子减1。
  • 注意:这种方式完全依赖所有使用该共享内存的进程都遵守这个计数规则,如果有进程异常退出或者不按约定操作,计数会不准确。

二、Linux平台专属方案

Linux可以通过/proc虚拟文件系统实现查询,步骤如下:

  1. 定位目标映射的标识信息
    • 读取当前进程的/proc/self/maps文件,找到目标指针对应的行:
      • 如果是文件映射(基于磁盘文件的mmap),记录下文件路径、偏移量和inode号;
      • 如果是匿名共享内存(MAP_ANONYMOUS | MAP_SHARED),该行会显示[anon],记录下inode号(内核会给每个匿名共享映射分配唯一的inode)。
  2. 遍历所有进程统计匹配数
    • 遍历/proc目录下的所有进程ID(数字命名的目录),逐个读取/proc/[pid]/maps文件;
    • 对每个进程的映射项,检查是否是共享映射(权限位带s),并且inode、偏移量(文件映射)匹配目标映射的标识;
    • 统计所有符合条件的进程数量。

简化代码示例

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <ctype.h>

int count_shared_mmap_processes(void *addr) {
    // 1. 从当前进程maps中提取目标映射的关键信息
    FILE *self_maps = fopen("/proc/self/maps", "r");
    if (!self_maps) return -1;

    char line[256];
    unsigned long target_start = 0, target_end = 0;
    unsigned long target_offset = 0;
    unsigned long target_inode = 0;
    char target_path[256] = {0};

    while (fgets(line, sizeof(line), self_maps)) {
        unsigned long start, end;
        char perm[5];
        unsigned long offset;
        unsigned int dev_maj, dev_min;
        unsigned long inode;
        char path[256];

        if (sscanf(line, "%lx-%lx %s %lx %x:%x %lu %s", &start, &end, perm, &offset, &dev_maj, &dev_min, &inode, path) >= 7) {
            if ((unsigned long)addr >= start && (unsigned long)addr < end) {
                target_start = start;
                target_end = end;
                target_offset = offset;
                target_inode = inode;
                strncpy(target_path, path, sizeof(target_path)-1);
                break;
            }
        }
    }
    fclose(self_maps);
    if (target_start == 0) return -1;

    // 2. 遍历所有进程统计匹配数
    int count = 0;
    DIR *proc_dir = opendir("/proc");
    if (!proc_dir) return -1;

    struct dirent *entry;
    while ((entry = readdir(proc_dir)) != NULL) {
        if (entry->d_type != DT_DIR || !isdigit(entry->d_name[0])) {
            continue;
        }

        char proc_maps_path[256];
        snprintf(proc_maps_path, sizeof(proc_maps_path), "/proc/%s/maps", entry->d_name);
        FILE *proc_maps = fopen(proc_maps_path, "r");
        if (!proc_maps) continue;

        int matched = 0;
        while (fgets(line, sizeof(line), proc_maps)) {
            unsigned long start, end;
            char perm[5];
            unsigned long offset;
            unsigned int dev_maj, dev_min;
            unsigned long inode;
            char path[256];

            if (sscanf(line, "%lx-%lx %s %lx %x:%x %lu %s", &start, &end, perm, &offset, &dev_maj, &dev_min, &inode, path) >= 7) {
                // 检查是否是共享映射,且标识匹配
                if (strchr(perm, 's') && inode == target_inode && offset == target_offset) {
                    matched = 1;
                    break;
                }
            }
        }
        if (matched) count++;
        fclose(proc_maps);
    }
    closedir(proc_dir);
    return count;
}
  • 注意:需要root权限才能读取其他进程的/proc/[pid]/maps;匿名共享内存的inode匹配逻辑在不同内核版本下可能有细微差异,需测试验证。

三、macOS平台专属方案

macOS可以通过内核提供的proc_info和vm_region系列API实现查询:

  1. 获取目标映射的标识
    • 调用vm_region_64函数,传入目标指针,获取该内存区域的vm_region_basic_info_64结构体,其中的object_id是共享内存对象的唯一标识(文件映射对应vnode的ID,匿名共享映射对应内核对象ID)。
  2. 遍历所有进程统计匹配数
    • 用proc_listpids获取系统中所有进程ID;
    • 对每个PID调用proc_pidinfo,传入PROC_PIDVMMAP参数获取进程的内存映射信息;
    • 遍历进程的VM区域,检查是否是共享映射(pve_protection带VM_PROT_SHARED),且pve_object_id匹配目标标识,统计符合条件的进程数。

简化代码示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/proc_info.h>
#include <sys/vm.h>
#include <mach/mach.h>
#include <string.h>

int count_shared_mmap_processes(void *addr) {
    mach_port_t task = mach_task_self();
    vm_address_t vm_addr = (vm_address_t)addr;
    vm_size_t vm_size;
    vm_region_basic_info_64 region_info;
    mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
    memory_object_name_t obj_name;

    // 获取当前进程中目标地址的区域信息
    kern_return_t ret = vm_region_64(task, &vm_addr, &vm_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&region_info, &info_count, &obj_name);
    if (ret != KERN_SUCCESS) return -1;
    uint64_t target_obj_id = region_info.object_id;

    // 获取所有进程ID
    pid_t pids[1024];
    int num_pids = proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids));
    if (num_pids <= 0) return -1;

    int count = 0;
    for (int i = 0; i < num_pids; i++) {
        pid_t pid = pids[i];
        if (pid == 0) continue;

        // 获取进程的VM映射信息
        size_t vmmap_size = proc_pidinfo(pid, PROC_PIDVMMAP, 0, NULL, 0);
        if (vmmap_size <= 0) continue;

        struct proc_vmmap *vmmap = malloc(vmmap_size);
        if (!vmmap) continue;

        ret = proc_pidinfo(pid, PROC_PIDVMMAP, 0, vmmap, vmmap_size);
        if (ret != vmmap_size) {
            free(vmmap);
            continue;
        }

        // 遍历VM区域找匹配项
        struct proc_vmmap_entry *entry = (struct proc_vmmap_entry *)vmmap;
        int matched = 0;
        while ((char *)entry < (char *)vmmap + vmmap_size) {
            if ((entry->pve_protection & VM_PROT_SHARED) && entry->pve_object_id == target_obj_id) {
                matched = 1;
                break;
            }
            entry = (struct proc_vmmap_entry *)((char *)entry + entry->pve_next_offset);
        }
        if (matched) count++;
        free(vmmap);
    }
    return count;
}
  • 注意:需要root权限才能访问其他进程的VM映射信息;macOS的API会随系统版本更新,需根据目标系统调整(比如部分版本可能需要用vm_region而非vm_region_64)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 07:52:58