1. 管理的内存从哪里来?(初始化)
744 图13.5
memory.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-2
memory.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-4
static 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 14
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行
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;
}