forkexit.c

Go to the documentation of this file.
00001 
00019 #include "pm.h"
00020 #include <sys/wait.h>
00021 #include <minix/callnr.h>
00022 #include <minix/com.h>
00023 #include <sys/resource.h>
00024 #include <signal.h>
00025 #include "mproc.h"
00026 #include "param.h"
00027 
00028 #define LAST_FEW            2   /* last few slots reserved for superuser */
00029 
00030 FORWARD _PROTOTYPE (void cleanup, (register struct mproc *child) );
00031 
00032 /*===========================================================================*
00033  *                              do_fork                                      *
00034  *===========================================================================*/
00035 PUBLIC int do_fork()
00036 {
00037 /* The process pointed to by 'mp' has forked.  Create a child process. */
00038   register struct mproc *rmp;   /* pointer to parent */
00039   register struct mproc *rmc;   /* pointer to child */
00040   int child_nr, s;
00041   phys_clicks prog_clicks, child_base;
00042   phys_bytes prog_bytes, parent_abs, child_abs; /* Intel only */
00043   pid_t new_pid;
00044   static int next_child;
00045   int n = 0, r;
00046 
00047  /* If tables might fill up during FORK, don't even start since recovery half
00048   * way through is such a nuisance.
00049   */
00050   rmp = mp;
00051   if ((procs_in_use == NR_PROCS) || 
00052                 (procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0))
00053   {
00054         printf("PM: warning, process table is full!\n");
00055         return(EAGAIN);
00056   }
00057 
00058   /* Determine how much memory to allocate.  Only the data and stack need to
00059    * be copied, because the text segment is either shared or of zero length.
00060    */
00061   prog_clicks = (phys_clicks) rmp->mp_seg[S].mem_len;
00062   prog_clicks += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
00063   prog_bytes = (phys_bytes) prog_clicks << CLICK_SHIFT;
00064   if ( (child_base = alloc_mem(prog_clicks)) == NO_MEM) return(ENOMEM);
00065 
00066   /* Create a copy of the parent's core image for the child. */
00067   child_abs = (phys_bytes) child_base << CLICK_SHIFT;
00068   parent_abs = (phys_bytes) rmp->mp_seg[D].mem_phys << CLICK_SHIFT;
00069   s = sys_abscopy(parent_abs, child_abs, prog_bytes);
00070   if (s < 0) panic(__FILE__,"do_fork can't copy", s);
00071 
00072   /* Find a slot in 'mproc' for the child process.  A slot must exist. */
00073   do {
00074         next_child = (next_child+1) % NR_PROCS;
00075         n++;
00076   } while((mproc[next_child].mp_flags & IN_USE) && n <= NR_PROCS);
00077   if(n > NR_PROCS)
00078         panic(__FILE__,"do_fork can't find child slot", NO_NUM);
00079   if(next_child < 0 || next_child >= NR_PROCS
00080  || (mproc[next_child].mp_flags & IN_USE))
00081         panic(__FILE__,"do_fork finds wrong child slot", next_child);
00082 
00083   rmc = &mproc[next_child];
00084   /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
00085   child_nr = (int)(rmc - mproc);        /* slot number of the child */
00086   procs_in_use++;
00087   *rmc = *rmp;                  /* copy parent's process slot to child's */
00088   rmc->mp_parent = who_p;                       /* record child's parent */
00089   /* inherit only these flags */
00090   rmc->mp_flags &= (IN_USE|SEPARATE|PRIV_PROC|DONT_SWAP);
00091   rmc->mp_child_utime = 0;              /* reset administration */
00092   rmc->mp_child_stime = 0;              /* reset administration */
00093 
00094   /* A separate I&D child keeps the parents text segment.  The data and stack
00095    * segments must refer to the new copy.
00096    */
00097   if (!(rmc->mp_flags & SEPARATE)) rmc->mp_seg[T].mem_phys = child_base;
00098   rmc->mp_seg[D].mem_phys = child_base;
00099   rmc->mp_seg[S].mem_phys = rmc->mp_seg[D].mem_phys + 
00100                         (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
00101   rmc->mp_exitstatus = 0;
00102   rmc->mp_sigstatus = 0;
00103 
00104   /* Find a free pid for the child and put it in the table. */
00105   new_pid = get_free_pid();
00106   rmc->mp_pid = new_pid;        /* assign pid to child */
00107 
00108   /* Tell kernel and file system about the (now successful) FORK. */
00109   if((r=sys_fork(who_e, child_nr, &rmc->mp_endpoint)) != OK) {
00110         panic(__FILE__,"do_fork can't sys_fork", r);
00111   }
00112   tell_fs(FORK, who_e, rmc->mp_endpoint, rmc->mp_pid);
00113 
00114   /* Report child's memory map to kernel. */
00115   if((r=sys_newmap(rmc->mp_endpoint, rmc->mp_seg)) != OK) {
00116         panic(__FILE__,"do_fork can't sys_newmap", r);
00117   }
00118 
00119   /* Reply to child to wake it up. */
00120   setreply(child_nr, 0);                /* only parent gets details */
00121   rmp->mp_reply.endpt = rmc->mp_endpoint;       /* child's process number */
00122 
00123   return(new_pid);                      /* child's pid */
00124 }
00125 
00126 /*===========================================================================*
00127  *                              do_pm_exit                                   *
00128  *===========================================================================*/
00129 PUBLIC int do_pm_exit()
00130 {
00131 /* Perform the exit(status) system call. The real work is done by pm_exit(),
00132  * which is also called when a process is killed by a signal.
00133  */
00134   pm_exit(mp, m_in.status);
00135   return(SUSPEND);              /* can't communicate from beyond the grave */
00136 }
00137 
00138 /*===========================================================================*
00139  *                              pm_exit                                      *
00140  *===========================================================================*/
00141 PUBLIC void pm_exit(rmp, exit_status)
00142 register struct mproc *rmp;     /* pointer to the process to be terminated */
00143 int exit_status;                /* the process' exit status (for parent) */
00144 {
00145 /* A process is done.  Release most of the process' possessions.  If its
00146  * parent is waiting, release the rest, else keep the process slot and
00147  * become a zombie.
00148  */
00149   register int proc_nr, proc_nr_e;
00150   int parent_waiting, right_child, r;
00151   pid_t pidarg, procgrp;
00152   struct mproc *p_mp;
00153   clock_t t[5];
00154 
00155   proc_nr = (int) (rmp - mproc);        /* get process slot number */
00156   proc_nr_e = rmp->mp_endpoint;
00157 
00158   /* Remember a session leader's process group. */
00159   procgrp = (rmp->mp_pid == mp->mp_procgrp) ? mp->mp_procgrp : 0;
00160 
00161   /* If the exited process has a timer pending, kill it. */
00162   if (rmp->mp_flags & ALARM_ON) set_alarm(proc_nr_e, (unsigned) 0);
00163 
00164   /* Do accounting: fetch usage times and accumulate at parent. */
00165   if((r=sys_times(proc_nr_e, t)) != OK)
00166         panic(__FILE__,"pm_exit: sys_times failed", r);
00167 
00168   p_mp = &mproc[rmp->mp_parent];                        /* process' parent */
00169   p_mp->mp_child_utime += t[0] + rmp->mp_child_utime;   /* add user time */
00170   p_mp->mp_child_stime += t[1] + rmp->mp_child_stime;   /* add system time */
00171 
00172   /* Tell the kernel the process is no longer runnable to prevent it from 
00173    * being scheduled in between the following steps. Then tell FS that it 
00174    * the process has exited and finally, clean up the process at the kernel.
00175    * This order is important so that FS can tell drivers to cancel requests
00176    * such as copying to/ from the exiting process, before it is gone.
00177    */
00178   sys_nice(proc_nr_e, PRIO_STOP);       /* stop the process */
00179   if(proc_nr_e != FS_PROC_NR)           /* if it is not FS that is exiting.. */
00180         tell_fs(EXIT, proc_nr_e, 0, 0);         /* tell FS to free the slot */
00181   else
00182         printf("PM: FS died\n");
00183   if((r=sys_exit(proc_nr_e)) != OK)     /* destroy the process */
00184         panic(__FILE__,"pm_exit: sys_exit failed", r);
00185 
00186   /* Pending reply messages for the dead process cannot be delivered. */
00187   rmp->mp_flags &= ~REPLY;
00188   
00189   /* Release the memory occupied by the child. */
00190   if (find_share(rmp, rmp->mp_ino, rmp->mp_dev, rmp->mp_ctime) == NULL) {
00191         /* No other process shares the text segment, so free it. */
00192         free_mem(rmp->mp_seg[T].mem_phys, rmp->mp_seg[T].mem_len);
00193   }
00194   /* Free the data and stack segments. */
00195   free_mem(rmp->mp_seg[D].mem_phys,
00196       rmp->mp_seg[S].mem_vir 
00197         + rmp->mp_seg[S].mem_len - rmp->mp_seg[D].mem_vir);
00198 
00199   /* The process slot can only be freed if the parent has done a WAIT. */
00200   rmp->mp_exitstatus = (char) exit_status;
00201 
00202   pidarg = p_mp->mp_wpid;               /* who's being waited for? */
00203   parent_waiting = p_mp->mp_flags & WAITING;
00204   right_child =                         /* child meets one of the 3 tests? */
00205         (pidarg == -1 || pidarg == rmp->mp_pid || -pidarg == rmp->mp_procgrp);
00206 
00207   if (parent_waiting && right_child) {
00208         cleanup(rmp);                   /* tell parent and release child slot */
00209   } else {
00210         rmp->mp_flags = IN_USE|ZOMBIE;  /* parent not waiting, zombify child */
00211         sig_proc(p_mp, SIGCHLD);        /* send parent a "child died" signal */
00212   }
00213 
00214   /* If the process has children, disinherit them.  INIT is the new parent. */
00215   for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
00216         if (rmp->mp_flags & IN_USE && rmp->mp_parent == proc_nr) {
00217                 /* 'rmp' now points to a child to be disinherited. */
00218                 rmp->mp_parent = INIT_PROC_NR;
00219                 parent_waiting = mproc[INIT_PROC_NR].mp_flags & WAITING;
00220                 if (parent_waiting && (rmp->mp_flags & ZOMBIE)) cleanup(rmp);
00221         }
00222   }
00223 
00224   /* Send a hangup to the process' process group if it was a session leader. */
00225   if (procgrp != 0) check_sig(-procgrp, SIGHUP);
00226 }
00227 
00228 /*===========================================================================*
00229  *                              do_waitpid                                   *
00230  *===========================================================================*/
00231 PUBLIC int do_waitpid()
00232 {
00233 /* A process wants to wait for a child to terminate. If a child is already 
00234  * waiting, go clean it up and let this WAIT call terminate.  Otherwise, 
00235  * really wait. 
00236  * A process calling WAIT never gets a reply in the usual way at the end
00237  * of the main loop (unless WNOHANG is set or no qualifying child exists).
00238  * If a child has already exited, the routine cleanup() sends the reply
00239  * to awaken the caller.
00240  * Both WAIT and WAITPID are handled by this code.
00241  */
00242   register struct mproc *rp;
00243   int pidarg, options, children;
00244 
00245   /* Set internal variables, depending on whether this is WAIT or WAITPID. */
00246   pidarg  = (call_nr == WAIT ? -1 : m_in.pid);     /* 1st param of waitpid */
00247   options = (call_nr == WAIT ?  0 : m_in.sig_nr);  /* 3rd param of waitpid */
00248   if (pidarg == 0) pidarg = -mp->mp_procgrp;    /* pidarg < 0 ==> proc grp */
00249 
00250   /* Is there a child waiting to be collected? At this point, pidarg != 0:
00251    *    pidarg  >  0 means pidarg is pid of a specific process to wait for
00252    *    pidarg == -1 means wait for any child
00253    *    pidarg  < -1 means wait for any child whose process group = -pidarg
00254    */
00255   children = 0;
00256   for (rp = &mproc[0]; rp < &mproc[NR_PROCS]; rp++) {
00257         if ( (rp->mp_flags & IN_USE) && rp->mp_parent == who_p) {
00258                 /* The value of pidarg determines which children qualify. */
00259                 if (pidarg  > 0 && pidarg != rp->mp_pid) continue;
00260                 if (pidarg < -1 && -pidarg != rp->mp_procgrp) continue;
00261 
00262                 children++;             /* this child is acceptable */
00263                 if (rp->mp_flags & ZOMBIE) {
00264                         /* This child meets the pid test and has exited. */
00265                         cleanup(rp);    /* this child has already exited */
00266                         return(SUSPEND);
00267                 }
00268                 if ((rp->mp_flags & STOPPED) && rp->mp_sigstatus) {
00269                         /* This child meets the pid test and is being traced.*/
00270                         mp->mp_reply.reply_res2 = 0177|(rp->mp_sigstatus << 8);
00271                         rp->mp_sigstatus = 0;
00272                         return(rp->mp_pid);
00273                 }
00274         }
00275   }
00276 
00277   /* No qualifying child has exited.  Wait for one, unless none exists. */
00278   if (children > 0) {
00279         /* At least 1 child meets the pid test exists, but has not exited. */
00280         if (options & WNOHANG) return(0);    /* parent does not want to wait */
00281         mp->mp_flags |= WAITING;             /* parent wants to wait */
00282         mp->mp_wpid = (pid_t) pidarg;        /* save pid for later */
00283         return(SUSPEND);                     /* do not reply, let it wait */
00284   } else {
00285         /* No child even meets the pid test.  Return error immediately. */
00286         return(ECHILD);                      /* no - parent has no children */
00287   }
00288 }
00289 
00290 /*===========================================================================*
00291  *                              cleanup                                      *
00292  *===========================================================================*/
00293 PRIVATE void cleanup(child)
00294 register struct mproc *child;   /* tells which process is exiting */
00295 {
00296 /* Finish off the exit of a process.  The process has exited or been killed
00297  * by a signal, and its parent is waiting.
00298  */
00299   struct mproc *parent = &mproc[child->mp_parent];
00300   int exitstatus;
00301 
00302   /* Wake up the parent by sending the reply message. */
00303   exitstatus = (child->mp_exitstatus << 8) | (child->mp_sigstatus & 0377);
00304   parent->mp_reply.reply_res2 = exitstatus;
00305   setreply(child->mp_parent, child->mp_pid);
00306   parent->mp_flags &= ~WAITING;         /* parent no longer waiting */
00307 
00308   /* Release the process table entry and reinitialize some field. */
00309   child->mp_pid = 0;
00310   child->mp_flags = 0;
00311   child->mp_child_utime = 0;
00312   child->mp_child_stime = 0;
00313   procs_in_use--;
00314 }
00315 

Generated on Fri Apr 14 22:57:32 2006 for minix by  doxygen 1.4.6