linux 0.11 源码学习(十一)

memory.c

在X86的保护模式中,线性地址由页目录表(10位)+页表(10位)+ 偏移(12位)组成,因此对线性地址而言可以寻址4G的地址空间。而实际中linux支持16M的内存,因此在memory.c或者说linux的内存管理模块中,维护了线性地址和实际物理地址的映射。本篇博客主要记录内存管理的几个主要函数学习。

下面几个宏定义可以看出物理页面的分配数:

#define USED 100    //mem_map中的映射值,UNUSED是初始化值0,在mem_init中完成
#define
PAGING_MEMORY (15*1024*1024) //实际的主内存15M,最低端的1M分配给内核 #define PAGING_PAGES (PAGING_MEMORY>>12) //实际的物理页,每页表项是12位偏移地址,因此长度为4096
  • get_free_page(void)代码,完成功能是获取一个可用的页:
/*
 * Get physical address of first (actually last :-) free page, and mark it
 * used. If no free pages left, return 0.
 */
unsigned long get_free_page(void)
{
register unsigned long __res asm("ax"); //返回值是寄存器ax

__asm__("std ; repne ; scasb\n\t"//edi指向的值与al(0)比较
    "jne 1f\n\t"
    "movb $1,1(%%edi)\n\t" //edi+1的值=1
    "sall $12,%%ecx\n\t" //ecx的值左移12位,即ecx * 4KB
    "addl %2,%%ecx\n\t" //ecx的值加2
    "movl %%ecx,%%edx\n\t" //赋值给edx
    "movl $1024,%%ecx\n\t" //ecx = 1024
    "leal 4092(%%edx),%%edi\n\t" //edx + 4092的值读进edi
    "rep ; stosl\n\t" //将eax的值拷贝到edi中,重复ecx次,完成页表项清零
    "movl %%edx,%%eax\n" //edx赋值给eax,返回eax
    "1:"
    :"=a" (__res) 
    :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), //ax = 0, ecx = PAGIN_PAGES 物理内存页数
    "D" (mem_map+PAGING_PAGES-1)//edi = mem_map + PAGING_PAGES - 1
    );
return __res;
}
  •  free_page完成释放指定物理地址对应的物理页,代码如下:
    addr -= LOW_MEM;
    addr >>= 12; //上述两行代码,将addr由实际物理地址转换为mem_map中的索引
    if (mem_map[addr]--) return;
    mem_map[addr]=0;//对应的addr的mem_map设置为未使用
  • copy_page_tables在进程fork时被调用,如下:
    if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
        printk("free_page_tables: from copy_mem\n");
        free_page_tables(new_data_base,data_limit);
        return -ENOMEM;
    }

注: 此处的from/to都是线性地址,如上述描述由10+10+12表示,但要注意的是这里的10\10\都是在页目录表和页表中的索引,因此对于实际的物理地址要*4B。


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)) panic("copy_page_tables called with wrong alignment"); from_dir = (unsigned long *) ((from>>20) & 0xffc); //左移24位 * 4B,即from线性地址对应的目录表实际地址 to_dir = (unsigned long *) ((to>>20) & 0xffc); //同上 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 */ *to_dir = ((unsigned long) to_page_table) | 7; nr = (from==0)?0xA0:1024; for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {//每个页目录表有1024个页表项 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]++;//该物理页(即是1024*4大小) } } } invalidate(); return 0; }

在linux中采用了写时复制技术,也就是当某个线性地址被写时,触发相应的缺页错误。该缺页错误会导致分配物理页面,实现的代码是page.s,

  • 具体的业务逻辑在函数do_no_page中,如下:
void do_no_page(unsigned long error_code,unsigned long address)//address是产生异常页面的线性地址
{
    int nr[4];
    unsigned long tmp;
    unsigned long page;
    int block,i;

    address &= 0xfffff000;//低12位是页面内的偏移,此处要复制的是整个页的起始地址,因此低12位取0
    tmp = address - current->start_code;//current->start_code是进程线性地址的起始地址
    if (!current->executable || tmp >= current->end_data) {
        get_empty_page(address);
        return;
    }
    if (share_page(tmp))//申请共享内存,如果有其他进程已执行了一样的文件
        return;
    if (!(page = get_free_page()))//获取一个page
        oom();
/* remember that 1 block is used for header */
    block = 1 + tmp/BLOCK_SIZE;
    for (i=0 ; i<4 ; block++,i++)
        nr[i] = bmap(current->executable,block);//寻求进程相应地址在文件系统中的逻辑号
    bread_page(page,current->executable->i_dev,nr);//将文件中的内容读入到分配的内存页中
    i = tmp + 4096 - current->end_data;
    tmp = page + 4096;
    while (i-- > 0) {
        tmp--;
        *(char *)tmp = 0;
    }
    if (put_page(page,address))
        return;
    free_page(page);
    oom();
}

转载于:https://www.cnblogs.com/Fredric-2013/archive/2013/05/09/3060839.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值