fd file descriptor to an open /proc/pid/pagemap file
* @param[in] vaddr virtual address to get entry for
* @return 0 for success, 1 for failure
*/
int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
{
size_t nread;
ssize_t ret;
uint64_t data;
nread = 0;
while (nread < sizeof(data)) {
ret = pread(pagemap_fd, &data, sizeof(data),
(vaddr / sysconf(_SC_PAGE_SIZE)) * sizeof(data) + nread);
nread += ret;
if (ret <= 0) {
return 1;
}
}
entry->pfn = data & (((uint64_t)1 << 54) - 1);
entry->soft_dirty = (data >> 54) & 1;
entry->file_page = (data >> 61) & 1;
entry->swapped = (data >> 62) & 1;
entry->present = (data >> 63) & 1;
return 0;
}
/* Convert the given virtual address to physical using /proc/PID/pagemap.
*
* @param[out] paddr physical address
* @param[in] pid process to convert for
* @param[in] vaddr virtual address to get entry for
* @return 0 for success, 1 for failure
*/
int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
{
char pagemap_file[BUFSIZ];
int pagemap_fd;
snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
pagemap_fd = open(pagemap_file, O_RDONLY);
if (pagemap_fd < 0) {
return 1;
}
PagemapEntry entry;
if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
return 1;
}
close(pagemap_fd);
*paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
return 0;
}
int main(int argc, char ** argv){
char *end;
int pid;
uintptr_t virt_addr;
uintptr_t paddr;
int return_code;
pid = strtol(argv[1],&end, 10);
virt_addr = strtol(argv[2], NULL, 16);
return_code = virt_to_phys_user(&paddr, pid, virt_addr);
if(return_code == 0)
printf("Vaddr: 0x%lx, paddr: 0x%lx \n", virt_addr, paddr);
else
printf("error\n");
}
另外,收集到一些可以读取pagemap信息的工具:
检查两个jvm进程是否映射modules
的物理地址一致
- 先启动两个jshell
$ jps
25105 jdk.internal.jshell.tool.JShellToolProvider
25142 jdk.internal.jshell.tool.JShellToolProvider
- 把上面转换地址的代码保存为
mymap.c
,再编绎 gcc mymap.c -o mymap
- 获取两个jvm的modules的虚拟地址,并转换为物理地址、、
$ pmap -x 25105 | grep modules
00007f82b4b43000 185076 9880 0 r--s- modules
$ sudo ./mymap 25105 00007f82b4b43000
Vaddr: 0x7f82b4b43000, paddr: 0x33598000
$ pmap -x 25142 | grep modules
00007ff220504000 185076 10064 0 r--s- modules
$ sudo ./mymap 25142 00007ff220504000
Vaddr: 0x7ff220504000, paddr: 0x33598000
可以看到两个jvm进程映射modules
的物理地址是一样的,证实了最开始的想法。
kernel 里的 page-types 工具
其实在kernel里自带有一个工具page-types
可以输出一个page信息,可以通过下面的方式来获取内核源码,然后自己编绎:
sudo apt-get source linux-image-$(uname -r)
sudo apt-get build-dep linux-image-$(uname -r)
到tools/vm
目录下面,可以直接sudo make
编绎。
sudo ./page-types -p 25105
flags page-count MB symbolic-flags long-symbolic-flags
0x0000000000000000 2 0 ____________________________________
0x0000000000400000 14819 57 ______________________t_____________ thp
0x0000000000000800 1 0 ___________M________________________ mmap
0x0000000000000828 33 0 ___U_l_____M________________________ uptodate,lru,mmap
0x000000000000086c 663 2 __RU_lA____M________________________ referenced,uptodate,lru,active,mmap
0x000000000000087c 2 0 __RUDlA