如何在Linux中解码/proc/pid/pagemap条目?

18

我正在尝试理解如何使用/proc/pid/pagemap获取给定页的物理地址。假设从/proc/pid/maps中,我得到了虚拟地址afa2d000-afa42000,它对应于堆。我的问题是如何使用这些信息遍历pagemap文件并找到与地址afa2d000-afa42000相对应的物理页面帧。

/proc/pid/pagemap条目以二进制格式存储。是否有任何工具可用来帮助解析此文件?

4个回答

10

Linux内核文档

Linux内核文档描述了格式:https://github.com/torvalds/linux/blob/v4.9/Documentation/vm/pagemap.txt

* 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

在更新的Linux中,文档文件已经移动到:https://github.com/torvalds/linux/blob/v6.2/Documentation/admin-guide/mm/pagemap.rst(感谢评论中的matvore)。 C解析器函数 GitHub上游
#define _XOPEN_SOURCE 700
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t  */
#include <stdlib.h> /* size_t */
#include <unistd.h> /* pread, sysconf */

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, ((uint8_t*)&data) + nread, sizeof(data) - nread,
                (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;
}

使用它的示例可运行程序:


1
Linux Git仓库对于最近的内核版本有不同的文档路径:https://github.com/torvalds/linux/blob/v6.2/Documentation/admin-guide/mm/pagemap.rst - matvore

5

2
尝试这个: http://www.eqware.net/Articles/CapturingProcessMemoryUsageUnderLinux/ 它可以为您解析页面映射。例如,如果您感兴趣的虚拟地址在堆中,其地址为0x055468: = 0004c000-0005a000 rw-p 00000000 00:00 0 [heap] : 86000000000FD6D6 : 0600000000000000
: 0600000000000000
: 86000000000FE921
: 86000000000FE922
: 0600000000000000
: 86000000000FD5AD
: 86000000000FD6D4
: 86000000000FD5F8
: 86000000000FD5FA =>第9页

假设页面大小为4KB, (0x055468 - 0x4c000) mod 4K = 9, 因此您的页面是第9个页面框对应的页框号为:86000000000FD5FA 所以物理pfn为0xFD5FA000(取最后55位并乘以页面大小) 加上偏移量:(0x055468 - 0x4c000 - 9*4K)= 0x468 ==> 物理地址为0xFD5FA000 + 0x468 = 0xFD5FA468。

除了缺少#include <algorithm>之外,您是否成功编译了page-analyse.cpp?我遇到了这个编译器错误:page-analyze.cpp: 在函数‘void make_short_name(char*, const char*)’中: page-analyze.cpp:135:35: 错误:将只读位置‘* strchr(b, 93)’赋值 make: *** [page-analyze] Error 1 - JohnnyFromBF
抱歉,除了链接之外,答案本身应该有一定的意义,但我认为它写得非常糟糕。需要进行格式化,数据来源不清晰/未展示...还有,“mod”需要改成“div”。 - nhed
网络存档:https://web.archive.org/web/20181009215628/http://www.eqware.net/archive/CapturingProcessMemoryUsageUnderLinux/ - Roi

0

如果有人想用Rust来实现,我已经添加了一个Rust实现,这样你就可以轻松地浏览/proc/$pid/maps/proc/$pid/pagemaphttps://crates.io/crates/vm-info


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接