?? memory.c
字號:
oom (); }}/** 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.*//** try_to_share()在任務"p"中檢查位于地址"address"處的頁面,看頁面是否存在,是否干凈。* 如果是干凈的話,就與當前任務共享。** 注意!這里我們已假定p !=當前任務,并且它們共享同一個執行程序。*///// 嘗試對進程指定地址處的頁面進行共享操作。// 同時還驗證指定的地址處是否已經申請了頁面,若是則出錯,死機。// 返回1-成功,0-失敗。static inttry_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);// 計算進程p 的代碼起始地址所對應的頁目錄項。 from_page += ((p->start_code >> 20) & 0xffc);// 計算當前進程中代碼起始地址所對應的頁目錄項。 to_page += ((current->start_code >> 20) & 0xffc);/* is there a page-directory at from? *//* 在from 處是否存在頁目錄?*/// *** 對p 進程頁面進行操作。// 取頁目錄項內容。如果該目錄項無效(P=0),則返回。否則取該目錄項對應頁表地址??from。 from = *(unsigned long *) from_page; if (!(from & 1)) return 0; from &= 0xfffff000;// 計算地址對應的頁表項指針值,并取出該頁表項內容??phys_addr。 from_page = from + ((address >> 10) & 0xffc); phys_addr = *(unsigned long *) from_page;/* is the page clean and present? *//* 頁面干凈并且存在嗎?*/// 0x41 對應頁表項中的Dirty 和Present 標志。如果頁面不干凈或無效則返回。 if ((phys_addr & 0x41) != 0x01) return 0;// 取頁面的地址??phys_addr。如果該頁面地址不存在或小于內存低端(1M)也返回退出。 phys_addr &= 0xfffff000; if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM) return 0;// *** 對當前進程頁面進行操作。// 取頁目錄項內容??to。如果該目錄項無效(P=0),則取空閑頁面,并更新to_page 所指的目錄項。 to = *(unsigned long *) to_page; if (!(to & 1)) if (to = get_free_page ()) *(unsigned long *) to_page = to | 7; else oom ();// 取對應頁表地址??to,頁表項地址??to_page。如果對應的頁面已經存在,則出錯,死機。 to &= 0xfffff000; to_page = to + ((address >> 10) & 0xffc); if (1 & *(unsigned long *) to_page) panic ("try_to_share: to_page already exists");/* share them: write-protect *//* 對它們進行共享處理:寫保護 */// 對p 進程中頁面置寫保護標志(置R/W=0 只讀)。并且當前進程中的對應頁表項指向它。 *(unsigned long *) from_page &= ~2; *(unsigned long *) to_page = *(unsigned long *) from_page;// 刷新頁變換高速緩沖。 invalidate ();// 計算所操作頁面的頁面號,并將對應頁面映射數組項中的引用遞增1。 phys_addr -= LOW_MEM; phys_addr >>= 12; mem_map[phys_addr]++; return 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.*//** share_page()試圖找到一個進程,它可以與當前進程共享頁面。參數address 是* 當前數據空間中期望共享的某頁面地址。** 首先我們通過檢測executable->i_count 來查證是否可行。如果有其它任務已共享* 該inode,則它應該大于1。*///// 共享頁面。在缺頁處理時看看能否共享頁面。// 返回1 - 成功,0 - 失敗。static intshare_page (unsigned long address){ struct task_struct **p;// 如果是不可執行的,則返回。excutable 是執行進程的內存i 節點結構。 if (!current->executable) return 0;// 如果只能單獨執行(executable->i_count=1),也退出。 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) // 如果executable 不等,也繼續。 continue; if (try_to_share (address, *p)) // 嘗試共享頁面。 return 1; } return 0;}// 參數error_code 是由CPU 自動產生,address 是頁面線性地址。voiddo_no_page (unsigned long error_code, unsigned long address){ int nr[4]; unsigned long tmp; unsigned long page; int block, i; address &= 0xfffff000; // 頁面地址。// 首先算出指定線性地址在進程空間中相對于進程基址的偏移長度值。 tmp = address - current->start_code;// 若當前進程的executable 空,或者指定地址超出代碼+數據長度,則申請一頁物理內存,并映射// 影射到指定的線性地址處。executable 是進程的i 節點結構。該值為0,表明進程剛開始設置,// 需要內存;而指定的線性地址超出代碼加數據長度,表明進程在申請新的內存空間,也需要給予。// 因此就直接調用get_empty_page()函數,申請一頁物理內存并映射到指定線性地址處即可。// start_code 是進程代碼段地址,end_data 是代碼加數據長度。對于linux 內核,它的代碼段和// 數據段是起始基址是相同的。 if (!current->executable || tmp >= current->end_data) { get_empty_page (address); return; }// 如果嘗試共享頁面成功,則退出。 if (share_page (tmp)) return;// 取空閑頁面,如果內存不夠了,則顯示內存不夠,終止進程。 if (!(page = get_free_page ())) oom ();/* remember that 1 block is used for header *//* 記住,(程序)頭要使用1 個數據塊 */// 首先計算缺頁所在的數據塊項。BLOCK_SIZE = 1024 字節,因此一頁內存需要4 個數據塊。 block = 1 + tmp / BLOCK_SIZE;// 根據i 節點信息,取數據塊在設備上的對應的邏輯塊號。 for (i = 0; i < 4; block++, i++) nr[i] = bmap (current->executable, block);// 讀設備上一個頁面的數據(4 個邏輯塊)到指定物理地址page 處。 bread_page (page, current->executable->i_dev, nr);// 在增加了一頁內存后,該頁內存的部分可能會超過進程的end_data 位置。下面的循環即是對物理// 頁面超出的部分進行清零處理。 i = tmp + 4096 - current->end_data; tmp = page + 4096; while (i-- > 0) { tmp--; *(char *) tmp = 0; }// 如果把物理頁面映射到指定線性地址的操作成功,就返回。否則就釋放內存頁,顯示內存不夠。 if (put_page (page, address)) return; free_page (page); oom ();}//// 物理內存初始化。// 參數:start_mem - 可用作分頁處理的物理內存起始位置(已去除RAMDISK 所占內存空間等)。// end_mem - 實際物理內存最大地址。// 在該版的linux 內核中,最多能使用16Mb 的內存,大于16Mb 的內存將不于考慮,棄置不用。// 0 - 1Mb 內存空間用于內核系統(其實是0-640Kb)。voidmem_init (long start_mem, long end_mem){ int i; HIGH_MEMORY = end_mem; // 設置內存最高端。 for (i = 0; i < PAGING_PAGES; i++) // 首先置所有頁面為已占用(USED=100)狀態, mem_map[i] = USED; // 即將頁面映射數組全置成USED。 i = MAP_NR (start_mem); // 然后計算可使用起始內存的頁面號。 end_mem -= start_mem; // 再計算可分頁處理的內存塊大小。 end_mem >>= 12; // 從而計算出可用于分頁處理的頁面數。 while (end_mem-- > 0) // 最后將這些可用頁面對應的頁面映射數組清零。 mem_map[i++] = 0;}// 計算內存空閑頁面數并顯示。voidcalc_mem (void){ int i, j, k, free = 0; long *pg_tbl;// 掃描內存頁面映射數組mem_map[],獲取空閑頁面數并顯示。 for (i = 0; i < PAGING_PAGES; i++) if (!mem_map[i]) free++; printk ("%d pages free (of %d)\n\r", free, PAGING_PAGES);// 掃描所有頁目錄項(除0,1 項),如果頁目錄項有效,則統計對應頁表中有效頁面數,并顯示。 for (i = 2; i < 1024; i++) { if (1 & pg_dir[i]) { pg_tbl = (long *) (0xfffff000 & pg_dir[i]); 10.5 page.s
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -