/*
拷贝一个范围内的内存,以内存页为粒度进行拷贝
这里的from和to都是32位的线性地址,size表示要拷贝的地址范围
*/
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
nr;
/*
要求地址的低22位全部是0,即地址要是4M对齐的。因为这里只是拷贝对应的页目录表中的内容,而页目录中每一项对应的地址是以4M对齐的
*/
if
((from&0x3fffff) || (to&0x3fffff))
panic(
"copy_page_tables called with wrong alignment"
);
/*
0xffc=1111 1111 1100
这里是将线性地址转换成对应的在页目录表中的索引。又因为页目录表所在的地址为物理地址0处。
所以这里的索引就刚好是对应的页目录表项的物理地址
*/
from_dir = (
unsigned
long
*) ((from>>20) & 0xffc);
/* _pg_dir = 0 */
to_dir = (
unsigned
long
*) ((to>>20) & 0xffc);
/*
参数中所给的size代表的是拷贝的线性地址范围的大小,这里将其转换成对应的在页目录表中项的个数。不足一项的按一项来处理
*/
size = ((
unsigned
) (size+0x3fffff)) >> 22;
for
( ; size-->0 ; from_dir++,to_dir++) {
/*
地址的第0 bit位表示的是相应的页是否已在内存中,即是否已被使用
*/
if
(1 & *to_dir)
panic(
"copy_page_tables: already exist"
);
/*表示来源地址中此项没有被使用到,即此部分地址空间没有用到,因此不用拷贝*/
if
(!(1 & *from_dir))
continue
;
/*
获取页目录表项数据
页目录项和页表项数据的低12位是有特殊含义的,对于不同的进程来说,意义不一样,不用拷贝
只需要高20位的地址即可*/
from_page_table = (
unsigned
long
*) (0xfffff000 & *from_dir);
/*get_free_page函数的作用是获取一页可用的物理内存页,并标记为已用
如果获取成功就返回对应的内存页的物理地址
如果获取失败,则返回0,表示内存不足
*/
if
(!(to_page_table = (
unsigned
long
*) get_free_page()))
return
-1;
/* Out of memory, see freeing */
*to_dir = ((
unsigned
long
) to_page_table) | 7; //设置地址项的标记位
/*设置要复制的页表项的个数。如果是从内核处进行复制,那么只需要复制160项。
如果是从一般的用户进程处进行复制,那么需要复制全部的1024项。
至于为什么对内核的时候只需要复制160项,可能是与内核本身所占的空间的大小有关
*/
nr = (from==0)?0xA0:1024;
for
( ; nr-- > 0 ; from_page_table++,to_page_table++) {
this_page = *from_page_table; //对应的物理页在来源的地址中没有被用到
if
(!(1 & this_page))
continue
;
this_page &= ~2; //将R/W位设成0。设置成只读,是与Linux的写时复制有关
*to_page_table = this_page;
/*LOW_MEM=0x100000=2^20=1M。1M以下是内核代码页面。
如果此地址在1M以上,就需要设置内存页面映射数组*/
if
(this_page > LOW_MEM) {
*from_page_table = this_page; //将来源的项的R/W位设成0
this_page -= LOW_MEM;
this_page >>= 12; //定位页的时候,低12可以不需要
mem_map[this_page]++; //引用次数加1
}
}
}
invalidate();
return
0;
}