2011-06-08 107 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; 
}