(Minix 3.1.0)
Minix 3 不支持頁式存儲,進程在整個執行過程中,所占用內存的大小和位置不會變化。
它的內存管理工作,主要是:
1.調用 fork() 創建新進程時,為新進程申請內存;
2.調用 exec() 等更換內存鏡像時,釋放舊鏡像占用的內存,為新鏡像申請內存(先釋放,后申請);
3.進程結束(不管是自己退出,還是被殺死)時,釋放其所占用的內存;
4.系統初始化時,系統進程申請內存。
下面不考慮系統初始化時的內存管理工作。
在 Minix 3 中,由進程管理器PM管理內存。
Minix 3 中的程序可以被編譯為指令和數據不分離的形式,此時一個進程的正文,數據,堆棧共用一塊作為一個整體申請或釋放的內存;
也可以被編譯為指令和數據分離的形式,兩部分占用的內存分別管理。
指令和數據分離的好處在于可以通過共享正文來節約內存:
1.當一個進程調用 fork() 來創建新進程時,只需要為新進程的數據和堆棧申請內存,而正文可以共享。
2.當一個進程調用 exec() 等更換內存鏡像時,系統進程表會被搜索,以確定需要的正文是否已經由其它進程載入內存,
若已經載入,則該正文被共享,就只需為新鏡像的數據和堆棧申請內存。
3.進程結束時,它的數據和堆棧所占用的內存會被釋放,而正文所占用的內存,只有在該正文沒有被其它進程共享時才會釋放。
Minix 3 雖不支持頁式存儲,但是提供一些用于交換的函數,由文件
/include/minix/config.h 中的宏 ENABLE_SWAP 決定是否編譯進系統。
進程管理器PM管理內存的主要數據結構為 struct hole,
見 /servers/pm/alloc.c :
PRIVATE struct hole {
struct hole *h_next; /* pointer to next entry on the list */
phys_clicks h_base; /* where does the hole begin? */
phys_clicks h_len; /* how big is the hole? */
} hole[ NR_HOLES ];
使用單鏈表,按內存地址由低到高記錄內存塊。注意,進程中數據和堆棧間的空隙被認為已經分配給了進程,而不被記錄。
鏈表節點所占用的內存,不是動態申請釋放的,而是定義了數組 hole,為鏈表節點提供內存。
見該文件接下來的定義,
PRIVATE struct hole *hole_head; /* pointer to first hole */
PRIVATE struct hole *free_slots; /* ptr to list of unused table slots */
hole_head 一個單鏈表頭指針,此鏈表用于維護尚未分配的內存塊,按內存塊地址由低到高記錄。
free_slots 一個單鏈表頭指針,此鏈表用于維護數組 hole 中尚未被 hole_head 使用的節點。
mem_init() 對內存管理的相關數據進行初始化。
文件 /servers/pm/main.c 中的 main() 以適當的參數調用 mem_init() 完成內存管理的初始化。
alloc_mem() 申請內存。
當申請一定大小的內存時,遍歷鏈表 hole_head ,直至找到足夠大的內存塊,從中剪切出所需大小。
即首次適應算法。
free_mem() 釋放內存。
與 alloc_mem() 功能相反,而且釋放內存后,還會調用 merge() 合并相鄰的內存塊。
merge() 合并連續的未分配內存塊。
本來連續的內存,經過若干次內存申請與釋放后,可能被切成多塊,由鏈表 hole_head 中的多個節點記錄,
每次釋放內存后調用 merge() 以避免此情況出現。
del_slot() 將一個鏈表節點從 hole_head 中刪除,移入 free_slots 。
因為鏈表 hole_head 中的節點所占用的內存不是動態申請釋放的,而是定義了數組,
需要新節點時,就從數組中取,所以需要一些相關的維護工作。