内存-虚拟地址到物理内存地址转换

虚拟地址的位数

[root@new ~]# cat /proc/cpuinfo  | grep virtu | tail -1
address sizes : 46 bits physical, 48 bits virtual

高性能C++之虚拟内存_哔哩哔哩_bilibili

第零层,每一项是4KB,每一个大项512个4KB是2MB,

第一层,每一项是2MB,每一大项是512个2MB是1GB

第二层,每一项是1GB,每一个大项是512个1GB是512GB

第三层,每一项是512GB,512个526GB是256TB

下在内核模块:

#include <iostream>
#include <fstream>
#include <string>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>

struct Page
{
	uint64_t address;
    // 页表中每一项是一个64bit的页表条目
	uint64_t entry[512];
};

uint64_t get_phys_address(uint64_t entry)
{
    // 获得物理地址,是48bit的真是物理地址
    // 第63位置有个1,低11个位置都是1,总共是12个1
    // ~mask是总共
	static const uint64_t mask = (1LL << 63) | ((1 << 12) - 1);
	return entry & ~mask;
}

bool writable(uint64_t entry)
{
	return (entry & (1 << 1)) != 0;
}

bool executable(uint64_t entry)
{
	return (entry & (1LL << 63)) == 0;
}

bool user_mode(uint64_t entry)
{
	return (entry & (1 << 2)) != 0;
}

void print_entry(FILE* fp, int level, uint64_t entry, uint64_t virtual_address)
{
	fprintf(fp, "%d\t0x%016lx\t0x%016lx\t%d\t%d\t%d\n", level, get_phys_address(entry), virtual_address, writable(entry), executable(entry), user_mode(entry));
}

void dump(FILE* fp, const Page*& page, int level, uint64_t virtual_address)
{
	const Page* cur_page = page++;
    // 每页表项都是512个
	for (int i = 0; i < 512; i++)
	{
        // 页表条目
		const uint64_t entry = cur_page->entry[i];
        // 获取当前的页表条目对应的虚拟地址
		const uint64_t child_virtual_address = (virtual_address << 9) | i;
		if (level > 0)
		{
			if (entry & 1)
			{
				if (!(entry&(1<<7)))
				{
                    // 继续递归下一层
					dump(fp, page, level - 1, child_virtual_address);
				}
				else
				{
                    // 打印当前页表条目对应的信息
					print_entry(fp, level, entry, child_virtual_address << (level * 9 + 12));
				}
			}
		}
		else
		{
            // 第0层是,512个4KB的页表条目
			if (entry)
			{
				print_entry(fp, level, entry, child_virtual_address << 12);
			}
		}
	}
}

void dump_pagetable(FILE* fp)
{
	std::ifstream ifs("/proc/page_table_3", std::ios::binary);
	if (!ifs)
	{
		return;
	}
	std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
	const Page* page = (const Page*)&content[0];
	const Page* end_page = (const Page*)(&content[0] + content.length());
	dump(fp, page, 3, 0);
	std::cout << (const void*)end_page << '\t' <<  (const void*)page << std::endl;
	std::flush(std::cout);
}

int main()
{
	const int N = 1024 * 1024 * 8;
    // 是否开启大页
	const bool hugetable = true;
	const bool do_fork = false;

	char* m = (char*)mmap(NULL, N, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED | (hugetable ? MAP_HUGETLB: 0), -1, 0);
			std::cout << *m << std::endl;
	FILE* fp = NULL;
	if (do_fork)
	{
		pid_t pid = fork();
		if (pid == 0)
		{
			fp = fopen("/home/fractal/lecture/child.log", "w");
		}
		else
		{
			fp = fopen("/home/fractal/lecture/father.log", "w");
		}
	}
	else
	{
		fp = fopen("/home/fractal/lecture/father.log", "w");
	}

	fprintf(fp, "mmap address: %p\n", m);
	dump_pagetable(fp);

	fclose(fp);
	while (true)
	{
		usleep(10000);
	}
	return 0;
}

1.只有向mmap申请的地址写入东西,操作系统才会分配物理地址

2.是大页,系统需要预先分配大页

3.for父子进程公用的内存页,被设置为不可写。子进程,如果往页写入东西,会出发异常,操作系统会捕获,分配新的物理内容,把内容copy一份(孩子的),并且设置为可写。父亲的页面还是不可以写入的。fork机制可以省内存,相对于多线程,不会有数据冲突。多线程需要考虑数据冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值