/ proc / / pagemaps和/ proc / / maps | Linux的

我试图让我的头在标题中提到的两个文件。 我查了一下这些东西。 然而,我不明白如何从他们中提取有用的信息(或者我只是以错误的方式接近它)。

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

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

我想问题是,我应该如何有效地使用页面地图来获得/ proc / [pid] / maps给出的地址的等效物理地址

公平的说,我发表了一个类似的问题,但是几天前这个方法有些不同。

如果有人能够对这个问题有所了解,我会非常感激。

===编辑===

要解决下面的注释:我正在从/ proc / [pid] / maps中读取一行,并且行如下所示:

00400000-00401000 r-xp 00000000 08:01 8915461 / home / janjust / my_programs / shared_mem 7ffffef1b000-7ffffef3c000 rw-p 00000000 00:00 0 [stack]

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

输出如下所示:

00400000-00401000 r-xp 00000000 08:01 8915461 / home / 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); 

然而,尽pipe我认为我得到了正确的输出,但是索引似乎或者是types不匹配或者其他事情正在发生:例如,对于地图中的[共享内存]行,输出提供了错误的索引; 但我仍然能够扫描二进制文件,并获得物理页面地址。

该输出的例子如下:

 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位的操作系统。

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

缺少头文件: – / sigh修正了比较off64_t和unsigned long的问题。

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

这是一个将虚拟地址转换为物理地址的页面地图示例: 是否有任何API用于从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; } 

使用page-types.c作为你正在寻找的东西的指南,它解析pagemap和maps的内容: https ://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable 。 GIT中/纯/文档/ VM /页面types.cΔH= Linux的2.6.32.y