参照《操作系统:设计与实现》。很多文字是从此书中直接摘抄下来的。之所以摘抄此文是为了加深自己的印象。
在创建和撤销进程时必须分配或释放内存、必须更新进程表,包括由内核和FS保存的部分。进程创建是由FORK完成的。执行步骤如下:
1.检查进程表是否满了。
2.试为子进程的数据和堆栈分配内存。
3.把父进程的数据和堆栈复制到子进程的内存中。
4.找到一个空闲的进程表项并把父进程的表项复制进去。
5.在进程表中输入子进程的内存映像。
6.为子进程选择一个子进程号。
7.告诉内核和文件系统子进程的情况。
8.向内核报告子进程的内存映像。
9.向父进程和子进程发送应答信息。
fork函数位于/src/mm/forkexit.c中。
源代码如下:
PUBLIC int do_fork()
{
/* The process pointed to by 'mp' has forked. Create a child process. */
register struct mproc *rmp; /* pointer to parent */
register struct mproc *rmc; /* pointer to child */
int i, child_nr, t;
phys_clicks prog_clicks, child_base = 0;
phys_bytes prog_bytes, parent_abs, child_abs; /* Intel only */
/* If tables might fill up during FORK, don't even start since recovery half
* way through is such a nuisance.
*/
rmp = mp;
/*判段启动的进程数是否超过了最大数*/
if (procs_in_use == NR_PROCS) return(EAGAIN);
if (procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0)return(EAGAIN);
/* Determine how much memory to allocate. Only the data and stack need to
* be copied, because the text segment is either shared or of zero length.
* 栈的虚地址就是栈的起始物理地址与栈的数据段的起始地址之差,此差即为数据段的大小,此数
* 值加上栈的长度即为总共的数据区的大小,栈的长度即mp_seg[s].mem_len。新的正文段或为零或与
* 其他进程映像共享。如果为零,那么需要在执行exec系统调用时为其分配正文段。
*/
prog_clicks = (phys_clicks) rmp->mp_seg[S].mem_len;
prog_clicks += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
#if (SHADOWING == 0)
prog_bytes = (phys_bytes) prog_clicks << CLICK_SHIFT;
#endif
/*调用alloc_mem从内存空洞表中为新的进程分配内存*/
if ( (child_base = alloc_mem(prog_clicks)) == NO_MEM) return(ENOMEM);
#if (SHADOWING == 0)
/* Create a copy of the parent's core image for the child.
* 将父进程的数据区和堆栈区中的内容拷贝给新进程
*/
child_abs = (phys_bytes) child_base << CLICK_SHIFT;
parent_abs = (phys_bytes) rmp->mp_seg[D].mem_phys << CLICK_SHIFT;
i = sys_copy(ABS, 0, parent_abs, ABS, 0, child_abs, prog_bytes);
if (i < 0) panic("do_fork can't copy", i);
#endif
/* Find a slot in 'mproc' for the child process. A slot must exist.
* 为新进程找到一个mproc内存进程表项。
*/
for (rmc = &mproc[0]; rmc < &mproc[NR_PROCS]; rmc++)
if ( (rmc->mp_flags & IN_USE) == 0) break;
/* Set up the child and its memory map; copy its 'mproc' slot from parent.
* 设置表项。
*/
child_nr = (int)(rmc - mproc); /* slot number of the child */
procs_in_use++;
*rmc = *rmp; /* copy parent's process slot to child's */
rmc->mp_parent = who; /* record child's parent */
rmc->mp_flags &= ~TRACED; /* child does not inherit trace status */
#if (SHADOWING == 0)
/* A separate I&D child keeps the parents text segment. The data and stack
* segments must refer to the new copy.
* 设置子进程mp_seg[]表项中的数据段和栈,指向新的内存区,由于此处的数据段和正文段并未分开
* ,而我们上面看到数据区中的内容完全是从父进程中拷贝过来的,所以mp_seg[T].mem_phys中指
* 的新内存中的正文段中的内容和父进程中正文段中的内容是一致的。
*/
if (!(rmc->mp_flags & SEPARATE)) rmc->mp_seg[T].mem_phys = child_base;
rmc->mp_seg[D].mem_phys = child_base;
rmc->mp_seg[S].mem_phys = rmc->mp_seg[D].mem_phys +
(rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
#endif
rmc->mp_exitstatus = 0;
rmc->mp_sigstatus = 0;
/* Find a free pid for the child and put it in the table.找到一个未用的pid */
do {
t = 0; /* 't' = 0 means pid still free */
next_pid = (next_pid < 30000 ? next_pid + 1 : INIT_PID + 1);
for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++)
if (rmp->mp_pid == next_pid || rmp->mp_procgrp == next_pid) {
t = 1;
break;
}
rmc->mp_pid = next_pid; /* assign pid to child */
} while (t);
/* Tell kernel and file system about the (now successful) FORK. */
sys_fork(who, child_nr, rmc->mp_pid, child_base); /* child_base is 68K only*/
tell_fs(FORK, who, child_nr, rmc->mp_pid);
#if (SHADOWING == 0)
/* Report child's memory map to kernel. */
sys_newmap(child_nr, rmc->mp_seg);
#endif
/* Reply to child to wake it up.向子进程发送消息,使其启动*/
reply(child_nr, 0, 0, NIL_PTR);
return(next_pid); /* child's pid ,返回到父进程中*/
}
阅读(1277) | 评论(0) | 转发(0) |