《linux内核完全剖析》笔记06-内存管理
2017-01-12 02:08
239 查看
1. 管理的内存从哪里来?(初始化)
744 图13.5memory.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-2memory.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-4static 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 144.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; }