内核文档: 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. linux usb枚举过程分析之守护进程及其唤醒【转】

    转自:http://blog.csdn.net/xuelin273/article/details/38646765 usb热插拔,即usb设备可以实现即插即用,像U盘一样,插到电脑里就可以用,不用时 ...

  2. linux串口驱动分析【转】

    转自:http://blog.csdn.net/hanmengaidudu/article/details/11946591 硬件资源及描述 s3c2440A 通用异步接收器和发送器(UART)提供了 ...

  3. 秘钥登录服务器执行shell脚本

    做自动化的时候,有时候避免不了要和服务器有互动,刚巧碰上一个项目,需要执行命令才能完成本次测试. 昨天遇到的是秘钥形式的,只有秘钥和用户名,百度找了许久也没有思路,(能账号密码登录服务器的还简单些), ...

  4. wpf XAML 设计器异常,提示NullReferenceException 未将对象引用设置到对象例

    设计了一个控件,然后在使用该控件的界面上,出现上图,这个应该是设计器的bug,解决办法 不要在界面上直接写Load事件 在cs构造函数里手动注册,并且在控件的构造函数里增加判断 if (Designe ...

  5. 基于Prometheus的Pushgateway实战

    一.Pushgateway 简介 Pushgateway 是 Prometheus 生态中一个重要工具,使用它的原因主要是: Prometheus 采用 pull 模式,可能由于不在一个子网或者防火墙 ...

  6. IntelliJ IDEA :Error:(1, 1) java: 非法字符: '\ufeff'

    将file encodings由utf-8改成utf-16,再将utf-16改成utf-8就好了

  7. Codeforces Round #189 (Div. 1) C - Kalila and Dimna in the Logging Industry 斜率优化dp

    C - Kalila and Dimna in the Logging Industry 很容易能得到状态转移方程 dp[ i ] = min( dp[ j ] + b[ j ] * a[ i ] ) ...

  8. 024 Yarn核心生态系统

    1.Yarn核心生态系统 2.Tez Tez是Apache最新的支持DAG作业的开源计算框架,它可以将多个有依赖的作业转换为一个作业从而大幅提升DAG作业的性能. 3.solo 全文检索 也可以部署在 ...

  9. Vue之双向绑定原理动手记

    Vue.js的核心功能有两个:一是响应式的数据绑定系统,二是组件系统.本文是通过学习他人的文章,从而理解了双向绑定原理,从而在自己理解的基础上,自己动手实现数据的双向绑定. 目前几种主流的mvc(vm ...

  10. Mac idea 快捷键

    Mac键盘符号和修饰键说明 ⌘ Command⇧ Shift⌥ Option⌃ Control↩︎ Return/Enter⌫ Delete⌦ 向前删除键(Fn+Delete)↑ 上箭头↓ 下箭头← ...