虚拟地址的位数
- [root@new ~]# cat /proc/cpuinfo | grep virtu | tail -1
- address sizes : 46 bits physical, 48 bits virtual
第零层,每一项是4KB,每一个大项512个4KB是2MB,
第一层,每一项是2MB,每一大项是512个2MB是1GB
第二层,每一项是1GB,每一个大项是512个1GB是512GB
第三层,每一项是512GB,512个526GB是256TB
下在内核模块:
- #include
- #include
- #include
- #include
- #include
- #include
-
- 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机制可以省内存,相对于多线程,不会有数据冲突。多线程需要考虑数据冲突。