source: trunk/minix/servers/pm/forkexit.c@ 10

Last change on this file since 10 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 12.3 KB
Line 
1/* This file deals with creating processes (via FORK) and deleting them (via
2 * EXIT/WAIT). When a process forks, a new slot in the 'mproc' table is
3 * allocated for it, and a copy of the parent's core image is made for the
4 * child. Then the kernel and file system are informed. A process is removed
5 * from the 'mproc' table when two events have occurred: (1) it has exited or
6 * been killed by a signal, and (2) the parent has done a WAIT. If the process
7 * exits first, it continues to occupy a slot until the parent does a WAIT.
8 *
9 * The entry points into this file are:
10 * do_fork: perform the FORK system call
11 * do_pm_exit: perform the EXIT system call (by calling pm_exit())
12 * pm_exit: actually do the exiting
13 * do_wait: perform the WAITPID or WAIT system call
14 */
15
16#include "pm.h"
17#include <sys/wait.h>
18#include <minix/callnr.h>
19#include <minix/com.h>
20#include <sys/resource.h>
21#include <signal.h>
22#include "mproc.h"
23#include "param.h"
24
25#define LAST_FEW 2 /* last few slots reserved for superuser */
26
27FORWARD _PROTOTYPE (void cleanup, (register struct mproc *child) );
28
29/*===========================================================================*
30 * do_fork *
31 *===========================================================================*/
32PUBLIC int do_fork()
33{
34/* The process pointed to by 'mp' has forked. Create a child process. */
35 register struct mproc *rmp; /* pointer to parent */
36 register struct mproc *rmc; /* pointer to child */
37 int child_nr, s;
38 phys_clicks prog_clicks, child_base;
39 phys_bytes prog_bytes, parent_abs, child_abs; /* Intel only */
40 pid_t new_pid;
41 static int next_child;
42 int n = 0, r;
43
44 /* If tables might fill up during FORK, don't even start since recovery half
45 * way through is such a nuisance.
46 */
47 rmp = mp;
48 if ((procs_in_use == NR_PROCS) ||
49 (procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0))
50 {
51 printf("PM: warning, process table is full!\n");
52 return(EAGAIN);
53 }
54
55 /* Determine how much memory to allocate. Only the data and stack need to
56 * be copied, because the text segment is either shared or of zero length.
57 */
58 prog_clicks = (phys_clicks) rmp->mp_seg[S].mem_len;
59 prog_clicks += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
60 prog_bytes = (phys_bytes) prog_clicks << CLICK_SHIFT;
61 if ( (child_base = alloc_mem(prog_clicks)) == NO_MEM) return(ENOMEM);
62
63 /* Create a copy of the parent's core image for the child. */
64 child_abs = (phys_bytes) child_base << CLICK_SHIFT;
65 parent_abs = (phys_bytes) rmp->mp_seg[D].mem_phys << CLICK_SHIFT;
66 s = sys_abscopy(parent_abs, child_abs, prog_bytes);
67 if (s < 0) panic(__FILE__,"do_fork can't copy", s);
68
69 /* Find a slot in 'mproc' for the child process. A slot must exist. */
70 do {
71 next_child = (next_child+1) % NR_PROCS;
72 n++;
73 } while((mproc[next_child].mp_flags & IN_USE) && n <= NR_PROCS);
74 if(n > NR_PROCS)
75 panic(__FILE__,"do_fork can't find child slot", NO_NUM);
76 if(next_child < 0 || next_child >= NR_PROCS
77 || (mproc[next_child].mp_flags & IN_USE))
78 panic(__FILE__,"do_fork finds wrong child slot", next_child);
79
80 rmc = &mproc[next_child];
81 /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
82 child_nr = (int)(rmc - mproc); /* slot number of the child */
83 procs_in_use++;
84 *rmc = *rmp; /* copy parent's process slot to child's */
85 rmc->mp_parent = who_p; /* record child's parent */
86 /* inherit only these flags */
87 rmc->mp_flags &= (IN_USE|SEPARATE|PRIV_PROC|DONT_SWAP);
88 rmc->mp_child_utime = 0; /* reset administration */
89 rmc->mp_child_stime = 0; /* reset administration */
90
91 /* A separate I&D child keeps the parents text segment. The data and stack
92 * segments must refer to the new copy.
93 */
94 if (!(rmc->mp_flags & SEPARATE)) rmc->mp_seg[T].mem_phys = child_base;
95 rmc->mp_seg[D].mem_phys = child_base;
96 rmc->mp_seg[S].mem_phys = rmc->mp_seg[D].mem_phys +
97 (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
98 rmc->mp_exitstatus = 0;
99 rmc->mp_sigstatus = 0;
100
101 /* Find a free pid for the child and put it in the table. */
102 new_pid = get_free_pid();
103 rmc->mp_pid = new_pid; /* assign pid to child */
104
105 /* Tell kernel and file system about the (now successful) FORK. */
106 if((r=sys_fork(who_e, child_nr, &rmc->mp_endpoint)) != OK) {
107 panic(__FILE__,"do_fork can't sys_fork", r);
108 }
109 tell_fs(FORK, who_e, rmc->mp_endpoint, rmc->mp_pid);
110
111 /* Report child's memory map to kernel. */
112 if((r=sys_newmap(rmc->mp_endpoint, rmc->mp_seg)) != OK) {
113 panic(__FILE__,"do_fork can't sys_newmap", r);
114 }
115
116 /* Reply to child to wake it up. */
117 setreply(child_nr, 0); /* only parent gets details */
118 rmp->mp_reply.endpt = rmc->mp_endpoint; /* child's process number */
119
120 return(new_pid); /* child's pid */
121}
122
123/*===========================================================================*
124 * do_pm_exit *
125 *===========================================================================*/
126PUBLIC int do_pm_exit()
127{
128/* Perform the exit(status) system call. The real work is done by pm_exit(),
129 * which is also called when a process is killed by a signal.
130 */
131 pm_exit(mp, m_in.status);
132 return(SUSPEND); /* can't communicate from beyond the grave */
133}
134
135/*===========================================================================*
136 * pm_exit *
137 *===========================================================================*/
138PUBLIC void pm_exit(rmp, exit_status)
139register struct mproc *rmp; /* pointer to the process to be terminated */
140int exit_status; /* the process' exit status (for parent) */
141{
142/* A process is done. Release most of the process' possessions. If its
143 * parent is waiting, release the rest, else keep the process slot and
144 * become a zombie.
145 */
146 register int proc_nr, proc_nr_e;
147 int parent_waiting, right_child, r;
148 pid_t pidarg, procgrp;
149 struct mproc *p_mp;
150 clock_t t[5];
151
152 proc_nr = (int) (rmp - mproc); /* get process slot number */
153 proc_nr_e = rmp->mp_endpoint;
154
155 /* Remember a session leader's process group. */
156 procgrp = (rmp->mp_pid == mp->mp_procgrp) ? mp->mp_procgrp : 0;
157
158 /* If the exited process has a timer pending, kill it. */
159 if (rmp->mp_flags & ALARM_ON) set_alarm(proc_nr_e, (unsigned) 0);
160
161 /* Do accounting: fetch usage times and accumulate at parent. */
162 if((r=sys_times(proc_nr_e, t)) != OK)
163 panic(__FILE__,"pm_exit: sys_times failed", r);
164
165 p_mp = &mproc[rmp->mp_parent]; /* process' parent */
166 p_mp->mp_child_utime += t[0] + rmp->mp_child_utime; /* add user time */
167 p_mp->mp_child_stime += t[1] + rmp->mp_child_stime; /* add system time */
168
169 /* Tell the kernel the process is no longer runnable to prevent it from
170 * being scheduled in between the following steps. Then tell FS that it
171 * the process has exited and finally, clean up the process at the kernel.
172 * This order is important so that FS can tell drivers to cancel requests
173 * such as copying to/ from the exiting process, before it is gone.
174 */
175 sys_nice(proc_nr_e, PRIO_STOP); /* stop the process */
176 if(proc_nr_e != FS_PROC_NR) /* if it is not FS that is exiting.. */
177 tell_fs(EXIT, proc_nr_e, 0, 0); /* tell FS to free the slot */
178 else
179 printf("PM: FS died\n");
180 if((r=sys_exit(proc_nr_e)) != OK) /* destroy the process */
181 panic(__FILE__,"pm_exit: sys_exit failed", r);
182
183 /* Pending reply messages for the dead process cannot be delivered. */
184 rmp->mp_flags &= ~REPLY;
185
186 /* Release the memory occupied by the child. */
187 if (find_share(rmp, rmp->mp_ino, rmp->mp_dev, rmp->mp_ctime) == NULL) {
188 /* No other process shares the text segment, so free it. */
189 free_mem(rmp->mp_seg[T].mem_phys, rmp->mp_seg[T].mem_len);
190 }
191 /* Free the data and stack segments. */
192 free_mem(rmp->mp_seg[D].mem_phys,
193 rmp->mp_seg[S].mem_vir
194 + rmp->mp_seg[S].mem_len - rmp->mp_seg[D].mem_vir);
195
196 /* The process slot can only be freed if the parent has done a WAIT. */
197 rmp->mp_exitstatus = (char) exit_status;
198
199 pidarg = p_mp->mp_wpid; /* who's being waited for? */
200 parent_waiting = p_mp->mp_flags & WAITING;
201 right_child = /* child meets one of the 3 tests? */
202 (pidarg == -1 || pidarg == rmp->mp_pid || -pidarg == rmp->mp_procgrp);
203
204 if (parent_waiting && right_child) {
205 cleanup(rmp); /* tell parent and release child slot */
206 } else {
207 rmp->mp_flags = IN_USE|ZOMBIE; /* parent not waiting, zombify child */
208 sig_proc(p_mp, SIGCHLD); /* send parent a "child died" signal */
209 }
210
211 /* If the process has children, disinherit them. INIT is the new parent. */
212 for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
213 if (rmp->mp_flags & IN_USE && rmp->mp_parent == proc_nr) {
214 /* 'rmp' now points to a child to be disinherited. */
215 rmp->mp_parent = INIT_PROC_NR;
216 parent_waiting = mproc[INIT_PROC_NR].mp_flags & WAITING;
217 if (parent_waiting && (rmp->mp_flags & ZOMBIE)) cleanup(rmp);
218 }
219 }
220
221 /* Send a hangup to the process' process group if it was a session leader. */
222 if (procgrp != 0) check_sig(-procgrp, SIGHUP);
223}
224
225/*===========================================================================*
226 * do_waitpid *
227 *===========================================================================*/
228PUBLIC int do_waitpid()
229{
230/* A process wants to wait for a child to terminate. If a child is already
231 * waiting, go clean it up and let this WAIT call terminate. Otherwise,
232 * really wait.
233 * A process calling WAIT never gets a reply in the usual way at the end
234 * of the main loop (unless WNOHANG is set or no qualifying child exists).
235 * If a child has already exited, the routine cleanup() sends the reply
236 * to awaken the caller.
237 * Both WAIT and WAITPID are handled by this code.
238 */
239 register struct mproc *rp;
240 int pidarg, options, children;
241
242 /* Set internal variables, depending on whether this is WAIT or WAITPID. */
243 pidarg = (call_nr == WAIT ? -1 : m_in.pid); /* 1st param of waitpid */
244 options = (call_nr == WAIT ? 0 : m_in.sig_nr); /* 3rd param of waitpid */
245 if (pidarg == 0) pidarg = -mp->mp_procgrp; /* pidarg < 0 ==> proc grp */
246
247 /* Is there a child waiting to be collected? At this point, pidarg != 0:
248 * pidarg > 0 means pidarg is pid of a specific process to wait for
249 * pidarg == -1 means wait for any child
250 * pidarg < -1 means wait for any child whose process group = -pidarg
251 */
252 children = 0;
253 for (rp = &mproc[0]; rp < &mproc[NR_PROCS]; rp++) {
254 if ( (rp->mp_flags & IN_USE) && rp->mp_parent == who_p) {
255 /* The value of pidarg determines which children qualify. */
256 if (pidarg > 0 && pidarg != rp->mp_pid) continue;
257 if (pidarg < -1 && -pidarg != rp->mp_procgrp) continue;
258
259 children++; /* this child is acceptable */
260 if (rp->mp_flags & ZOMBIE) {
261 /* This child meets the pid test and has exited. */
262 cleanup(rp); /* this child has already exited */
263 return(SUSPEND);
264 }
265 if ((rp->mp_flags & STOPPED) && rp->mp_sigstatus) {
266 /* This child meets the pid test and is being traced.*/
267 mp->mp_reply.reply_res2 = 0177|(rp->mp_sigstatus << 8);
268 rp->mp_sigstatus = 0;
269 return(rp->mp_pid);
270 }
271 }
272 }
273
274 /* No qualifying child has exited. Wait for one, unless none exists. */
275 if (children > 0) {
276 /* At least 1 child meets the pid test exists, but has not exited. */
277 if (options & WNOHANG) return(0); /* parent does not want to wait */
278 mp->mp_flags |= WAITING; /* parent wants to wait */
279 mp->mp_wpid = (pid_t) pidarg; /* save pid for later */
280 return(SUSPEND); /* do not reply, let it wait */
281 } else {
282 /* No child even meets the pid test. Return error immediately. */
283 return(ECHILD); /* no - parent has no children */
284 }
285}
286
287/*===========================================================================*
288 * cleanup *
289 *===========================================================================*/
290PRIVATE void cleanup(child)
291register struct mproc *child; /* tells which process is exiting */
292{
293/* Finish off the exit of a process. The process has exited or been killed
294 * by a signal, and its parent is waiting.
295 */
296 struct mproc *parent = &mproc[child->mp_parent];
297 int exitstatus;
298
299 /* Wake up the parent by sending the reply message. */
300 exitstatus = (child->mp_exitstatus << 8) | (child->mp_sigstatus & 0377);
301 parent->mp_reply.reply_res2 = exitstatus;
302 setreply(child->mp_parent, child->mp_pid);
303 parent->mp_flags &= ~WAITING; /* parent no longer waiting */
304
305 /* Release the process table entry and reinitialize some field. */
306 child->mp_pid = 0;
307 child->mp_flags = 0;
308 child->mp_child_utime = 0;
309 child->mp_child_stime = 0;
310 procs_in_use--;
311}
312
Note: See TracBrowser for help on using the repository browser.