(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 中的节点所占用的内存不是动态申请释放的,而是定义了数组,
需要新节点时,就从数组中取,所以需要一些相关的维护工作。