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 |
|
---|
27 | FORWARD _PROTOTYPE (void cleanup, (register struct mproc *child) );
|
---|
28 |
|
---|
29 | /*===========================================================================*
|
---|
30 | * do_fork *
|
---|
31 | *===========================================================================*/
|
---|
32 | PUBLIC 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 | *===========================================================================*/
|
---|
126 | PUBLIC 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 | *===========================================================================*/
|
---|
138 | PUBLIC void pm_exit(rmp, exit_status)
|
---|
139 | register struct mproc *rmp; /* pointer to the process to be terminated */
|
---|
140 | int 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 | *===========================================================================*/
|
---|
228 | PUBLIC 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 | *===========================================================================*/
|
---|
290 | PRIVATE void cleanup(child)
|
---|
291 | register 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 |
|
---|