Minix中fork函数的实现

来源:互联网 发布:天猫历年双11数据分析 编辑:程序博客网 时间:2024/05/22 13:57

 

参照《操作系统:设计与实现》。很多文字是从此书中直接摘抄下来的。之所以摘抄此文是为了加深自己的印象。
在创建和撤销进程时必须分配或释放内存、必须更新进程表,包括由内核和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 ,返回到父进程中*/
}
原创粉丝点击