// put_page用来完成物理页面与一个线性地址页面的挂接,从而将一个
// 线性地址空间内的页面落实到物理地址空间内,copy_page_tables函数
// 只是为一个进程提供了在线性地址空间的一个页表及1024页内存,而当时
// 并未将其对应的物理内存上。put_page函数则负责为copy_page_tables开
// 的“空头支票”买单。
// page为物理地址,address为线性地址
unsigned long put_page(unsigned long page,unsigned long address)
{
unsigned long tmp, *page_table;
/* NOTE !!! This uses the fact that _pg_dir=0 */
// 要映射的线性地址只能在主内存区,你不能给内存起始1M范围内映射线性地址
// 因为LOW_MEM内是给内核用的,且不属于分页内存范围。
if (page < LOW_MEM || page >= HIGH_MEMORY)
printk("Trying to put page %p at %p\n",page,address);
// (page-LOW_MEM)>>12得到page这个物理地址对应的页号
// mem_map[(page-LOW_MEM)>>12] !=1是检查这个page地址对应的物理内存页面
// 是不是已经注册过的页面(用get_free_page函数申请,申请时会再mem_map中
// 注册这个页面,也就是把这个页面对应的mem_map项加1。所以此处==1就代表
// 这个物理页面是刚申请好备用的,只有这种物理页面才能被映射到一个线性地
// 址。所以这里!=1就要警告)
if (mem_map[(page-LOW_MEM)>>12] != 1)
printk("mem_map disagrees with %p at %p\n",page,address);
// page_table得到address线性地址页面在页目录表中对应表项的首地址指针,这个
// 指针用来得到页表基地址,然后寻址就进行到了页表级。再配合address的次10位
// 就能寻址到address对应的内存页面基地址了。最后12bit用来在页面内寻址。
page_table = (unsigned long *) ((address>>20) & 0xffc);
// 检验address对应的页目录表项内容,即页表地址是不是有效地,若页表有效,则
// 直接把页表地址给page_table;若页表无效,则调用get_free_page函数在mem_map
// 数组内申请一个空闲页面,并将这个空闲页面设置为用户级、只读、存在,再将
// 新申请并处理好的页面地址赋值给page_table
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了,现在就要将page这个物理页面和address
// 这个线性地址处的页面挂接起来了。挂接的方法就是在page_table中,将address对应
// 的页表项中的内容(即address这个地址所在的页面基地址)设置为page这个物理地址
// |7没什么好说的,这里值得关注的是page_table竟然还可以当做数组来用···刚开始
// 着实吃了一惊,不过仔细想想page_table本来就是个指针,而1024个页表项也和数组
// 结构一样的,用数组的方式来访问也是可行的(当然我还是觉得用指针+偏移量更好理解)
// 再次感叹C语言的灵活啊
// 再说明下(address>>12)&0x3ff吧,毕竟自己没看出来,还是看了赵炯博士的注释才发现
// 的。这个操作实际是取出address次10位表示页面在页表内的偏移值。这样用这个偏移值
// 结合page_table这个页表首地址,就能找到address所在的页面基地址了。
page_table[(address>>12) & 0x3ff] = page | 7;
/* no need for invalidate */
return page;
}
// 线性地址空间内的页面落实到物理地址空间内,copy_page_tables函数
// 只是为一个进程提供了在线性地址空间的一个页表及1024页内存,而当时
// 并未将其对应的物理内存上。put_page函数则负责为copy_page_tables开
// 的“空头支票”买单。
// page为物理地址,address为线性地址
unsigned long put_page(unsigned long page,unsigned long address)
{
unsigned long tmp, *page_table;
/* NOTE !!! This uses the fact that _pg_dir=0 */
// 要映射的线性地址只能在主内存区,你不能给内存起始1M范围内映射线性地址
// 因为LOW_MEM内是给内核用的,且不属于分页内存范围。
if (page < LOW_MEM || page >= HIGH_MEMORY)
printk("Trying to put page %p at %p\n",page,address);
// (page-LOW_MEM)>>12得到page这个物理地址对应的页号
// mem_map[(page-LOW_MEM)>>12] !=1是检查这个page地址对应的物理内存页面
// 是不是已经注册过的页面(用get_free_page函数申请,申请时会再mem_map中
// 注册这个页面,也就是把这个页面对应的mem_map项加1。所以此处==1就代表
// 这个物理页面是刚申请好备用的,只有这种物理页面才能被映射到一个线性地
// 址。所以这里!=1就要警告)
if (mem_map[(page-LOW_MEM)>>12] != 1)
printk("mem_map disagrees with %p at %p\n",page,address);
// page_table得到address线性地址页面在页目录表中对应表项的首地址指针,这个
// 指针用来得到页表基地址,然后寻址就进行到了页表级。再配合address的次10位
// 就能寻址到address对应的内存页面基地址了。最后12bit用来在页面内寻址。
page_table = (unsigned long *) ((address>>20) & 0xffc);
// 检验address对应的页目录表项内容,即页表地址是不是有效地,若页表有效,则
// 直接把页表地址给page_table;若页表无效,则调用get_free_page函数在mem_map
// 数组内申请一个空闲页面,并将这个空闲页面设置为用户级、只读、存在,再将
// 新申请并处理好的页面地址赋值给page_table
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了,现在就要将page这个物理页面和address
// 这个线性地址处的页面挂接起来了。挂接的方法就是在page_table中,将address对应
// 的页表项中的内容(即address这个地址所在的页面基地址)设置为page这个物理地址
// |7没什么好说的,这里值得关注的是page_table竟然还可以当做数组来用···刚开始
// 着实吃了一惊,不过仔细想想page_table本来就是个指针,而1024个页表项也和数组
// 结构一样的,用数组的方式来访问也是可行的(当然我还是觉得用指针+偏移量更好理解)
// 再次感叹C语言的灵活啊
// 再说明下(address>>12)&0x3ff吧,毕竟自己没看出来,还是看了赵炯博士的注释才发现
// 的。这个操作实际是取出address次10位表示页面在页表内的偏移值。这样用这个偏移值
// 结合page_table这个页表首地址,就能找到address所在的页面基地址了。
page_table[(address>>12) & 0x3ff] = page | 7;
/* no need for invalidate */
return page;
}