Linux用户态进程虚拟地址转物理页帧

文章讨论了Linux内核如何通过/proc/pid/pagemap接口让用户空间程序查询虚拟页面对应的物理帧信息。它介绍了pagemap接口的功能,包括如何获取虚拟地址的物理页帧号、页状态等,并提供了一个C程序示例来读取和解析pagemap文件。
摘要由CSDN通过智能技术生成

我们可以看到的这些地址都被称为虚拟地址,为什么叫虚拟地址?因为这个地址不是真正表征内存条上的内存单元。

# 内核空间地址
curtis@curtis-PC:~/work$ sudo cat /proc/kallsyms | grep vfs_read
ffffffffa8539c80 t vfs_readv
ffffffffa853b8e0 T vfs_read
ffffffffa8549890 T vfs_readlink
ffffffffa9913180 r __ksymtab_vfs_readlink
ffffffffa992e536 r __kstrtabns_vfs_readlink
ffffffffa9931e78 r __kstrtab_vfs_readlink
curtis@curtis-PC:~/work$ sudo cat /proc/1/maps 
560cffea4000-560cffedc000 r--p 00000000 08:05 3824652                    /usr/lib/systemd/systemd
560cffedc000-560cfffba000 r-xp 00038000 08:05 3824652                    /usr/lib/systemd/systemd
560cfffba000-560d0001d000 r--p 00116000 08:05 3824652                    /usr/lib/systemd/systemd
560d0001d000-560d00067000 r--p 00178000 08:05 3824652                    /usr/lib/systemd/systemd
560d00067000-560d00068000 rw-p 001c2000 08:05 3824652                    /usr/lib/systemd/systemd
560d01313000-560d015e1000 rw-p 00000000 00:00 0                          [heap]
7f14e8000000-7f14e8021000 rw-p 00000000 00:00 0 
7f14e8021000-7f14ec000000 ---p 00000000 00:00 0 
7f14f0000000-7f14f0021000 rw-p 00000000 00:00 0 
7f14f0021000-7f14f4000000 ---p 00000000 00:00 0 
7f14f55d6000-7f14f55d7000 ---p 00000000 00:00 0 
7f14f55d7000-7f14f5dd7000 rw-p 00000000 00:00 0 
7f14f5dd7000-7f14f5dd8000 ---p 00000000 00:00 0 
7f14f5dd8000-7f14f65df000 rw-p 00000000 00:00 0 
7f14f65df000-7f14f65e3000 r--p 00000000 08:05 3827280                    /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.26.1
7f14f65e3000-7f14f65f6000 r-xp 00004000 08:05 3827280                    /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.26.1
7f14f65f6000-7f14f65ff000 r--p 00017000 08:05 3827280                    /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.26.1
7f14f65ff000-7f14f6600000 ---p 00020000 08:05 3827280                    /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.26.1
7f14f6600000-7f14f6601000 r--p 00020000 08:05 3827280                    /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.26.1
7f14f6601000-7f14f6602000 rw-p 00021000 08:05 3827280                    /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.26.1
7f14f6602000-7f14f6604000 r--p 00000000 08:05 3826684                    /usr/lib/x86_64-linux-gnu/libattr.so.1.1.2448
7f14f6604000-7f14f6607000 r-xp 00002000 08:05 3826684                    /usr/lib/x86_64-linux-gnu/libattr.so.1.1.2448
7f14f6607000-7f14f6608000 r--p 00005000 08:05 3826684                    /usr/lib/x86_64-linux-gnu/libattr.so.1.1.2448
7f14f6608000-7f14f6609000 r--p 00005000 08:05 3826684                    /usr/lib/x86_64-linux-gnu/libattr.so.1.1.2448
7f14f6609000-7f14f660a000 rw-p 00006000 08:05 3826684                    /usr/lib/x86_64-linux-gnu/libattr.so.1.1.2448

Linux 内核版本为6.2 内核文档路径:/Documentation/admin-guide/mm/pagemap.rst

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
      :ref:Documentation/admin-guide/mm/soft-dirty.rst <soft_dirty>)
    • Bit 56 page exclusively mapped (since 4.2)
    • Bit 57 pte is uffd-wp write-protected (since 5.13) (see
      :ref:Documentation/admin-guide/mm/userfaultfd.rst <userfaultfd>)
    • Bits 58-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.

读取虚拟地址对应的物理页帧
vaddr_to_pfn.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>

// 虚拟地址pfn相关信息占用8个字节
#define PAGEMAP_ENTRY 8
// 获取某一个bit的数据
#define GET_BIT(X,Y) (X & ((uint64_t)1<<Y)) >> Y
// 获取X 0-54bit数据
#define GET_PFN(X) X & 0x7FFFFFFFFFFFFF

const int __endian_bit = 1;
#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!=3){
        printf("Argument number is not correct!\n pagemap PID VIRTUAL_ADDRESS\n");
        return -1;
    }
    if(!memcmp(argv[1],"self",sizeof("self"))){
        sprintf(path_buf, "/proc/self/pagemap");
        pid = -1;
    }
    else{
        pid = strtol(argv[1],&end, 10);
        if (end == argv[1] || *end != '\0' || pid<=0){
            printf("PID must be a positive number or 'self'\n");
            return -1;
        }
    }
    virt_addr = strtoll(argv[2], NULL, 16);
    if(pid!=-1)
        sprintf(path_buf, "/proc/%u/pagemap", pid);

    page_size = getpagesize();
    read_pagemap(path_buf, virt_addr);
    return 0;
}

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 -1;
    }

    //Shifting by virt-addr-offset number of bytes
    //and multiplying by the size of an address (the size of an entry in pagemap file)
    // vfn = (virt_addr / page_size) -> 虚拟地址所对应的页帧号
   	// offset = vfsn * 8 -> 页帧相关信息在pagemap文件中的偏移
    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 -1;
    }
    errno = 0;
    read_val = 0;
    unsigned char c_buf[PAGEMAP_ENTRY];
    for(i=0; i < PAGEMAP_ENTRY; i++){
        c = getc(f);
        if(c==EOF){
            printf("\nReached end of the file\n");
            return 0;
        }
        if(is_bigendian())
            c_buf[i] = c;
        else
            c_buf[PAGEMAP_ENTRY - i - 1] = c;
        printf("[%d]0x%x ", i, c);
    }
    for(i=0; i < PAGEMAP_ENTRY; i++){
        //printf("%d ",c_buf[i]);
        read_val = (read_val << 8) + c_buf[i];
    }
    printf("\n");
    printf("Result: 0x%llx\n", (unsigned long long) read_val);
    // 检查该虚拟地址是否被映射
    if(GET_BIT(read_val, 63)) {
        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");
    // 检查该虚拟机地址是否被交换到磁盘swap分区
    if(GET_BIT(read_val, 62))
        printf("Page swapped\n");
    fclose(f);
    return 0;
}

参考连接:https://www.cnblogs.com/pengdonglin137/p/6802108.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值