内存管理有两个非常关键和基本的数据结构:进程表、空洞表。
1.内存管理器的进程表叫:mproc。/src/mm/mproc.h中
定义如下:
EXTERN struct mproc {
struct mem_map mp_seg[NR_SEGS];/* points to text, data, stack */
char mp_exitstatus; /* storage for status when process exits */
char mp_sigstatus; /* storage for signal # for killed procs */
pid_t mp_pid; /* process id */
pid_t mp_procgrp; /* pid of process group (used for signals) */
pid_t mp_wpid; /* pid this process is waiting for */
int mp_parent; /* index of parent process */
/* Real and effective uids and gids. */
uid_t mp_realuid; /* process' real uid */
uid_t mp_effuid; /* process' effective uid */
gid_t mp_realgid; /* process' real gid */
gid_t mp_effgid; /* process' effective gid */
/* File identification for sharing. */
ino_t mp_ino; /* inode number of file */
dev_t mp_dev; /* device number of file system */
time_t mp_ctime; /* inode changed time */
/* Signal handling information. */
sigset_t mp_ignore; /* 1 means ignore the signal, 0 means don't */
sigset_t mp_catch; /* 1 means catch the signal, 0 means don't */
sigset_t mp_sigmask; /* signals to be blocked */
sigset_t mp_sigmask2; /* saved copy of mp_sigmask */
sigset_t mp_sigpending; /* signals being blocked */
struct sigaction mp_sigact[_NSIG + 1]; /* as in sigaction(2) */
vir_bytes mp_sigreturn; /* address of C library __sigreturn function */
/* Backwards compatibility for signals. */
sighandler_t mp_func; /* all sigs vectored to a single user fcn */
unsigned mp_flags; /* flag bits */
vir_bytes mp_procargs; /* ptr to proc's initial stack arguments */
} mproc[NR_PROCS];
其中最重要的就是struct mem_map mp_seg[NR_SEGS];数组,此数组定义了可执行文件在内存中的分布状况。其中#define NR_SEGS 3;分别是
#define T 0 /* proc[i].mem_map[T] is for text ,正文*/
#define D 1 /* proc[i].mem_map[D] is for data ,数据段*/
#define S 2 /* proc[i].mem_map[S] is for stack,堆栈段*/
struct mem_map 结构定义如下:
struct mem_map {
vir_clicks mem_vir; /* virtual address 虚地址*/
phys_clicks mem_phys; /* physical address 物理地址*/
vir_clicks mem_len; /* length 长度*/
};
S段即堆栈段的虚地址是:堆栈的物理地址和数据段的物理地址之差。
2.空洞表定义在/src/mm/allo.c中,定义如下:
PRIVATE struct hole {
phys_clicks h_base; /* where does the hole begin? */
phys_clicks h_len; /* how big is the hole? */
struct hole *h_next; /* pointer to next entry on the list */
} hole[NR_HOLES];
此结构是个静态结构,即在二进制可执行文件中就存在,在系统启动后就会加载到内存中。这种结构就是我们在传统的操作系统的课程中学到的最简单的内存管理结构。在Minix中采用最佳适配算法。h_base指向空洞的起始地址。h_len指示空洞的大小。*h_next指向下一个空洞。图像如下图所示:
父进程调用系统调用fork创建新进程,fork会调用alloc_mem从空洞表中分配一块合适大小的内存,所谓的分配内存就是选取空洞表的某一表项,从h_base所指向的内存地址开始,分配合适大小的内存给子进程。如果此空洞表项远比需要分配的内存大,则将表项剩下的空间又用一表项划出。调用alloc_mem函数后,父进程将mp_seg[NR_SEGS];数组中的三个表项分别赋值,并将父进程的数据和堆栈复制到子进程中去,并设置mproc内存进程表中相应的表项。
当子进程需要运行新的可执行文件时,调用exec系统调用为新的可执行文件分配新的内存,然后将老内存中的栈中的数据复制到新的内存栈中,通过读取可执行文件的头来判定可执行文件的合法性,并判断内存中是否已存在该可执行文件的镜像,如果有则共享,如果没有,则调用lseek函数读取可执行文件中的正文段和数据段到新分配内存的正文段和数据段中。并设置内存进程表mproc表项中的相应项。
当进程退出时调用mm_exit函数,此函数位于/src/mm/Forkexit.c中,此函数首先查看当前进程的正文段是否有其他进程在共享,如果没有则调用free_mem函数释放内存,所谓的是否内存,不过是按照最佳适配算法,对内存空洞表的一些简单操作而已。之后调用free_mem释放数据段和栈空间。如果当前退出的进程的父节点正在等待其退出,即处于WAITING状态,则调用cleanup()函数清除当前进程mproc表项,并通知父进程,并将父进程的等待状态取消。接下来遍历整个mproc[]数组,如果找到当前进程的子进程,则将其父进程改变为INIT进程,如果当前进程的父进程处于WAITING状态,且当前进程的子进程处于HANGING即挂起状态,则对子进程调用cleanup()函数,清除其表项.
阅读(1750) | 评论(0) | 转发(0) |