【发布时间】: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进行编译。谢谢。
-
如果您的设备允许多个
open和mmap您可以为一个物理设备获得多个虚拟 -
你从哪里得知两个进程不能为一个文本共享段共享同一个物理内存页(例如
ls(1)程序文本段的两个实例)???
标签: c linux memory memory-management