从/ proc / [pid] / pagemap获取物理地址失败

时间:2015-08-31 14:31:09

标签: c linux memory memory-management

我正在尝试使用虚拟地址从/ 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;
}

addresstranslation.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内存管理有关,是否可以将一个物理地址映射到两个不同的虚拟地址?

1 个答案:

答案 0 :(得分:1)

C语言区分大小写。

所以这个宏:

#DEFINE PAGE_MAPPING_FILE "/PROC/self/pagemap"
使用时不会调用

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

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

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