【无标题】

1、将虚拟地址传入到内核态,借助内核态中mm_struct结构体的pgd页表基地址成员,经过查页表的方式最终获取到物理地址。这种方法虽然很直观,但是一会内核态,一会用户态,操作起来相对表麻烦。虚拟地址如何访问到物理地址_sydyh43的博客-CSDN博客_虚拟地址计算物理地址但是有一点可以明确的,转换出来,获取到的是页帧号(即页对齐),最后加上虚拟地址的低N位(32bit的系统是低12位)得到最终的物理地址。

2、内核开发者会把很多基于内核的操作挪到用户态实现。借助用户态众多的系统接口实现与之前在内核态一样的功能。虚拟地址转换成物理地址就是这么一个例子。

通过

man proc

查看到其中有一项/pro/[pid]/pagemap,可以看出54~0就是对应着物理地址的页帧号,再加上虚拟地址的低12位,就是虚拟地址对应的物理地址。

【无标题】_第1张图片

3、 在用户态,读取/proc/[pid]/pagemap这个节点的数据,获取物理内存的页帧号。

3.1、进程下有很多虚拟地址,pagemap文件相当于存放物理地址的一张表格,需要根据虚拟地址获取到在这张表格的下标,这个下标对应的表格,存放的就是这个虚拟地址对应的物理地址等信息。

#ifdef _ENV_IS_64_
#define PAGEMAP_SIZE    sizeof(unsigned long)
#else
#define PAGEMAP_SIZE    sizeof(unsigned long long)
#endif

/*
*    1、需要把addr转换成页帧,即下标
*    2、每个下标存放数据的空间是PAGESIZE,需要乘以PAGEMAP_SIZE
*    3、unsigned long的sizeof由CPU环境决定。32bit是4, 64bit是8
*/
size_t offset = (addr/4096) * PAGEMAP_SIZE;	

3.2、获取pagemap中的数值后,最终转换成实际的物理地址

/*
*    1、获取pte后,&上地55bit都是高的数值
*    2、左右12bit,加上低12bit的偏移量,获得最终的物理地址
*/
read(fd, &pte, PAGEMAP_SIZE);
phy_addr = ((pte & ((((unsigned long)1) << 55) - 1)) << 12) + addr%4096;

3.3、此刻,我们来验证一下bit63的数值变化,感知下用户态malloc一段内存空间后,暂没有分配内存空间,而是当使用到的时候才会分配实际的物理内存。

有些平台使用的时候,proc目录下没有pagemap的节点,需要打开对应(fs/proc/base.c)的宏CONFIG_PROC_PAGE_MONITOR

#include 
#include 
#include 
#include 
#include 

#define _ENV_IS_32_

#ifdef _ENV_IS_64_
#define PAGEMAP_SIZE    sizeof(unsigned long)
#else
#define PAGEMAP_SIZE    sizeof(unsigned long long)
#endif

int main(int argc, char **argv)	
{	
	int fd 		 = 0;
	pid_t pid	 = 0;
	char bit_val = 0;
	unsigned long addr = 0;
	unsigned long phy_addr = 0;
#ifdef _ENV_IS_64_
	unsigned long pte;
#else
	unsigned long long pte;
#endif
	size_t offset = 0;
	char procbuf[64] = {0};

	pid = getpid();	
	sprintf(procbuf, "/proc/%d/pagemap", pid);
	addr = (unsigned long)malloc(100);
	if (!addr) {
		printf("malloc error.\n");
		return -1;
	}
	fd = open(procbuf, O_RDONLY);
	offset = (addr/4096) * PAGEMAP_SIZE;
	lseek(fd, offset, SEEK_SET);
	read(fd, &pte, PAGEMAP_SIZE);
	phy_addr = (pte & 0x7FFFFFFFFFFFFF)*4096 + addr%4096;		//(((1) << 55) - 1)
	bit_val = !!(pte & 0x8000000000000000);					//((1) << 63)
	printf("\nbef: pte=0x%llx, phy addr:%x, in ram:%d\n", pte, phy_addr, bit_val);

	memset((char *)addr, 0x00, 100);
	lseek(fd, offset, SEEK_SET);
	read(fd, &pte, PAGEMAP_SIZE);
	phy_addr = (pte & 0x7FFFFFFFFFFFFF)*4096 + addr%4096;
	bit_val = !!(pte & 0x8000000000000000);
	printf("aft: pte=0x%llx, phy addr:%x, in ram:%d\n", pte, phy_addr, bit_val);	
	
	close(fd);
	
	return 0;	
}

 

你可能感兴趣的:(内存管理,linux)