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表示失敗
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章