慢慢来吧~~
Linux 系统自 V0.12 就加入了对虚拟内存的支持.
Linux V0.11 对内存是以页(1024K) 为单位管理的. 所有的页的使用情况存储在数组pg_dir 中.
提供free_page , get_free_page 之类的接口来释放或者获取页面.
提供put_page 接口来将页面映射到具体地址.
虚拟内存实现细节.
1. 在get_free_page中 , 当发现没有空闲内存页面的时候, 就尝试调用换出接口 swap_out .
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; }
swap_out 接口遍历内存数组, 查找当前可用的内存并尝试换出, 若内存已写,则保存到磁盘
中,否则直接释放.
换入磁盘的页面,其内存页表项将会改写为对应的磁盘页掩码X2 . 这样将来就可以对应的
换入.
int try_to_swap_out(unsigned long * table_ptr) { unsigned long page; unsigned long swap_nr; page = *table_ptr; if (!(PAGE_PRESENT & page)) return 0; if (page - LOW_MEM > PAGING_MEMORY) return 0; if (PAGE_DIRTY & page) { page &= 0xfffff000; if (mem_map[MAP_NR(page)] != 1) // 内存在使用中 return 0; if (!(swap_nr = get_swap_page())) // 获得一个空闲的磁盘页 return 0; *table_ptr = swap_nr<<1; // 页表项改为记录磁盘页吗 invalidate(); write_swap_page(swap_nr, (char *) page); free_page(page); return 1; } *table_ptr = 0; invalidate(); free_page(page); return 1; } /* * Ok, this has a rather intricate logic - the idea is to make good * and fast machine code. If we didn't worry about that, things would * be easier. */ int swap_out(void) { static int dir_entry = FIRST_VM_PAGE>>10; static int page_entry = -1; int counter = VM_PAGES; int pg_table; while (counter>0) { pg_table = pg_dir[dir_entry]; if (pg_table & 1) break; counter -= 1024; // 扣去已经使用的page dir_entry++; if (dir_entry >= 1024) dir_entry = FIRST_VM_PAGE>>10; } pg_table &= 0xfffff000; //找到一个有效页表项 while (counter-- > 0) { page_entry++; if (page_entry >= 1024) { page_entry = 0; repeat: dir_entry++; if (dir_entry >= 1024) dir_entry = FIRST_VM_PAGE>>10; pg_table = pg_dir[dir_entry]; if (!(pg_table&1)) // 页面无效 , 可能已经在swap中 if ((counter -= 1024) > 0) // BUG goto repeat; else break; pg_table &= 0xfffff000; // 页面有效 } if (try_to_swap_out(page_entry + (unsigned long *) pg_table)) // 置换一页. return 1; } printk("Out of swap-memory\n\r"); return 0; }
1. 修改try_to_swap_out的一个BUG
page &= 0xfffff000;
int swap_out(void) { static int dir_entry = 1024; static int page_entry = -1; int counter = VM_PAGES; int pg_table = 0; repeat: while (counter > 0) { counter -= 1024; dir_entry++; if (dir_entry >= 1024) dir_entry = FIRST_VM_PAGE>>10; if (pg_table = pg_dir[dir_entry]) break; } if (counter <= 0) { printk("Out of swap-memory\n"); return 0; } if (!(pg_table & 1)) { printk("bad page-table at pg_dir[%d]: %08x\n\r",dir_entry, pg_table); return 0; } pg_table &= 0xfffff000; while (counter > 0) { counter--; page_entry++; if (page_entry >= 1024) { page_entry = -1; goto repeat; } if (try_to_swap_out(page_entry + (unsigned long *) pg_table)) return 1; } printk("Out of swap-memory\n\r"); return 0; }
全面改写了 swap_out 接口 , 仍然是随便找一个换出去.
int swap_out(void) { static int dir_entry = 1024; static int page_entry = -1; int counter = VM_PAGES; int pg_table; check_dir: if (counter < 0) goto no_swap; if (dir_entry >= 1024) dir_entry = FIRST_VM_PAGE>>10; if (!(1 & (pg_table = pg_dir[dir_entry]))) { // 找个可用的一级页表 if (pg_table) { printk("bad page-table at pg_dir[%d]: %08x\n\r", dir_entry,pg_table); pg_dir[dir_entry] = 0; } counter -= 1024; dir_entry++; goto check_dir; } pg_table &= 0xfffff000; check_table: if (counter < 0) goto no_swap; counter--; page_entry++; if (page_entry >= 1024) { page_entry = -1; dir_entry++; goto check_dir; } if (try_to_swap_out(page_entry + (unsigned long *) pg_table)) // 一一尝试换出 return 1; goto check_table; no_swap: printk("Out of swap-memory\n\r"); return 0; }
swap_out加入对进程的一点操作\
if (try_to_swap_out(page_entry + (unsigned long *) pg_table)) rerurn 1;==>
if (try_to_swap_out(page_entry + (unsigned long *) pg_table)) { if (! task[dir_entry >> 4]) printk("swapping out page from non-existent task\n\r"); else task[dir_entry >> 4]->rss--; return 1; }
之后的版本不再默认一级页表总是在地址0
越来越复杂了