如何查询mmap MAP_SHARED内存页被多少个进程映射?
查询共享内存页的映射进程数
一、POSIX可移植方案(需自行维护)
POSIX标准没有提供直接查询内核中共享内存页映射进程数的API——这类页表引用计数属于系统实现细节,不在POSIX规范覆盖范围内。如果要做可移植的实现,只能自己在共享内存里维护引用计数:
- 在共享内存区域的开头预留一块空间,用原子操作(比如
atomic_fetch_add/atomic_fetch_sub)来管理计数:进程成功映射后原子加1,退出前(可以用atexit注册回调)原子减1。 - 注意:这种方式完全依赖所有使用该共享内存的进程都遵守这个计数规则,如果有进程异常退出或者不按约定操作,计数会不准确。
二、Linux平台专属方案
Linux可以通过/proc虚拟文件系统实现查询,步骤如下:
- 定位目标映射的标识信息
- 读取当前进程的
/proc/self/maps文件,找到目标指针对应的行:- 如果是文件映射(基于磁盘文件的mmap),记录下文件路径、偏移量和inode号;
- 如果是匿名共享内存(
MAP_ANONYMOUS | MAP_SHARED),该行会显示[anon],记录下inode号(内核会给每个匿名共享映射分配唯一的inode)。
- 读取当前进程的
- 遍历所有进程统计匹配数
- 遍历
/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实现查询:
- 获取目标映射的标识
- 调用
vm_region_64函数,传入目标指针,获取该内存区域的vm_region_basic_info_64结构体,其中的object_id是共享内存对象的唯一标识(文件映射对应vnode的ID,匿名共享映射对应内核对象ID)。
- 调用
- 遍历所有进程统计匹配数
- 用
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)®ion_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
相关产品推荐
相关产品推荐

