/* * up_wp_page()函数用于解除物理页的共享状态,同时给发生写时复制的进程提供一页新的 * 物理页,新物理页是之前共享页的数据相同的拷贝。 * table_entry是共享物理页的地址的指针,即页表实际地址+表内偏移地址 */ void un_wp_page(unsigned long * table_entry) { unsigned long old_page,new_page; old_page = 0xfffff000 & *table_entry; //取得共享物理页实际地址 if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) { *table_entry |= 2; //如果该页位于主内存区,且不是共享页,设为可写 invalidate(); //刷新页变换高速缓存 return; //直接退出 } if (!(new_page=get_free_page())) //如果是共享页,申请一页新物理页 oom(); //如果0 = get_free_page(),内存溢出,die if (old_page >= LOW_MEM) //如果共享页在主内存区 mem_map[MAP_NR(old_page)]--; //共享次数-1 *table_entry = new_page | 7; //将新页挂载到写时复制进程的页表中 invalidate(); //刷新页变换高速缓存 copy_page(old_page,new_page); //拷贝old_page内容到new_page } 1 /************************************************************************* 2 > File Name: table.c 3 > Author: Linpeng1577 4 > Mail:linpeng1577@gmail.com 5 > Created Time: Mon 24 Nov 2014 06:05:54 PM PST 6 ************************************************************************/ 7 8 #include<stdio.h> 9 10 static int page_dir[1023] = {0}; 11 static int page_table[1023] = {0}; 12 static int phy_addr_table[1023] = {0}; 13 static int new_phy_addr_table[1023] = {0}; 14 15 int main(void) 16 { 17 int *phy_page = NULL; 18 19 phy_page = phy_addr_table; 20 page_table[114] = (int)phy_page; 21 page_dir[83] = (int)(&page_table); 22 23 printf("phy_addr_table = 0x%x\n", (int)phy_addr_table); 24 printf("phy_page = 0x%x\n", (int)phy_page); 25 printf("page_table = 0x%x\n", (int)page_table); 26 printf("page_dir = 0x%x\n", (int)page_dir); 27 28 printf("*(&page_dir[83]) = 0x%x\n", *(&page_dir[83])); 29 printf("*(&page_table[114]) = 0x%x\n", *(&page_table[114])); 30 31 *(&page_table[114]) = (int)new_phy_addr_table; 32 printf("new_phy_addr_table = 0x%x\n", (int)new_phy_addr_table); 33 printf("*(&page_table[114]) = 0x%x\n", *(&page_table[114])); 34 } ~ ~ </lin/tests/linux/test_ptr/table.c [FORMAT=unix] [TYPE=C] [POS=31,30][91%] 24/11/14 - 21:27 "table.c" 34L, 1162C root@ubuntu:/home/iubuntu/lin/tests/linux/test_ptr# ./table phy_addr_table = 0x804c040 //共享物理页地址 phy_page = 0x804c040 page_table = 0x804b040 //页表地址 page_dir = 0x804a040 //页目录地址 *(&page_dir[83]) = 0x804b040 *(&page_table[114]) = 0x804c040 //old_page = 0xfffff000 & *table_entry; new_phy_addr_table = 0x804d040 //新物理页地址 *(&page_table[114]) = 0x804d040 //*table_entry = new_page | 7; 1.假设存在实际物理地址phy_page = phy_addr_table; 2.这个地址由页表page_table的第114项管理,page_table[114] = phy_page; 3.这个页表的地址由页目录表page_dir的第83项管理,page_dir[83] = page_table; 4.现在已经知道页目录表page_dir,页表page_table,页表由第83页目录项管理,物理页由页表 第114项管理,现在要取物理页地址phy_page; 5.通过 *(&page_dir[83])可以得到页表page_table的地址; 6.通过 *(&page_table[114])可以得到物理页地址phy_addr; 7.在void un_wp_page(unsigned long * table_entry)中,table_entry 扮演着类似(&page_table[114])的角色。 /* * 用于写时复制 * 通过给写共享页的进程分配新页,同时前共享页共享计数减1 * * 如果该页属于代码段,报错 */ /* * This routine handles present pages, when users try to write * to a shared page. It is done by copying the page to a new address * and decrementing the shared-page counter for the old page. * * If it's in code space we exit with a segment error. */ void do_wp_page(unsigned long error_code,unsigned long address) { #if 0 //被linus屏蔽的代码 /* we cannot do this yet: the estdio library writes to code space */ /* stupid, stupid. I really want the libc.a from GNU */ if (CODE_SPACE(address)) do_exit(SIGSEGV); #endif un_wp_page((unsigned long *) (((address>>10) & 0xffc) + (0xfffff000 & *((unsigned long *) ((address>>20) &0xffc))))); /* address是线性地址 (unsigned long *) (((address>>10) & 0xffc) + (0xfffff000 & *((unsigned long *) ((address>>20) &0xffc)))) 这里的前提是pg_dir = 0; 上面的地址可以分为下面[1][2]组合 [1]((address>>10) & 0xffc)用于计算物理页在页表的偏移索引 这里是取物理页地址,每个页表管理1024个页表项,每个页表项占4个字节 每个页表项可以管理一个物理页 所以这里是((address>>12) & 0x3ff)<<2 [2](0xfffff000 & *((unsigned long *) ((address>>20) &0xffc)) 这里是取页表地址值 页表保存在页目录项中 1个页目录表有1024个页目录项 1个页目录项占4个字节 1个页目录项管理1个页表 1个页表管理1024项页表项 1个页表项管理1个物理页 1个物理页占4K字节 = 1024*4 即1个页表项管理4K连续内存 页目录项数(1024) X 页表数(1024) X 4K = 4096M = 4G ((address>>22) &0x3ff))<<2这个是页目录项在页目录表中的偏移地址 (unsigned long *) ((address>>22) &0x3ff))<<2是取保存在该目录项中页表的物理地址 所以物理页被保存的地址是[2]页表的物理地址+[1]页表内偏移地址 所以物理页的地址是 (unsigned long *) (((address>>12) & 0x3ff)<<2) + (unsigned long *) ((address>>22) &0x3ff))<<2 指针取指 页表内偏移地址 页表物理地址 } */
Linux-0.11内核源码分析系列:内存管理up_wp_page()与do_wp_page()函数分析
原文地址:http://blog.csdn.net/linpeng12358/article/details/41510575