《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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值