日期:2014-05-16  浏览次数:20551 次

Linux内核设计的艺术-用户进程与内存管理、缓冲区和多进程操作文件

1、用户进程与内存管理

        父子进程共享同一页面,如果这个页面设置为可写状态,那么两个进程同时写一个页面会造成混乱,所以此时页表表项后三位设置为101,U/S=1,

R/W=0,P=1。对应代码如下:

        代码路径:mm/memory.c

int copy_page_tables(unsigned long from,unsigned long to,long size)
{
	...
	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 */
		*to_dir = ((unsigned long) to_page_table) | 7;
		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,~2为101
			*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]++;//父子进程共享,引用计数记录在mem_map中,累加为2
	...
}

       如果父子进程都要写页面,该怎么办呢?

       假设进程A是父进程,B是子进程。进程A要往共享页面写数据,因为该页面为只读,会产生页写保护,页写保护为进程A申请新页面,并且引用计数递

1,并让进程A的页表中指向原页面的页表项改为指向新页面,并将其使用的属性从“只读”改变为“可读可写”,一切准备就绪后,将原页面中的内容复制到页面中。具体结果如下图:

       

        进程A执行一段时间后,就该轮到它的子进程-进程B执行了。进程B仍然使用着原页面。假设也要在原页面总进程写操作,但是现在原页面的属性仍然

是“只读”的,这一点在进程A创建B时就是这样设置的,一直都没有改变过。所以在这种情况下,又需要进程页写保护,由于原页面的引用计数已经被消减为1了 ,所以现在就要将院页面的属性设置为“可读可写”。如下图:

        

         页写保护,代码路径:mm/memory.c

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;//010,R/W位设置为1,可读可写
		invalidate();
		return;
	}
	if (!(new_page=get_free_page()))//申请到新页面
		oom();
	if (old_page >= LOW_MEM)
		mem_map[MAP_NR(old_page)]--;//页面引用计数递减1
	*table_entry = new_page | 7;//111,标志着新页面可读可写了
	invalidate();
	copy_page(old_page,new_page);//将原页面内容复制给进程A新申请的页面
}	
        


        进程切换,一是时间片到了,切换,且只有在3特权级下才能切换,0特权级下不能切换;二是等待硬盘读盘时,要切换到其他进程。

        代码路径:kernel/sched.c