linux0.11記憶體管理 描述linux 0.11的記憶體管理主要內容。 1:記憶體初始化linux 0.11最大支援16MB的實體記憶體。main函數和mem_init函數對記憶體進行了初始化。主要使用數組mem_map[]來標記相應的記憶體頁是否被佔用。 memory_end是用BIOS中斷調用得到的實際記憶體大小。if (memory_end > 16 * 1024 * 1024) memory_end = 16 * 1024 * 1024; # 因此最大隻支援16MB記憶體if (memory_end > 12*1024*1024) buffer_memory_end = 4 * 1024 * 1024; # buffer_memory_end為快取末端地址,其大小於機器總記憶體大小相關else if (memory_end > 6 * 1024 * 1024) buffer_memory_end = 2 * 1024 * 1024else buffer_memory_end = 1*1024*1024 main_memory_start = buffer_memory_end;mem_init(main_memory_start, memory_end); 在mem_init中會對mem_map[]數組進行初始化。在1MB~16MB之間,共有(15 * 1024 * 1024) >> 12 = 3840頁。定義數組mem_map[3840]對應於這段記憶體的每一頁,在main_memory_start和memory_end之間的頁,相應的mem_map[i]的值初始化為0,表示未使用,其餘的項初始化為100,表示被佔用。 2:基本頁面分配函數有幾個基本的頁面分配和釋放的函數。get_free_page():返回一個空閑頁面的物理地址, 該函數就是在mem_map數組中尋找值為0的項,然後轉換成頁面物理地址返回。free_page(phy_addr):釋放phy_addr指向的頁面, 釋放操作就是將phy_addr在mem_map中對應項的值進行減1。get_empty_page(line_addr):該函數的參數指定的是線性地址,要求獲得一頁實體記憶體,並用line_addr指向這頁記憶體。 void get_empty_page(unsigned long address){ unsigned long tmp; if (!(tmp=get_free_page()) || !put_page(tmp, address)) { free_page(tmp); oom(); # 記憶體不夠 }}明顯該函數先調用get_free_page獲得一頁實體記憶體, 然後用put_page對這頁實體記憶體的物理地址和線性地址之間建立映射。 建立映射的過程: 對於32位的線性地址, 高10位表示頁目錄索引, 中間10位為頁表索引, 低12位為頁內位移。因此給定一個線性地址,我們就能通過頁目錄基地址和線性地址高10位來確定它的頁目錄項。在Linux 0.11中,頁目錄基地址就是0。 put_page: page_table = (unsigned long *) ((line_addr >> 20) & 0xffc) # 獲得頁目錄項的指標 如果該頁目錄項所指向的頁表是存在的, 則利用線性地址的中間10位,定位到頁表中的相應頁表項,並將物理地址儲存進去即可建立映射。 若頁表不存在,則用get_free_page首先分配一頁作為頁表,再去建立映射。 不管是建立映射還是進行頁表拷貝,都是先考慮頁目錄,再考慮頁表。 3:sys_fork與copy_page_tables函數在sys_fork時會調用copy_process,copy_process中會調用copy_mem函數, 該函數會將父進程的頁表拷貝給子進程,這是父進程和子進程會共用相應的代碼和資料區段,只有當父進程或子進程對共用的記憶體進行寫操作時,才會為子進程分配記憶體,即為寫時複製。 copy_mem是調用copy_page_table進行頁表拷貝的。 copy_page_table(old_data_base, new_data_base, data_limit)將父進程的線性地址old_data_base ~ old_data_base+data_limit對應的頁表,拷貝給子進程。在拷貝過程中,將每個頁表項設定為唯讀, 並且執行相應的mem_map[i]++(相當於添加引用計數) 4:頁出錯異常處理有兩種不同的頁出錯:i)頁表項指向的頁不存在,即頁表項的存在位的值為0.ii)頁保護機制。 寫唯讀頁面時出錯。 但出現頁錯誤時,會發生int 14中斷。系統會執行_page_fault:異常處理代碼。該代碼會根據兩種不同的頁錯誤,分別執行do_no_page和do_wp_page do_wp_page(error_code, address)該函數主要是實現了寫時複製功能, 在copy_page_table時,將父進程和子進程的頁表都設定成了唯讀,當訪問了其中一個頁面後,會觸發中斷並執行do_wp_page函數。此函數會分配一頁新的實體記憶體, 並將相應的頁表項設成可讀寫。 do_no_page(error_code, address)該函數可處理兩種情況:i)在應用程式指派記憶體時,核心並不會實際分配實體記憶體, 只有在訪問相應記憶體時,才會分配。ii)在執行exec系列函數時,同樣只有在訪問相應記憶體時,才會去讀檔案,對於第一種情況, do_no_page直接調用get_empty_page函數,擷取一頁記憶體。對於第二種情況,有兩步過程。i)調用share_page函數。 該函數的主要目的是共用代碼和資料區段。 如果一個可執行檔,已經有一個進程執行個體在執行,那麼新進程可以和其他進程共用該可執行檔的代碼和資料區段。ii)如果只執行過一次該檔案, 那就先調用get_free_page函數,將檔案內容讀入記憶體,然後用put_page函數建立映射。