【问题标题】:Getting physical address from /proc/[pid]/pagemap fails从 /proc/[pid]/pagemap 获取物理地址失败
【发布时间】:2015-08-31 14:31:08
【问题描述】:

我正在尝试使用虚拟地址从 /proc/[pid]/pagemap 获取物理地址,我认为它工作正常,直到我尝试使用一个简单的测试程序。 这是我使用虚拟地址获取物理地址的代码:

#include "addresstranslation.h"
#include <stdio.h>
#define PAGEMAP_ENTRY 8
#define GET_BIT(X,Y) (X & ((uint64_t)1<<Y)) >> Y
#define GET_PFN(X) X & 0x7FFFFFFFFFFFFF
#define page_mapping_file "/proc/self/pagemap"

const int __endian_bit = 1;
#define is_bigendian() ( (*(char*)&__endian_bit) == 0 )


uintptr_t virtual_to_physical_address(uintptr_t virt_addr)
{
   uintptr_t file_offset = 0;
   uintptr_t read_val = 0;
   uintptr_t page_number = 0;
   int i = 0;
   int c = 0;
   int pid = 0;
   int status = 0;
   unsigned char c_buf[PAGEMAP_ENTRY];

   FILE *f = fopen(page_mapping_file, "rb");
   if(!f)
   {
      // if this happens run as root
      printf("Error! Cannot open %s. Please, run as root.\n", page_mapping_file);
      return 0;
   }

   file_offset = virt_addr / getpagesize() * PAGEMAP_ENTRY;

   status = fseek(f, file_offset, SEEK_SET);
   if(status)
   {
      printf("Error! Cannot seek in %s.\n", page_mapping_file);
      perror("Failed to do fseek!");
      fclose(f);
      return 0;
   }

   for(i = 0; i < PAGEMAP_ENTRY; i++)
   {
      c = getc(f);
      if(c == EOF)
      {
         fclose(f);
         return 0;
      }

      if(is_bigendian())
      {
           c_buf[i] = c;
      }
      else
      {
           c_buf[PAGEMAP_ENTRY - i - 1] = c;
      }
   }

   for(i=0; i < PAGEMAP_ENTRY; i++)
   {
      read_val = (read_val << 8) + c_buf[i];
   }

   /*
   if(GET_BIT(read_val, 63))
   {
      page_number = GET_PFN(read_val);
      printf("%d \n", page_number);
   }
   else
   {
     printf("Page not present\n");
   }
   if(GET_BIT(read_val, 62))
   {
      printf("Page swapped\n");
   }
   */
   fclose(f);

   return read_val;
}

地址翻译.h:

/*
 * addresstranslation.h
 *
 *  Translates virtual to physical address.
 */

#ifndef __ADDRESS_TRANSLATION_H
#define __ADDRESS_TRANSLATION_H

#include <inttypes.h>
#include <stdint.h>

uintptr_t virtual_to_physical_address(uintptr_t virt_addr);

#endif

这是我尝试过的简单测试。

#include <stdlib.h>
#include <stdio.h>
#include "addresstranslation.h"

int main(int argc, char* argv[])
{
  int *a1, *a2, *b1,*c1, *b2 = NULL;
   int N = 4096;
   uintptr_t ap1, ap2, bp1, bp2 = 0;

   printf("Test virtual to physical address translation.\n");

   a1 = (int*)malloc(sizeof(int) * N);
   if (!a1)
   {
      printf("Error: cannot allocate memory for a\n");
      return 1;
   }

   b1 = (int*)malloc(sizeof(int) * N);
   if (!b1)
   {
      printf("Error: cannot allocate memory for b\n");
      return 1;
   }

   ap1 = virtual_to_physical_address((uintptr_t)a1);
   bp1 = virtual_to_physical_address((uintptr_t)b1);

   printf("a1_virt= %p: a1_phys= %" PRIxPTR "\n", a1, ap1);
   printf("b1_virt= %p b1_phys= %" PRIxPTR "\n", b1, bp1);
   a2 = a1 + 1000;
   b2 = b1 + 1;
   ap2 = virtual_to_physical_address((uintptr_t)a2);
   bp2 = virtual_to_physical_address((uintptr_t)b2);
   printf("a2_virt= %p a2_phys= %" PRIxPTR "\n", a2, ap2);
   printf("b2_virt= %p b2_phys= %" PRIxPTR "\n", b2, bp2);
   printf("Done\n");
}

打印如下内容:

Test virtual to physical address translation.

a1_virt= 0x958d008: a1_phys= 4f8ce
b1_virt= 0x9591010 b1_phys= 4d40b
a2_virt= 0x958dfa8 a2_phys= 4f8ce
b2_virt= 0x9591014 b2_phys= 4d40b
Done

如您所见,a1 和 a2 具有不同的虚拟地址但具有相同的物理地址,我的问题是;是我的虚拟物理地址转换有问题还是Linux内存管理问题,一个物理地址可以映射到两个不同的虚拟地址?

【问题讨论】:

  • 发布的代码甚至还没有开始编译。强烈建议在编译时启用所有警告,然后修复这些警告和错误。 (对于 gcc,至少使用:-Wall -Wextra -pedantic
  • 我修复了复制错误时发生的语法错误并添加了addresstranslation.h进行编译。谢谢。
  • 如果您的设备允许多个openmmap 您可以为一个物理设备获得多个虚拟
  • 你从哪里得知两个进程不能为一个文本共享段共享同一个物理内存页(例如ls(1)程序文本段的两个实例)???

标签: c linux memory memory-management


【解决方案1】:

C 语言区分大小写。

所以这个宏:

#DEFINE PAGE_MAPPING_FILE "/PROC/self/pagemap"

使用时不调用:

FILE *f = fopen(page_mapping_file, "rb");

#DEFINE 对 C 编译器没有任何意义。

正确的拼写是:#define I.E.全部小写

【讨论】:

  • 谢谢,我的代码是小写的,但是当我复制它时我假设它发生了变化。我将编辑问题,但我真正的问题仍未得到解答。
  • @Tugberk,您的问题最好准确,这样我们就不会浪费时间认为您在代码中犯了错误。即使在您自己的编码中,您也最好更加精确。对于如何解释该 proc 文件的精确定义,您拥有的最精确的源代码是在您自己的机器中(内核源代码)。我无法完全理解为什么你需要从虚拟内存到物理内存的精确映射,因为你盒子里的所有东西都需要使用某种虚拟寻址(甚至内核也会构造一个映射来查看你的代码)。阅读copy_{to,from}_user()的来源
猜你喜欢
  • 2014-03-17
  • 2014-03-20
  • 1970-01-01
  • 2023-02-07
  • 1970-01-01
  • 1970-01-01
  • 2020-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多