为何mmap虚拟地址转物理地址始终为0?0x670有何特殊?
让我来逐一分析你遇到的两个问题:
一、mmap映射文件后页面未找到的原因
你用mmap映射文件后调用vtop返回页面未找到,核心原因和Linux内存管理的特性相关,主要有以下几点:
按需分页的懒加载机制:
mmap创建映射时,内核并不会立刻把文件内容加载到物理内存,也不会生成对应的页表项——只有当你实际读写该映射内存的某个页面时,内核才会触发缺页中断,完成文件内容加载、物理页分配和页表更新。你的代码在mmap后直接调用vtop,此时虚拟地址对应的页表项还未建立,pagemap中的present位(第63位)为0,所以vtop判定页面未找到。权限配置的潜在冲突:你用
O_RDONLY打开文件,但mmap的mprot设置了PROT_WRITE。虽然MAP_PRIVATE模式下写操作会触发写时复制(创建私有内存副本),但原文件只读的属性会让内核进一步延迟页表的建立,甚至在未实际访问时完全不分配物理页。验证方法:你可以在调用
vtop前,尝试访问映射的内存(比如执行*(char*)addr = 'x';,MAP_PRIVATE模式下这个修改不会影响原文件),之后再调用vtop,此时应该能获取到有效的物理地址。
二、malloc分配内存得到物理地址0x670的特殊之处
首先要明确:物理地址由页帧号(PFN)和页内偏移组成,假设你的系统页面大小是4KB(最常见的配置),页内偏移占低12位(范围0~0xFFF)。
0x670是低页帧号+页内偏移的组合:0x670的数值小于4096,说明它的低12位就是完整的页内偏移,而高地址部分(页帧号)为0——意味着这个物理地址属于系统的第0号物理页。
第0号物理页的特殊性:在绝大多数Linux系统中,物理页0是内核保留的特殊页,通常用于存储BIOS/UEFI启动信息、内核初始数据结构等。你的malloc分配的内存映射到这个页,大概率是因为:
- 你分配的是极小的内存块,malloc使用了进程初始堆的预留页,而该页恰好被内核映射到物理页0;
- 如果是嵌入式或定制化系统,可能存在物理页0允许分配给用户进程的特殊配置(这种情况比较少见)。
你可以通过sysconf(_SC_PAGESIZE)确认系统页面大小,这会直接影响物理地址的拆分逻辑。
附上你的格式化代码供参考:
#include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> // O_RDONLY #include <string.h> // NULL is also defined in string.h, stdlib.h #include<stdlib.h> #include<stdio.h> #include<unistd.h> #include "inttypes.h" uint64_t vtop(uint64_t vaddr) { FILE *pagemap; uint64_t paddr = 0; unsigned long long int offset = (vaddr / sysconf(_SC_PAGESIZE)) * sizeof(uint64_t); uint64_t e; if ((pagemap = fopen("/proc/self/pagemap", "r"))) { if (lseek(fileno(pagemap), offset, SEEK_SET) == offset) { if (fread(&e, sizeof(uint64_t), 1, pagemap)) { if (e & (1ULL << 63)) { // page present ? paddr = e & ((1ULL << 54) - 1); // pfn mask paddr = paddr * sysconf(_SC_PAGESIZE); // add offset within page paddr = paddr | (vaddr & (sysconf(_SC_PAGESIZE) - 1)); printf(" paddr %lu \n", paddr); } else { printf("page not found\n"); } } } fclose(pagemap); } return paddr; } int main() { int oflag = O_RDONLY; const char *path = "file.json"; const int fd = open(path, oflag); // use stat to find the file size struct stat stat; int ret = fstat(fd, &stat); int mflags = MAP_PRIVATE; // information about handling the mapped data int mprot = PROT_READ|PROT_WRITE; // access permissions to the data being mapped size_t size = stat.st_size; void *addr = mmap(NULL, size, mprot, mflags, fd, 0); printf("virtual addres is %p\n", addr); printf("physical addres is %ld\n", vtop((uint64_t)addr)); return 0; }
内容的提问来源于stack exchange,提问作者BODDU MANOHAR REDDY

