页写保护

来源:互联网 发布:linux服务器开通外网 编辑:程序博客网 时间:2024/05/01 22:31

    1、进程A和进程B共享页面,代码如下:

if (!(pid=fork())) {压栈操作;//子进程B}if (pid>0){    压栈操作;//父进程A}
    

    2、我们假设现在系统有一个用户进程A,他自己对应的程序代码已经载入内存中,此时该进程内存中所占用的页面引用计数都为”1“,接下来他开始执行,通过调用fork函数创建一个新进程(进程B)。在新进程创建的过程中,系统将进程A的页表项全部复制给进程B,并设置进程B的页目录项。此时这两个进程就共享页面,被共享页面的引用计数累加为2,并将此共享页面全部设置为”只读“属性,即无论是进程A还是进程B,都只能对这些共享的页面进程读操作,而不能进行写操作。执行代码如下:

int copy_page_tables(unsigned long from,unsigned long to,long size){......for( ; size-->0 ; from_dir++,to_dir++) {......for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {this_page = *from_page_table;if (!(1 & this_page))continue;this_page &= ~2;//进程A对页面的操作属性被设置为只读*to_page_table = this_page;//进程B对页面的操作属性被设置为只读if (this_page > LOW_MEM) {*from_page_table = this_page;this_page -= LOW_MEM;this_page >>= 12;mem_map[this_page]++;//引用计数记录在mem_map中,累加为2}}}invalidate();return 0;}

    3、我们假设接下来轮到进程A执行,进程A接下来的动作是一个压栈动作,现在看看会发生什么。

    现在进程A的程序对应的所有页面都是只读状态的。压栈是个写操作,所以会产生”页写保护“异常,”页写保护“中断对应的服务程序时up_wp_page()函数。

    函数执行时,先要在主内存中申请一个空闲页面(以后我们称之为新页面),以便备份刚才压栈的位置所在页面(以后我们称之为原页面)的全部数据,然后将原页面的引用计数减1。

    代码如下:

void un_wp_page(unsigned long * table_entry)//页表项,现在指向原页面{unsigned long old_page,new_page;old_page = 0xfffff000 & *table_entry;......if (!(new_page=get_free_page()))//申请到新页面oom();if (old_page >= LOW_MEM)mem_map[MAP_NR(old_page)]--;//页面引用计数递减1*table_entry = new_page | 7; //7的二进制形式为111,标志着新页面可读可写了,将进程A的页表项指向新申请的页面invalidate();copy_page(old_page,new_page);//复制原页面的内容到进程A新申请的页面}

    4、将进程A的页表项指向新申请的页面,代码是*table_entry = new_page | 7;


    5、复制原页面的内容到进程A新申请的页面,代码是copy_page(old_page,new_page);如下图:



    6、进程A在用户空间开始执行压栈,仿佛什么都没有发生,压栈的数据却已经压入了新申请的页面。


    7、进程A执行一段时间后,时钟中断就该轮到他的子进程--进程B执行了,进程B仍然使用着原页面。进行压栈操作,也会出现”页写保护“异常,执行up_wp_page()函数。由于原页面的引用计数已经被削减为1了,所以现在就要将原页面的属性设置为”可读可写“,如下:

void un_wp_page(unsigned long * table_entry){unsigned long old_page,new_page;old_page = 0xfffff000 & *table_entry;if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {//发现原页面引用计数为1,不用共享了*table_entry |= 2;//2的二进制形式为010,R/W位被设置为1,可读可写invalidate();return;}if (!(new_page=get_free_page()))oom();if (old_page >= LOW_MEM)mem_map[MAP_NR(old_page)]--;*table_entry = new_page | 7;invalidate();}


    进程B在用户空间开始执行压栈,仿佛什么都没有发生,压栈的数据却已经压入了原页面。

    最后形成如下图:



    8、进程A和进程B在压栈数据的处理方面可以操作不同的页面了。这些页面都是可读可写的,而且引用计数都为1,以后彼此都不会干扰对方。
    现在进程B并没有自己的程序,如果将来它有了自己的程序,就会和原页面解除关系,原页面的引用计数将会继续减1,于是变成0,系统将认定他为”空闲页面“,可以重新用于分配。

1 0
原创粉丝点击