1. 程式人生 > >《linux核心完全剖析》筆記06-記憶體管理

《linux核心完全剖析》筆記06-記憶體管理

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;
}