Linux-0.11核心源碼分析系列:記憶體管理copy_page_tables()函數分析,
/* *Author : DavidLin *Date : 2014-11-22pm *Email : linpeng1577@163.com or linpeng1577@gmail.com *world : the city of SZ, in China *Ver : 000.000.001 *history : editor time do * 1)LinPeng 2014-11-22 created this file! * 2) */ /* * Well, here is one of the most complicated functions in mm. It * copies a range of linerar addresses by copying only the pages. * Let's hope this is bug-free, 'cause this one I don't want to debug :-) * * Note! We don't copy just any chunks of memory - addresses have to * be divisible by 4Mb (one page-directory entry), as this makes the * function easier. It's used only by fork anyway. * * NOTE 2!! When from==0 we are copying kernel space for the first * fork(). Then we DONT want to copy a full page-directory entry, as * that would lead to some serious memory waste - we just copy the * first 160 pages - 640kB. Even that is more than we need, but it * doesn't take any more memory - we don't copy-on-write in the low * 1 Mb-range, so the pages can be shared with the kernel. Thus the * special case for nr=xxxx. *//* Linus認為下面copy_page_tables()函數是記憶體管理部分最難的之一 * copy_page_tables()函數只被fork函數調用 * 拷貝只是拷貝了一個頁表,頁表是管理4M地址的,所以按照4M對齊 * 不拷貝物理頁內容,當發生寫時拷貝才會拷貝頁表所管理的物理頁內容 * 對於進程0和1,只拷貝前160頁共640Kb,出於效率考慮 * 0-1M作為核心駐留地址地區,禁止寫覆蓋 * 參數from,to是0-4G線性地址,size是位元組為單位 */int copy_page_tables(unsigned long from,unsigned long to,long size){unsigned long * from_page_table; //用於管理源頁表 unsigned long * to_page_table; //用於管理目的頁表unsigned long this_page; //用於儲存頁表unsigned long * from_dir, * to_dir; //用於管理源頁目錄項,目的頁目錄項unsigned long nr; //用於儲存頁表項個數if ((from&0x3fffff) || (to&0x3fffff)) //4M對齊檢測,否則diepanic("copy_page_tables called with wrong alignment");from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */ //源頁目錄項to_dir = (unsigned long *) ((to>>20) & 0xffc); //目的頁目錄項size = ((unsigned) (size+0x3fffff)) >> 22; //頁表項個數是位元組數除以4Mfor( ; size-->0 ; from_dir++,to_dir++) { if (1 & *to_dir) //如果目的頁目錄項已經被使用,diepanic("copy_page_tables: already exist");if (!(1 & *from_dir)) continue; //如果源頁目錄項未使用,跳過,不拷貝from_page_table = (unsigned long *) (0xfffff000 & *from_dir);//取源頁表 if (!(to_page_table = (unsigned long *) get_free_page())) return -1; /* Out of memory, see freeing */ //取空閑物理頁為to_page_table賦值 //如果沒有空閑物理頁,die *to_dir = ((unsigned long) to_page_table) | 7; //將頁表存進相應頁目錄項, //7表示可讀寫 //想一下常用的chmod 777 anyfile nr = (from==0)?0xA0:1024; //如果是0地址,只拷貝160頁,否則拷貝1024頁 //一個頁目錄表管理1024個頁目錄項 //一個頁表管理1024個頁表項 //一個頁表項管理有4K物理地址 for ( ; nr-- > 0 ; from_page_table++,to_page_table++) { this_page = *from_page_table; //從源頁表中取源頁表項 if (!(1 & this_page)) //如果源頁表項未被使用,跳過 continue; this_page &= ~2; //目的頁表項讀寫位, //設定為唯讀 *to_page_table = this_page; //將源頁表項存進目的頁表項 if (this_page > LOW_MEM) { //如果是主記憶體區 *from_page_table = this_page;//源頁表項也要設定為唯讀 this_page -= LOW_MEM; //取相對主記憶體的位移地址 this_page >>= 12; //取主記憶體管理數組索引 mem_map[this_page]++; //物理頁引用次數加1 } } } invalidate(); //重新整理快取 return 0; //返回0表示成功}