您的位置:首页 > 运维架构 > Linux

Linux 虚拟内存发展历史 个人笔记

2015-01-20 12:50 260 查看
慢慢来吧~~

Linux 系统自 V0.12 就加入了对虚拟内存的支持.



我们先看下Linux V0.11 对内存管理的支持情况:

Linux V0.11 对内存是以页(1024K) 为单位管理的. 所有的页的使用情况存储在数组pg_dir 中.

提供free_page , get_free_page 之类的接口来释放或者获取页面.

提供put_page 接口来将页面映射到具体地址.



Linux V0.12

虚拟内存实现细节.

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;
}


Linux V0.95

1. 修改try_to_swap_out的一个BUG

page &= 0xfffff000;


2. 修改swap_out的BUG

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;
}


Linux V0.95a

全面改写了 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;
}


Linux V0.95c

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

越来越复杂了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: