笔记重点:
在琢磨linux源代码的过程,一直有一些疑惑,比如我明明只有4G的内存,计算机却可以运行很多进程,并且为每个进程都分配4G的内存,显然内存是不够用的;还有就是操作系统是怎么知道哪些内存可用,哪些内存不可用,以及怎样有效的分配他们,在从早期操作系统的平坦式管理,到粗糙的段式管理到段页式管理,可谓对内存的管理是不断的抽象,以至于页面管理对现代操作系统的重要性,不言而喻。
- 内存的大小以及可用段是通过BIOS传给操作系统初始化的
- 内存的段页式管理需要硬件的支持!!!
- 最初分配给进程的内存大概只有一页,其他的内存按需分配,通过缺页中断申请(进程虚拟页面和物理页面并不是一一对应)
1. 管理的内存从哪里来?(初始化)
744 图13.5
memory.c第443行
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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; while (end_mem-->0) mem_map[i++]=0; }
|
2. 进程的页目录项和页表项的复制
742 图13-2
memory.c第118行,这段代码是相当有意思的,在创建进程时调用,分配一页内存存放页表项,并复制源进程的页表信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| 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_dir = (unsigned long *) ((from>>20) & 0xffc); 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; *to_dir = ((unsigned long) to_page_table) | 7; 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| static unsigned long put_page(unsigned long page,unsigned long address) { unsigned long tmp, *page_table;
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); 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;
return page; }
|
4. 页面出错异常处理机制(写时复制和需求加载的01基础)
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行
1 2 3 4 5 6
| void trap_init(void) { ...... set_trap_gate(14,&page_fault); ...... }
|
page.s详细分析
- 写时复制 : 页面不共享时,设置可写则返回,共享时分配一页并复制
- 缺页处理: 是否在交换设备中,存在则交换回来,不存在则分配一页
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| .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()函数的分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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; }
|