Linux-0.11内核源码分析系列:内存管理try_to_share()与share_page()函数分析

/*   
 *Author  : DavidLin   
 *Date    : 2014-11-22pm   
 *Email   : [email protected] or [email protected]   
 *world   : the city of SZ, in China   
 *Ver     : 000.000.001   
 *history :     editor      time            do   
 *          1)LinPeng       2014-11-22      created this file!   
 *          2)   
 */ 

/*
 * try_to_share()检查进程P中地址”address"是否存在
 * 如果存在且是没有被污染的页面,将该页面与当前进程共享
 *
 * 注意,前提条件是进程p不是当前进程,而且它们共享同样的执行文件
 */

/*
 * try_to_share() checks the page at address "address" in the task "p",
 * to see if it exists, and if it is clean. If so, share it with the current
 * task.
 *
 * NOTE! This assumes we have checked that p != current, and that they
 * share the same executable.
 */
static int try_to_share(unsigned long address, struct task_struct * p)
{
	unsigned long from;         //管理源页表
	unsigned long to;           //管理目的页表
	unsigned long from_page;    //管理源页表项
	unsigned long to_page;      //管理目的页表项
	unsigned long phys_addr;    //管理源物理地址

	from_page = to_page = ((address>>20) & 0xffc);  //获取页表地址
	from_page += ((p->start_code>>20) & 0xffc);     //from_page加上进程P
                                                        //的数据段地址
                                                        //取得4G空间地址
	to_page += ((current->start_code>>20) & 0xffc); //to_page加上当前进程
                                                        //的数据段地址
                                                        //取得4G空间地址
        //注意:address指的是进程中0-64M的相对地址,要计算主内存区的数组偏移,
        //必须加上任务代码段(数据段)的偏移地址,才可以获取4G空间偏移地址
/* is there a page-directory at from? */                
	from = *(unsigned long *) from_page;    //from是from_page的页目录项
                                                //页目录项管理着页表
                                                //即页表的值保存在页目录项中
	if (!(from & 1))    //如果该目录项无效,即源页表也是无效
		return 0;   //返回0,表示失败
	from &= 0xfffff000; //取得源页表
	from_page = from + ((address>>10) & 0xffc);  //此处from_page指向页框
                                                     //即页表内偏移地址
        
	phys_addr = *(unsigned long *) from_page;    //取得源物理页地址
/* is the page clean and present? */
	if ((phys_addr & 0x41) != 0x01)    //如果源物理页不干净或者不存在
		return 0;                  //返回0表示失败
	phys_addr &= 0xfffff000;
	if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)  
		return 0;    //如果源物理地址不在主内存区,返回0表示失败
	to = *(unsigned long *) to_page;    //取得目的页目录项
	if (!(to & 1))                      //如果目的页目录项P位无效
		if (to = get_free_page())   //申请一块物理页用来保存目的页表
			*(unsigned long *) to_page = to | 7;//将页表挂到页目录项
		else
			oom();//如果无法申请到空闲物理页,内存溢出报警,die
	to &= 0xfffff000;     //取得目的页表
	to_page = to + ((address>>10) & 0xffc);  //to_page等于目的页表的表内偏移
                                                 //即to_page[0--1023]中的索引值
        
	if (1 & *(unsigned long *) to_page)  //如果目的页表项已经存在有效,die
		panic("try_to_share: to_page already exists");
/* share them: write-protect */
	*(unsigned long *) from_page &= ~2;  //源页表项设置为只读
	*(unsigned long *) to_page = *(unsigned long *) from_page;  //共享
	invalidate();            //刷新交换高速缓存
	phys_addr -= LOW_MEM;    
	phys_addr >>= 12;
	mem_map[phys_addr]++;    //主内存区相应物理页引用加1
	return 1;                //返回1表示共享成功
}

/* share_page()函数试图找到一个进程,该进程可以和当前进程共享某个页面
 * 参数address是当前进程中数据空间的某页面,即把数据空间的起始地址当做
 * 0地址的相对地址
 * 
 * 通过检查当前进程的executable->i_count,因为可执行文件被不同进程引用时会++1,
 * 所以如果executable->i_count > 1,表示除了当前进程,还有其他进程使用该文件
 * 如果上述条件成立,当前进程就试图与其他进程共享该页
 */
/*
 * share_page() tries to find a process that could share a page with
 * the current one. Address is the address of the wanted page relative
 * to the current data space.
 *
 * We first check if it is at all feasible by checking executable->i_count.
 * It should be >1 if there are other tasks sharing this inode.
 */
static int share_page(unsigned long address)
{
	struct task_struct ** p;    //指向进程指针的指针

	if (!current->executable)   //如果没有对应执行文件,返回
		return 0;
	if (current->executable->i_count < 2)    //如果只有当前进程引用该文件
		return 0;                        //返回
	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {    //遍历进程管理数组
		if (!*p)    //如果进程不存在,继续
			continue;
		if (current == *p)    //如果进程等于当前进程
			continue;     //继续
		if ((*p)->executable != current->executable)
			continue;     //如果执行文件不同
		if (try_to_share(address,*p))    //试图与找到的进程共享该页      
			return 1;                //返回1表示成功                
	}
	return 0;    //如果上述无法找到进程共享页面,返回0表示失败
}


你可能感兴趣的:(linux,kernel)