2011-06-08 130 views
12

我试图让我的头在标题中提到的两个文件。 我已经查过这些位是什么;然而,我不明白如何从中提取有用的信息(或者我只是以错误的方式接近它)。/proc/[pid]/pagemaps和/ proc/[pid]/maps | linux

让我解释一下:pagemaps是一个相当新的“特征”伪文件,它包含分配给当前[pid]的虚拟页面的物理帧信息。也就是说,给定一个从地址x开始的虚拟页面,对虚拟地址开始说'vas',我可以使用vas索引页面映射文件以获得映射物理页面帧的64位。这些位包含有关该虚拟页面的信息。 但是,当我提取这些位并做一些移位时,我正在看到我所迷失的东西。

位表示如下:0-54是页面帧号,55-60是页面移位,第63位是当前位,还有其他一些我感兴趣的位。 在使用/ proc/[pid]/maps中的地址进行映射之后,似乎几乎每个进程的页面都会被交换,即第63位总是零。 :(

我想这个问题是,我应该如何去有效利用pagemaps得到给出的地址相当于物理地址的/ proc/[PID] /映射

为了公平起见,我VE发布了类似的问题,但这种方法却有点不同早几天。

如果任何人都可以在这个问题上我是非常赞赏一些启发。

===编辑===

为了解决下面的评论: 我读线从/ proc/[PID] /地图和线条看起来像:

00400000-00401000 R-XP 00000000 08:01 8915461 /家/ janjust/my_programs/shared_mem 7ffffef1b000-7ffffef3c000 RW-p 00000000 00:00 0 [堆]

然后我提取倒是虚拟页的数量和索引的二进制文件/ proc/[PID]/pagemaps ,并且可以为每个虚拟页面提取分配给它的物理页面。

输出看起来像:

00400000-00401000 R-XP 00000000 08:01 8915461 /家庭/ janjust/my_programs/shared_mem NUM_PAGES:1 :86000000001464C6

一个物理地址用于虚拟范围中的每个虚拟页面。

读取线和提取的物理地址的代码是:

74  /* process /proc/pid/maps, by line*/ 
75  while(fgets(line, 256, in_map) != NULL){ 
76   unsigned long vas; 
77   unsigned long vae; 
78   int num_pages; 
79 
80   //print line 
81   printf("%s", line); 
82 
83   /*scan for the virtual addresses*/ 
84   n = sscanf(line, "%lX-%lX", &vas, &vae); 
85   if(n != 2){ 
86    printf("Involid line read from %s\n",maps); 
87    continue; 
88   } 
89 
90   num_pages = (vae - vas)/PAGE_SIZE; 
91   printf("num_pages: %d\n", num_pages); 
92 
93   if(num_pages > 0){ 
94    long index = (vas/PAGE_SIZE) * sizeof(unsigned long long); 
95    off64_t o; 
96    ssize_t t; 
97 
98    /* seek to index in pagemaps */ 
99    o = lseek64(pm, index, SEEK_SET); 
100    if (o != index){ 
101     printf("Error seeking to o:%ld, index:%ld.\n", o, index); 
102    } 
103 
104    /* map the virtual to physical page */ 
105    while(num_pages > 0){ 
106     unsigned long long pa; 
107 
108     /* Read a 64-bit word from each pagemap file... */ 
109     t = read(pm, &pa, sizeof(unsigned long long)); 
110     if(t < 0){ 
111      printf("Error reading file \"%s\" \n", page_map); 
112      goto next_line; 
113     } 
114     printf(": %016llX\n", pa); 

不过,虽然我觉得我得到了正确的输出,该指数似乎是两种类型不匹配或别的什么东西继续: 输出例如为地图上的[shared mem]行给出错误的索引;但我仍然能够扫描二进制文件并获取物理页面地址。

该输出的例子如下:

969 7f7f08d58000-7f7f08d59000 rw-s 00000000 00:04 0 /SYSV00003039 (deleted) 
970 num_pages: 1 
971 Error seeking to o:-1081840960, index:273796065984. 
972 : 8600000000148267 

好了,现在,我最后应该说,这是一个64位操作系统下,这个问题不会在32位操作系统坚持。

+1

也许你应该张贴您的代码或至少一些伪代码?或者只是一个解释,我在/ proc/pid/maps中看到了这一点,所以我在/ proc/pid/pagemap中查找了这个8字节... – Nemo 2011-06-08 20:40:52

+0

谢谢,我按照您的建议扩展了我的问题。 – janjust 2011-06-10 13:38:14

+0

另外,是否有更简单的方法来将代码添加到问题中?不得不将每行分隔4个空格有点费时: -/ – janjust 2011-06-10 13:38:47

回答

3

Oooh K,索引是正确的,但比较off64_t o(8字节)与长索引解释o错误,因此我为什么得到这个错误。哈哈!这是一个愚蠢的错误。 因此,添加适当的标题照顾了这一点。

缺失标头: -/叹息解决了比较off64_t和unsigned long的问题。

+0

接受该答案。 – cubuspl42 2013-05-01 17:47:00

2

/proc/<pid>/pagemap + /proc/<pid>/maps转储示例程序

这里一个pagemap示例将虚拟地址转换为物理地址:Is there any API for determining the physical address from virtual address in Linux

以下程序同时使用了/proc/<pid>/pagemap + /proc/<pid>/maps转储页面表信息以显示它们如何一起使用。用法:

sudo ./pagemap_dump.out <pid> 

输出示例:

addr pfn soft-dirty file/shared swapped present library 
400000 12845d 0 1 0 1 /bin/bash 
401000 12845e 0 1 0 1 /bin/bash 
402000 12845f 0 1 0 1 /bin/bash 

这告诉我们,例如虚拟地址映射0x400000到物理地址 0x12845d000

为什么sudo是必需的:https://unix.stackexchange.com/questions/345915/how-to-change-permission-of-proc-self-pagemap-file/383838#383838

这项计划分两步工作:

  • /proc/<pid>/maps解析人类可读的行线。该文件包含表单行:

    7ffff7b6d000-7ffff7bdd000 r-xp 00000000 fe:00 658      /lib/libuClibc-1.0.22.so 
    

    这给了我们:

    • 7f8af99f8000-7f8af99ff000:属于过程中,可能包含多个页面的虚拟地址范围。
    • /lib/libuClibc-1.0.22.so拥有该内存的库的名称。
  • 循环每个地址范围内的每一页,并要求/proc/<pid>/pagemap有关该页面的详细信息,包括物理地址。

pagemap_dump.c

#define _XOPEN_SOURCE 700 
#include <errno.h> 
#include <fcntl.h> 
#include <stdint.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <unistd.h> 

typedef struct { 
    uint64_t pfn : 54; 
    unsigned int soft_dirty : 1; 
    unsigned int file_page : 1; 
    unsigned int swapped : 1; 
    unsigned int present : 1; 
} PagemapEntry; 

/* Parse the pagemap entry for the given virtual address. 
* 
* @param[out] entry  the parsed entry 
* @param[in] pagemap_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 buffer[BUFSIZ]; 
    char maps_file[BUFSIZ]; 
    char pagemap_file[BUFSIZ]; 
    int maps_fd; 
    int offset = 0; 
    int pagemap_fd; 
    pid_t pid; 

    if (argc < 2) { 
     printf("Usage: %s pid\n", argv[0]); 
     return EXIT_FAILURE; 
    } 
    pid = strtoull(argv[1], NULL, 0); 
    snprintf(maps_file, sizeof(maps_file), "/proc/%ju/maps", (uintmax_t)pid); 
    snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid); 
    maps_fd = open(maps_file, O_RDONLY); 
    if (maps_fd < 0) { 
     perror("open maps"); 
     return EXIT_FAILURE; 
    } 
    pagemap_fd = open(pagemap_file, O_RDONLY); 
    if (pagemap_fd < 0) { 
     perror("open pagemap"); 
     return EXIT_FAILURE; 
    } 
    printf("addr pfn soft-dirty file/shared swapped present library\n"); 
    for (;;) { 
     ssize_t length = read(maps_fd, buffer + offset, sizeof buffer - offset); 
     if (length <= 0) break; 
     length += offset; 
     for (size_t i = offset; i < (size_t)length; i++) { 
      uintptr_t low = 0, high = 0; 
      if (buffer[i] == '\n' && i) { 
       const char *lib_name; 
       size_t y; 
       /* Parse a line from maps. Each line contains a range that contains many pages. */ 
       { 
        size_t x = i - 1; 
        while (x && buffer[x] != '\n') x--; 
        if (buffer[x] == '\n') x++; 
        while (buffer[x] != '-' && x < sizeof buffer) { 
         char c = buffer[x++]; 
         low *= 16; 
         if (c >= '0' && c <= '9') { 
          low += c - '0'; 
         } else if (c >= 'a' && c <= 'f') { 
          low += c - 'a' + 10; 
         } else { 
          break; 
         } 
        } 
        while (buffer[x] != '-' && x < sizeof buffer) x++; 
        if (buffer[x] == '-') x++; 
        while (buffer[x] != ' ' && x < sizeof buffer) { 
         char c = buffer[x++]; 
         high *= 16; 
         if (c >= '0' && c <= '9') { 
          high += c - '0'; 
         } else if (c >= 'a' && c <= 'f') { 
          high += c - 'a' + 10; 
         } else { 
          break; 
         } 
        } 
        lib_name = 0; 
        for (int field = 0; field < 4; field++) { 
         x++; 
         while(buffer[x] != ' ' && x < sizeof buffer) x++; 
        } 
        while (buffer[x] == ' ' && x < sizeof buffer) x++; 
        y = x; 
        while (buffer[y] != '\n' && y < sizeof buffer) y++; 
        buffer[y] = 0; 
        lib_name = buffer + x; 
       } 
       /* Get info about all pages in this page range with pagemap. */ 
       { 
        PagemapEntry entry; 
        for (uintptr_t addr = low; addr < high; addr += sysconf(_SC_PAGE_SIZE)) { 
         /* TODO always fails for the last page (vsyscall), why? pread returns 0. */ 
         if (!pagemap_get_entry(&entry, pagemap_fd, addr)) { 
          printf("%jx %jx %u %u %u %u %s\n", 
           (uintmax_t)addr, 
           (uintmax_t)entry.pfn, 
           entry.soft_dirty, 
           entry.file_page, 
           entry.swapped, 
           entry.present, 
           lib_name 
          ); 
         } 
        } 
       } 
       buffer[y] = '\n'; 
      } 
     } 
    } 
    close(maps_fd); 
    close(pagemap_fd); 
    return EXIT_SUCCESS; 
}