《linux核心完全剖析》筆記06-記憶體管理
阿新 • • 發佈:2019-02-04
1. 管理的記憶體從哪裡來?(初始化)
744 圖13.5
memory.c第443行
void mem_init(long start_mem, long end_mem)
{
int i;
HIGH_MEMORY = end_mem;
for (i=0 ; i<PAGING_PAGES ; i++)
mem_map[i] = USED;//核心使用的那些頁初始化被使用了
//算出開始頁的索引號
i = MAP_NR(start_mem);
end_mem -= start_mem;
end_mem >>= 12 ;//除以4096(4K)一頁的大小
while (end_mem-->0)
mem_map[i++]=0;
}
2. 程序的頁目錄項和頁表項的複製
742 圖13-2
memory.c第118行,這段程式碼是相當有意思的,在建立程序時呼叫,分配一頁記憶體存放頁表項,並複製源程序的頁表資訊
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 new_page;
unsigned long nr;
//沒有對齊就是異常了
if ((from&0x3fffff) || (to&0x3fffff))
panic("copy_page_tables called with wrong alignment");
//頁目錄表的偏移位置 (from >> 22) << 2
from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
to_dir = (unsigned long *) ((to>>20) & 0xffc);
//2^22 = 4M 一張頁表管理4M的內容,右移22計算出需要幾張頁表
size = ((unsigned) (size+0x3fffff)) >> 22;
for( ; size-->0 ; from_dir++,to_dir++) {
if (1 & *to_dir)
panic("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 */
// 7 代表 U/S W/R P第0,1,2位置1
*to_dir = ((unsigned long) to_page_table) | 7;
//頁表每項4位元組,一共1024項
nr = (from==0)?0xA0:1024;
for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
this_page = *from_page_table;
if (!this_page)
continue;
if (!(1 & this_page)) {
if (!(new_page = get_free_page()))
return -1;
read_swap_page(this_page>>1, (char *) new_page);
*to_page_table = this_page;
*from_page_table = new_page | (PAGE_DIRTY | 7);
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]++;
}
}
}
invalidate();
return 0;
}
3. 實體記憶體時如何分配給線性地址的
743 圖13-4
static unsigned long put_page(unsigned long page,unsigned long address)
{
unsigned long tmp, *page_table;
/*
page是索引值
address是實體地址
*/
/* NOTE !!! This uses the fact that _pg_dir=0 */
if (page < LOW_MEM || page >= HIGH_MEMORY)
printk("Trying to put page %p at %p\n",page,address);
if (mem_map[(page-LOW_MEM)>>12] != 1)
printk("mem_map disagrees with %p at %p\n",page,address);
//從實體地址得到頁表項
page_table = (unsigned long *) ((address>>20) & 0xffc);
//*page_table 是頁表項的內容,&1是表示頁表項已經存在,沒有就重新分配一頁
if ((*page_table)&1)
page_table = (unsigned long *) (0xfffff000 & *page_table);
else {
if (!(tmp=get_free_page()))
return 0;
*page_table = tmp | 7;
page_table = (unsigned long *) tmp;
}
//填寫頁表項內容
page_table[(address>>12) & 0x3ff] = page | 7;
/* no need for invalidate */
return page;
}
4. 寫時複製和需求載入的基礎
頁面出錯異常處理 int 14
4.1 引起異常的條件
地址變換過程中頁目錄項或頁表項存在位P=0
沒有足夠的特權訪問指定的頁面
4.2 異常的結果
cr2暫存器存放出錯的線形地址
棧中出錯碼資訊
- 位0 p=0 頁面不存在 p=1 違反頁面保護許可權
- 位1 w/r =0 讀引起 w/r =1 寫引起
- 位2 u/s =0 執行超級使用者程式碼
4.3 異常處理過程
trap.c 第203行
void trap_init(void)
{
......
set_trap_gate(14,&page_fault);//設定頁面異常處理函式
......
}
page.s詳細分析
- 寫時複製 : 頁面不共享時,設定可寫則返回,共享時分配一頁並複製
- 缺頁處理: 是否在交換裝置中,存在則交換回來,不存在則分配一頁
.globl _page_fault
_page_fault:
xchgl %eax,(%esp)
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%edx
mov %dx,%ds
mov %dx,%es
mov %dx,%fs
movl %cr2,%edx
pushl %edx
pushl %eax
testl $1,%eax #測試頁存在位
jne 1f
call _do_no_page #缺頁處理函式 do_no_page
jmp 2f
1: call _do_wp_page #防寫處理函式
2: addl $8,%esp
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
5. 到底怎麼分配實體記憶體
get_free_page()函式的分析
unsigned long get_free_page(void)
{
register unsigned long __res asm("ax");
repeat:
__asm__("std ; repne ; scasb\n\t"
"jne 1f\n\t"
"movb $1,1(%%edi)\n\t"
"sall $12,%%ecx\n\t"
"addl %2,%%ecx\n\t"
"movl %%ecx,%%edx\n\t"
"movl $1024,%%ecx\n\t"
"leal 4092(%%edx),%%edi\n\t"
"rep ; stosl\n\t"
"movl %%edx,%%eax\n"
"1:"
:"=a" (__res)
:"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
"D" (mem_map+PAGING_PAGES-1)
:"di","cx","dx");
if (__res >= HIGH_MEMORY)
goto repeat;
if (!__res && swap_out())
goto repeat;
return __res;
}