内核文档: Documentation/vm/pagemap.txt

pagemap is a new (as of 2.6.25) set of interfaces in the kernel that allow
userspace programs to examine the page tables and related information by
reading files in /proc.

There are four components to pagemap:

* /proc/pid/pagemap.  This file lets a userspace process find out which
   physical frame each virtual page is mapped to.  It contains one 64-bit
   value for each virtual page, containing the following data (from
   fs/proc/task_mmu.c, above pagemap_read):

* Bits 0-54  page frame number (PFN) if present
    * Bits 0-4   swap type if swapped
    * Bits 5-54  swap offset if swapped
    * Bit  55    pte is soft-dirty (see Documentation/vm/soft-dirty.txt)
    * Bit  56    page exclusively mapped (since 4.2)
    * Bits 57-60 zero
    * Bit  61    page is file-page or shared-anon (since 3.5)
    * Bit  62    page swapped
    * Bit  63    page present

Since Linux 4.0 only users with the CAP_SYS_ADMIN capability can get PFNs.
   In 4.0 and 4.1 opens by unprivileged fail with -EPERM.  Starting from
   4.2 the PFN field is zeroed if the user does not have CAP_SYS_ADMIN.
   Reason: information about PFNs helps in exploiting Rowhammer vulnerability.

If the page is not present but in swap, then the PFN contains an
   encoding of the swap file number and the page's offset into the
   swap. Unmapped pages return a null PFN. This allows determining
   precisely which pages are mapped (or in swap) and comparing mapped
   pages between processes.

Efficient users of this interface will use /proc/pid/maps to
   determine which areas of memory are actually mapped and llseek to
   skip over unmapped regions.

下面是一个工具:

 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <string.h> #define PAGEMAP_ENTRY 8
#define GET_BIT(X,Y) (X & ((uint64_t)1<<Y)) >> Y
#define GET_PFN(X) X & 0x7FFFFFFFFFFFFF const int __endian_bit = ;
#define is_bigendian() ( (*(char*)&__endian_bit) == 0 ) int i, c, pid, status;
unsigned long virt_addr;
uint64_t read_val, file_offset, page_size;
char path_buf [0x100] = {};
FILE * f;
char *end; int read_pagemap(char * path_buf, unsigned long virt_addr); int main(int argc, char ** argv){
if(argc!=){
printf("Argument number is not correct!\n pagemap PID VIRTUAL_ADDRESS\n");
return -;
}
if(!memcmp(argv[],"self",sizeof("self"))){
sprintf(path_buf, "/proc/self/pagemap");
pid = -;
}
else{
pid = strtol(argv[],&end, );
if (end == argv[] || *end != '\0' || pid<=){
printf("PID must be a positive number or 'self'\n");
return -;
}
}
virt_addr = strtoll(argv[], NULL, );
if(pid!=-)
sprintf(path_buf, "/proc/%u/pagemap", pid); page_size = getpagesize();
read_pagemap(path_buf, virt_addr);
return ;
} int read_pagemap(char * path_buf, unsigned long virt_addr){
printf("Big endian? %d\n", is_bigendian());
f = fopen(path_buf, "rb");
if(!f){
printf("Error! Cannot open %s\n", path_buf);
return -;
} //Shifting by virt-addr-offset number of bytes
//and multiplying by the size of an address (the size of an entry in pagemap file)
file_offset = virt_addr / page_size * PAGEMAP_ENTRY;
printf("Vaddr: 0x%lx, Page_size: %lld, Entry_size: %d\n", virt_addr, page_size, PAGEMAP_ENTRY);
printf("Reading %s at 0x%llx\n", path_buf, (unsigned long long) file_offset);
status = fseek(f, file_offset, SEEK_SET);
if(status){
perror("Failed to do fseek!");
return -;
}
errno = ;
read_val = ;
unsigned char c_buf[PAGEMAP_ENTRY];
for(i=; i < PAGEMAP_ENTRY; i++){
c = getc(f);
if(c==EOF){
printf("\nReached end of the file\n");
return ;
}
if(is_bigendian())
c_buf[i] = c;
else
c_buf[PAGEMAP_ENTRY - i - ] = c;
printf("[%d]0x%x ", i, c);
}
for(i=; i < PAGEMAP_ENTRY; i++){
//printf("%d ",c_buf[i]);
read_val = (read_val << ) + c_buf[i];
}
printf("\n");
printf("Result: 0x%llx\n", (unsigned long long) read_val);
if(GET_BIT(read_val, )) {
uint64_t pfn = GET_PFN(read_val);
printf("PFN: 0x%llx (0x%llx)\n", pfn, pfn * page_size + virt_addr % page_size);
} else
printf("Page not present\n");
if(GET_BIT(read_val, ))
printf("Page swapped\n");
fclose(f);
return ;
}

测试:

用Qemu+vexpress-ca9:

内存: 1GB, 物理地址范围: 0x60000000->0x9FFFFFFF

通过查看/proc/pid/maps获得进程的地址空间的内存映射情况:

 [root@vexpress ~]# cat /proc//maps
-001f3000 r-xp b3: /bin/busybox
001fa000-001fc000 rw-p 001ea000 b3: /bin/busybox
001fc000- rw-p : [heap]
b6c7f000-b6c80000 rw-p :
b6c80000-b6c8d000 r-xp b3: /lib/libnss_files-2.18.so
b6c8d000-b6c94000 ---p 0000d000 b3: /lib/libnss_files-2.18.so
b6c94000-b6c95000 r--p 0000c000 b3: /lib/libnss_files-2.18.so
b6c95000-b6c96000 rw-p 0000d000 b3: /lib/libnss_files-2.18.so
b6c96000-b6ca1000 r-xp b3: /lib/libnss_nis-2.18.so
b6ca1000-b6ca8000 ---p 0000b000 b3: /lib/libnss_nis-2.18.so
b6ca8000-b6ca9000 r--p 0000a000 b3: /lib/libnss_nis-2.18.so
b6ca9000-b6caa000 rw-p 0000b000 b3: /lib/libnss_nis-2.18.so
b6caa000-b6daa000 rw-p :
b6daa000-b6dca000 r-xp b3: /lib/ld-2.18.so
b6dca000-b6dd1000 ---p b3: /lib/ld-2.18.so
b6dd1000-b6dd2000 r--p 0001f000 b3: /lib/ld-2.18.so
b6dd2000-b6dd3000 rw-p b3: /lib/ld-2.18.so
b6dd3000-b6f06000 r-xp b3: /lib/libc-2.18.so
b6f06000-b6f0d000 ---p b3: /lib/libc-2.18.so
b6f0d000-b6f0f000 r--p b3: /lib/libc-2.18.so
b6f0f000-b6f10000 rw-p b3: /lib/libc-2.18.so
b6f10000-b6f13000 rw-p :
b6f13000-b6f26000 r-xp b3: /lib/libnsl-2.18.so
b6f26000-b6f2d000 ---p b3: /lib/libnsl-2.18.so
b6f2d000-b6f2e000 r--p b3: /lib/libnsl-2.18.so
b6f2e000-b6f2f000 rw-p b3: /lib/libnsl-2.18.so
b6f2f000-b6f31000 rw-p :
b6f31000-b6f39000 r-xp b3: /lib/libnss_compat-2.18.so
b6f39000-b6f40000 ---p b3: /lib/libnss_compat-2.18.so
b6f40000-b6f41000 r--p b3: /lib/libnss_compat-2.18.so
b6f41000-b6f42000 rw-p b3: /lib/libnss_compat-2.18.so
be958000-be979000 rw-p : [stack]
bed04000-bed05000 r-xp : [sigpage]
bed05000-bed06000 r--p : [vvar]
bed06000-bed07000 r-xp : [vdso]
ffff0000-ffff1000 r-xp : [vectors]

可以看看0x8000这个虚拟地址对应的物理地址:

 [root@vexpress ~]# ./translate  0x8000
Big endian?
Vaddr: 0x8000, Page_size: , Entry_size:
Reading /proc//pagemap at 0x40
[]0x0 []0xf8 []0x9 []0x0 []0x0 []0x0 []0x0 []0xa0
Result: 0xa00000000009f800
PFN: 0x9f800 (0x9f800000)

可以看到, 对应的物理页帧是0x9F800,那么物理地址就是0x9F800000.

下面我们再做一个实验, 进程746的地址空间有一部分用来映射libc:

 b6dd3000-b6f06000 r-xp  b3:         /lib/libc-2.18.so
b6f06000-b6f0d000 ---p b3: /lib/libc-2.18.so
b6f0d000-b6f0f000 r--p b3: /lib/libc-2.18.so
b6f0f000-b6f10000 rw-p b3: /lib/libc-2.18.so

此外, 进程835也会用到libc:

 [root@vexpress ~]# cat /proc//maps
... ...
b6e0b000-b6f3e000 r-xp b3: /lib/libc-2.18.so
b6f3e000-b6f45000 ---p b3: /lib/libc-2.18.so
b6f45000-b6f47000 r--p b3: /lib/libc-2.18.so
b6f47000-b6f48000 rw-p b3: /lib/libc-2.18.so
... ...

可以看到, 进程746和835虽然都用了libc,但是对应的虚拟地址却不同,前者是0xb6dd3000, 而后者是0xb6e0b000, 我们知道对于共享库, 在内存只会存在一份代码, 那么物理地址也就是唯一的(代码段是唯一的,所有调用libc的进程共享,而数据段每个进程一个), 那么进程746的虚拟地址空间的0xb6dd3000(代码段)跟进程835的虚拟地址空间的0xb6e0b000(代码段)对应的物理地址应该是同一个, 下面验证一下:

进程746:

 [root@vexpress ~]# ./translate  0xb6dd3000
virt_addr: 0xb6dd3000
Big endian?
Vaddr: 0xb6dd3000, Page_size: , Entry_size:
Reading /proc//pagemap at 0x5b6e98
[]0x68 []0xfa []0x9 []0x0 []0x0 []0x0 []0x0 []0xa0
Result: 0xa00000000009fa68
PFN: 0x9fa68 (0x9fa68000)

可以看到,物理地址是0x9FA68000

进程835:

 [root@vexpress ~]# ./translate  0xb6e0b000
virt_addr: 0xb6e0b000
Big endian?
Vaddr: 0xb6e0b000, Page_size: , Entry_size:
Reading /proc//pagemap at 0x5b7058
[]0x68 []0xfa []0x9 []0x0 []0x0 []0x0 []0x0 []0xa0
Result: 0xa00000000009fa68
PFN: 0x9fa68 (0x9fa68000)

可以看到, 物理地址也是0x9FA68000, 从而证明了我们的猜想。

完。

利用/proc/pid/pagemap将虚拟地址转换为物理地址的更多相关文章

  1. How to translate virtual to physical addresses through /proc/pid/pagemap

    墙外通道:http://fivelinesofcode.blogspot.com/2014/03/how-to-translate-virtual-to-physical.html I current ...

  2. 浅析Linux 64位系统虚拟地址和物理地址的映射及验证方法

    虚拟内存 先简单介绍一下操作系统中为什么会有虚拟地址和物理地址的区别.因为Linux中有进程的概念,那么每个进程都有自己的独立的地址空间. 现在的操作系统都是64bit的,也就是说如果在用户态的进程中 ...

  3. cpu为什么使用虚拟地址到物理地址的空间映射,解决了什么样的问题?

    当处理器读或写入内存位置时,它会使用虚拟地址.作为读或写操作的一部分,处理器将虚拟地址转换为物理地址.通过虚拟地址访问内存有以下优势: 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓 ...

  4. stuff in /proc/PID/

    Table of Contents 1. /proc/PID/cwd 2. /proc/PID/clear_refs 3. /proc/PID/coredump_filter 4. /proc/PID ...

  5. Page (computer memory) Memory segmentation Page table 虚拟地址到物理地址的转换

    A page, memory page, or virtual page is a fixed-length contiguous block of virtual memory, described ...

  6. X86在逻辑地址、线性地址、理解虚拟地址和物理地址

    参考:http://bbs.chinaunix.net/thread-2083672-1-1.html 本贴涉及的硬件平台是X86.假设是其他平台,不保证能一一对号入座.可是举一反三,我想是全然可行的 ...

  7. linux /proc/pid进程信息说明

    转:http://hi.baidu.com/sei_zhouyu/item/3ab5bc9fb2ea29c3b6253140 /proc/pid/是进程目录,存放的是当前运行进程的信息. 譬如apac ...

  8. [置顶] Linux 虚拟地址与物理地址的映射关系分析【转】

    转自:http://blog.csdn.net/ordeder/article/details/41630945 版权声明:本文为博主(http://blog.csdn.net/ordeder)原创文 ...

  9. Linux 虚拟地址与物理地址的映射关系分析【转】

    转自:http://blog.csdn.net/ordeder/article/details/41630945 版权声明:本文为博主(http://blog.csdn.net/ordeder)原创文 ...

随机推荐

  1. ARMCC和GCC编译ARM代码的软浮点和硬浮点问题【转】

    转自:https://blog.csdn.net/hunanchenxingyu/article/details/47003279 本文介绍了ARM代码编译时的软浮点(soft-float)和硬浮点( ...

  2. 公共语言运行库(CLR)开发系列课程(3):COM Interop基础 学习笔记

    上章地址 什么是COM Component Object Model 组建对象模型 基于接口(Interface) 接口=协议 IID 标识接口 V-table 虚表 方式调用 单继承 对象(Obje ...

  3. 使用eclipse构建Maven项目及发布一个Maven项目

    开发环境: Eclipse Jee Mars(截止2015年12月1日目前的最新版eclipse4.5),下载地址:http://www.eclipse.org/downloads/ 因为此版本已经集 ...

  4. export,import ,export default是什么

    首先要知道export,import ,export default是什么 ES6模块主要有两个功能:export和importexport用于对外输出本模块(一个文件可以理解为一个模块)变量的接口i ...

  5. HTML5+ App开发入门

    HTML5 Plus应用概述 HTML5 Plus移动App,简称5+App,是一种基于HTML.JS.CSS编写的运行于手机端的App,这种App可以通过扩展的JS API任意调用手机的原生能力,实 ...

  6. Spring对象依赖关系处理

    Spring中给对象属性赋值 1.通过set方法给属性注入值 2.p名称空间 3.自动装配 4.注解 编写MVCModel调用userAction MVCModel public class MVCM ...

  7. Kotlin中var和val的区别

    Kotlin中有两个关键字定义变量,这两个关键字外形看着差别很小就只差了一个字母,但实际差别很大的. var是一个可变变量,这是一个可以通过重新分配来更改为另一个值的变量.这种声明变量的方式和Java ...

  8. 【LOJ】#2533. 「CQOI2018」交错序列

    题解 有毒吧 这题\(O(n)\)过不去 非得写\(O((a + b)^3\log n)\)的矩乘,同样很卡常 把\(x\)换成\(n - y\) 我们拆完式子发现是这样的 \(\sum_{i = 0 ...

  9. 【LOJ】#2182. 「SDOI2015」寻宝游戏

    题解 终于了解怎么动态维护虚树了 就是把点按照dfs序排个序啊 这道题显然是求虚树上所有边长的两倍 我们把dfs序排完序,相邻两个点加上路径长(包括首尾),删除的时候删一个点减去它到两边再加上新近相邻 ...

  10. supervisor Error: Another program is already listening

    Error: Another program is already listening on a port that one of our HTTP servers is configured to ...