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.
9 * The entry points into this file are:
10 * do_fork: perform the FORK system call
11 * do_fork_nb: special nonblocking version of FORK, for RS
12 * do_exit: perform the EXIT system call (by calling exit_proc())
13 * exit_proc: actually do the exiting, and tell FS about it
14 * exit_restart: continue exiting a process after FS has replied
15 * do_waitpid: perform the WAITPID or WAIT system call
16 * wait_test: check whether a parent is waiting for a child
21 #include <minix/callnr.h>
22 #include <minix/com.h>
24 #include <sys/ptrace.h>
25 #include <sys/resource.h>
30 #define LAST_FEW 2 /* last few slots reserved for superuser */
32 FORWARD
_PROTOTYPE (void zombify
, (struct mproc
*rmp
) );
33 FORWARD
_PROTOTYPE (void check_parent
, (struct mproc
*child
,
35 FORWARD
_PROTOTYPE (void tell_parent
, (struct mproc
*child
) );
36 FORWARD
_PROTOTYPE (void tell_tracer
, (struct mproc
*child
) );
37 FORWARD
_PROTOTYPE (void tracer_died
, (struct mproc
*child
) );
38 FORWARD
_PROTOTYPE (void cleanup
, (register struct mproc
*rmp
) );
40 /*===========================================================================*
42 *===========================================================================*/
45 /* The process pointed to by 'mp' has forked. Create a child process. */
46 register struct mproc
*rmp
; /* pointer to parent */
47 register struct mproc
*rmc
; /* pointer to child */
49 static int next_child
;
54 /* If tables might fill up during FORK, don't even start since recovery half
55 * way through is such a nuisance.
58 if ((procs_in_use
== NR_PROCS
) ||
59 (procs_in_use
>= NR_PROCS
-LAST_FEW
&& rmp
->mp_effuid
!= 0))
61 printf("PM: warning, process table is full!\n");
65 /* Find a slot in 'mproc' for the child process. A slot must exist. */
67 next_child
= (next_child
+1) % NR_PROCS
;
69 } while((mproc
[next_child
].mp_flags
& IN_USE
) && n
<= NR_PROCS
);
71 panic(__FILE__
,"do_fork can't find child slot", NO_NUM
);
72 if(next_child
< 0 || next_child
>= NR_PROCS
73 || (mproc
[next_child
].mp_flags
& IN_USE
))
74 panic(__FILE__
,"do_fork finds wrong child slot", next_child
);
76 /* Memory part of the forking. */
77 if((s
=vm_fork(rmp
->mp_endpoint
, next_child
, &child_ep
)) != OK
) {
78 printf("PM: vm_fork failed: %d\n", s
);
82 /* PM may not fail fork after call to vm_fork(), as VM calls sys_fork(). */
84 rmc
= &mproc
[next_child
];
85 /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
87 *rmc
= *rmp
; /* copy parent's process slot to child's */
88 rmc
->mp_parent
= who_p
; /* record child's parent */
89 if (!(rmc
->mp_trace_flags
& TO_TRACEFORK
)) {
90 rmc
->mp_tracer
= NO_TRACER
; /* no tracer attached */
91 rmc
->mp_trace_flags
= 0;
92 sigemptyset(&rmc
->mp_sigtrace
);
94 /* inherit only these flags */
95 rmc
->mp_flags
&= (IN_USE
|PRIV_PROC
|DELAY_CALL
);
96 rmc
->mp_child_utime
= 0; /* reset administration */
97 rmc
->mp_child_stime
= 0; /* reset administration */
98 rmc
->mp_exitstatus
= 0;
99 rmc
->mp_sigstatus
= 0;
100 rmc
->mp_endpoint
= child_ep
; /* passed back by VM */
101 for (i
= 0; i
< NR_ITIMERS
; i
++)
102 rmc
->mp_interval
[i
] = 0; /* reset timer intervals */
104 /* Find a free pid for the child and put it in the table. */
105 new_pid
= get_free_pid();
106 rmc
->mp_pid
= new_pid
; /* assign pid to child */
109 m
.PM_PROC
= rmc
->mp_endpoint
;
110 m
.PM_PPROC
= rmp
->mp_endpoint
;
111 m
.PM_CPID
= rmc
->mp_pid
;
115 /* Tell the tracer, if any, about the new child */
116 if (rmc
->mp_tracer
!= NO_TRACER
)
117 sig_proc(rmc
, SIGSTOP
, TRUE
/*trace*/);
119 /* Do not reply until FS is ready to process the fork
125 /*===========================================================================*
127 *===========================================================================*/
128 PUBLIC
int do_fork_nb()
130 /* The process pointed to by 'mp' has forked. Create a child process. */
131 register struct mproc
*rmp
; /* pointer to parent */
132 register struct mproc
*rmc
; /* pointer to child */
135 static int next_child
;
140 /* Only system processes are allowed to use fork_nb */
141 if (!(mp
->mp_flags
& PRIV_PROC
))
144 /* If tables might fill up during FORK, don't even start since recovery half
145 * way through is such a nuisance.
148 if ((procs_in_use
== NR_PROCS
) ||
149 (procs_in_use
>= NR_PROCS
-LAST_FEW
&& rmp
->mp_effuid
!= 0))
151 printf("PM: warning, process table is full!\n");
155 /* Find a slot in 'mproc' for the child process. A slot must exist. */
157 next_child
= (next_child
+1) % NR_PROCS
;
159 } while((mproc
[next_child
].mp_flags
& IN_USE
) && n
<= NR_PROCS
);
161 panic(__FILE__
,"do_fork can't find child slot", NO_NUM
);
162 if(next_child
< 0 || next_child
>= NR_PROCS
163 || (mproc
[next_child
].mp_flags
& IN_USE
))
164 panic(__FILE__
,"do_fork finds wrong child slot", next_child
);
166 if((s
=vm_fork(rmp
->mp_endpoint
, next_child
, &child_ep
)) != OK
) {
167 printf("PM: vm_fork failed: %d\n", s
);
171 rmc
= &mproc
[next_child
];
172 /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
174 *rmc
= *rmp
; /* copy parent's process slot to child's */
175 rmc
->mp_parent
= who_p
; /* record child's parent */
176 if (!(rmc
->mp_trace_flags
& TO_TRACEFORK
)) {
177 rmc
->mp_tracer
= NO_TRACER
; /* no tracer attached */
178 rmc
->mp_trace_flags
= 0;
179 sigemptyset(&rmc
->mp_sigtrace
);
181 /* inherit only these flags */
182 rmc
->mp_flags
&= (IN_USE
|PRIV_PROC
|DELAY_CALL
);
183 rmc
->mp_child_utime
= 0; /* reset administration */
184 rmc
->mp_child_stime
= 0; /* reset administration */
185 rmc
->mp_exitstatus
= 0;
186 rmc
->mp_sigstatus
= 0;
187 rmc
->mp_endpoint
= child_ep
; /* passed back by VM */
188 for (i
= 0; i
< NR_ITIMERS
; i
++)
189 rmc
->mp_interval
[i
] = 0; /* reset timer intervals */
191 /* Find a free pid for the child and put it in the table. */
192 new_pid
= get_free_pid();
193 rmc
->mp_pid
= new_pid
; /* assign pid to child */
195 m
.m_type
= PM_FORK_NB
;
196 m
.PM_PROC
= rmc
->mp_endpoint
;
197 m
.PM_PPROC
= rmp
->mp_endpoint
;
198 m
.PM_CPID
= rmc
->mp_pid
;
202 /* Tell the tracer, if any, about the new child */
203 if (rmc
->mp_tracer
!= NO_TRACER
)
204 sig_proc(rmc
, SIGSTOP
, TRUE
/*trace*/);
206 /* Wakeup the newly created process */
207 setreply(rmc
-mproc
, OK
);
212 /*===========================================================================*
214 *===========================================================================*/
217 /* Perform the exit(status) system call. The real work is done by exit_proc(),
218 * which is also called when a process is killed by a signal.
220 exit_proc(mp
, m_in
.status
, FALSE
/*dump_core*/);
221 return(SUSPEND
); /* can't communicate from beyond the grave */
224 /*===========================================================================*
226 *===========================================================================*/
227 PUBLIC
void exit_proc(rmp
, exit_status
, dump_core
)
228 register struct mproc
*rmp
; /* pointer to the process to be terminated */
229 int exit_status
; /* the process' exit status (for parent) */
230 int dump_core
; /* flag indicating whether to dump core */
232 /* A process is done. Release most of the process' possessions. If its
233 * parent is waiting, release the rest, else keep the process slot and
236 register int proc_nr
, proc_nr_e
;
237 int parent_waiting
, r
;
240 clock_t user_time
, sys_time
;
243 /* Do not create core files for set uid execution */
244 if (dump_core
&& rmp
->mp_realuid
!= rmp
->mp_effuid
)
247 /* System processes are destroyed before informing FS, meaning that FS can
248 * not get their CPU state, so we can't generate a coredump for them either.
250 if (dump_core
&& (rmp
->mp_flags
& PRIV_PROC
))
253 proc_nr
= (int) (rmp
- mproc
); /* get process slot number */
254 proc_nr_e
= rmp
->mp_endpoint
;
256 /* Remember a session leader's process group. */
257 procgrp
= (rmp
->mp_pid
== mp
->mp_procgrp
) ? mp
->mp_procgrp
: 0;
259 /* If the exited process has a timer pending, kill it. */
260 if (rmp
->mp_flags
& ALARM_ON
) set_alarm(rmp
, (clock_t) 0);
262 /* Do accounting: fetch usage times and accumulate at parent. */
263 if((r
=sys_times(proc_nr_e
, &user_time
, &sys_time
, NULL
, NULL
)) != OK
)
264 panic(__FILE__
,"exit_proc: sys_times failed", r
);
266 p_mp
= &mproc
[rmp
->mp_parent
]; /* process' parent */
267 p_mp
->mp_child_utime
+= user_time
+ rmp
->mp_child_utime
; /* add user time */
268 p_mp
->mp_child_stime
+= sys_time
+ rmp
->mp_child_stime
; /* add system time */
270 /* Tell the kernel the process is no longer runnable to prevent it from
271 * being scheduled in between the following steps. Then tell FS that it
272 * the process has exited and finally, clean up the process at the kernel.
273 * This order is important so that FS can tell drivers to cancel requests
274 * such as copying to/ from the exiting process, before it is gone.
276 if ((r
= sys_stop(proc_nr_e
)) != OK
) /* stop the process */
277 panic(__FILE__
, "sys_stop failed", r
);
279 if((r
=vm_willexit(proc_nr_e
)) != OK
) {
280 panic(__FILE__
, "exit_proc: vm_willexit failed", r
);
282 vm_notify_sig_wrapper(rmp
->mp_endpoint
);
284 if (proc_nr_e
== INIT_PROC_NR
)
286 printf("PM: INIT died\n");
289 if (proc_nr_e
== FS_PROC_NR
)
291 panic(__FILE__
, "exit_proc: FS died", r
);
294 /* Tell FS about the exiting process. */
295 m
.m_type
= dump_core
? PM_DUMPCORE
: PM_EXIT
;
296 m
.PM_PROC
= rmp
->mp_endpoint
;
300 if (rmp
->mp_flags
& PRIV_PROC
)
302 /* Destroy system processes without waiting for FS. This is
303 * needed because the system process might be a block device
304 * driver that FS is blocked waiting on.
306 if((r
= sys_exit(rmp
->mp_endpoint
)) != OK
)
307 panic(__FILE__
, "exit_proc: sys_exit failed", r
);
310 /* Clean up most of the flags describing the process's state before the exit,
311 * and mark it as exiting.
313 rmp
->mp_flags
&= (IN_USE
|FS_CALL
|PRIV_PROC
|TRACE_EXIT
);
314 rmp
->mp_flags
|= EXITING
;
316 /* Keep the process around until FS is finished with it. */
318 rmp
->mp_exitstatus
= (char) exit_status
;
320 /* For normal exits, try to notify the parent as soon as possible.
321 * For core dumps, notify the parent only once the core dump has been made.
326 /* If the process has children, disinherit them. INIT is the new parent. */
327 for (rmp
= &mproc
[0]; rmp
< &mproc
[NR_PROCS
]; rmp
++) {
328 if (!(rmp
->mp_flags
& IN_USE
)) continue;
329 if (rmp
->mp_tracer
== proc_nr
) {
330 /* This child's tracer died. Do something sensible. */
333 if (rmp
->mp_parent
== proc_nr
) {
334 /* 'rmp' now points to a child to be disinherited. */
335 rmp
->mp_parent
= INIT_PROC_NR
;
337 /* Notify new parent. */
338 if (rmp
->mp_flags
& ZOMBIE
)
339 check_parent(rmp
, TRUE
/*try_cleanup*/);
343 /* Send a hangup to the process' process group if it was a session leader. */
344 if (procgrp
!= 0) check_sig(-procgrp
, SIGHUP
);
347 /*===========================================================================*
349 *===========================================================================*/
350 PUBLIC
void exit_restart(rmp
, dump_core
)
351 struct mproc
*rmp
; /* pointer to the process being terminated */
352 int dump_core
; /* flag indicating whether to dump core */
354 /* FS replied to our exit or coredump request. Perform the second half of the
359 /* For core dumps, now is the right time to try to contact the parent. */
363 if (!(rmp
->mp_flags
& PRIV_PROC
))
365 /* destroy the (user) process */
366 if((r
=sys_exit(rmp
->mp_endpoint
)) != OK
)
367 panic(__FILE__
, "exit_restart: sys_exit failed", r
);
370 /* Release the memory occupied by the child. */
371 if((r
=vm_exit(rmp
->mp_endpoint
)) != OK
) {
372 panic(__FILE__
, "exit_restart: vm_exit failed", r
);
375 if (rmp
->mp_flags
& TRACE_EXIT
)
377 /* Wake up the tracer, completing the ptrace(T_EXIT) call */
378 mproc
[rmp
->mp_tracer
].mp_reply
.reply_trace
= 0;
379 setreply(rmp
->mp_tracer
, OK
);
382 /* Clean up if the parent has collected the exit status */
383 if (rmp
->mp_flags
& TOLD_PARENT
)
387 /*===========================================================================*
389 *===========================================================================*/
390 PUBLIC
int do_waitpid()
392 /* A process wants to wait for a child to terminate. If a child is already
393 * waiting, go clean it up and let this WAIT call terminate. Otherwise,
395 * A process calling WAIT never gets a reply in the usual way at the end
396 * of the main loop (unless WNOHANG is set or no qualifying child exists).
397 * If a child has already exited, the routine tell_parent() sends the reply
398 * to awaken the caller.
399 * Both WAIT and WAITPID are handled by this code.
401 register struct mproc
*rp
;
402 int i
, pidarg
, options
, children
;
404 /* Set internal variables, depending on whether this is WAIT or WAITPID. */
405 pidarg
= (call_nr
== WAIT
? -1 : m_in
.pid
); /* 1st param of waitpid */
406 options
= (call_nr
== WAIT
? 0 : m_in
.sig_nr
); /* 3rd param of waitpid */
407 if (pidarg
== 0) pidarg
= -mp
->mp_procgrp
; /* pidarg < 0 ==> proc grp */
409 /* Is there a child waiting to be collected? At this point, pidarg != 0:
410 * pidarg > 0 means pidarg is pid of a specific process to wait for
411 * pidarg == -1 means wait for any child
412 * pidarg < -1 means wait for any child whose process group = -pidarg
415 for (rp
= &mproc
[0]; rp
< &mproc
[NR_PROCS
]; rp
++) {
416 if ((rp
->mp_flags
& (IN_USE
| TOLD_PARENT
)) != IN_USE
) continue;
417 if (rp
->mp_parent
!= who_p
&& rp
->mp_tracer
!= who_p
) continue;
418 if (rp
->mp_parent
!= who_p
&& (rp
->mp_flags
& ZOMBIE
)) continue;
420 /* The value of pidarg determines which children qualify. */
421 if (pidarg
> 0 && pidarg
!= rp
->mp_pid
) continue;
422 if (pidarg
< -1 && -pidarg
!= rp
->mp_procgrp
) continue;
424 children
++; /* this child is acceptable */
426 if (rp
->mp_tracer
== who_p
) {
427 if (rp
->mp_flags
& TRACE_ZOMBIE
) {
428 /* Traced child meets the pid test and has exited. */
430 check_parent(rp
, TRUE
/*try_cleanup*/);
433 if (rp
->mp_flags
& STOPPED
) {
434 /* This child meets the pid test and is being traced.
435 * Deliver a signal to the tracer, if any.
437 for (i
= 1; i
< _NSIG
; i
++) {
438 if (sigismember(&rp
->mp_sigtrace
, i
)) {
439 sigdelset(&rp
->mp_sigtrace
, i
);
441 mp
->mp_reply
.reply_res2
=
449 if (rp
->mp_parent
== who_p
) {
450 if (rp
->mp_flags
& ZOMBIE
) {
451 /* This child meets the pid test and has exited. */
452 tell_parent(rp
); /* this child has already exited */
453 if (!(rp
->mp_flags
& FS_CALL
))
460 /* No qualifying child has exited. Wait for one, unless none exists. */
462 /* At least 1 child meets the pid test exists, but has not exited. */
463 if (options
& WNOHANG
) {
464 return(0); /* parent does not want to wait */
466 mp
->mp_flags
|= WAITING
; /* parent wants to wait */
467 mp
->mp_wpid
= (pid_t
) pidarg
; /* save pid for later */
468 return(SUSPEND
); /* do not reply, let it wait */
470 /* No child even meets the pid test. Return error immediately. */
471 return(ECHILD
); /* no - parent has no children */
475 /*===========================================================================*
477 *===========================================================================*/
478 PUBLIC
int wait_test(rmp
, child
)
479 struct mproc
*rmp
; /* process that may be waiting */
480 struct mproc
*child
; /* process that may be waited for */
482 /* See if a parent or tracer process is waiting for a child process.
483 * A tracer is considered to be a pseudo-parent.
485 int parent_waiting
, right_child
;
488 pidarg
= rmp
->mp_wpid
; /* who's being waited for? */
489 parent_waiting
= rmp
->mp_flags
& WAITING
;
490 right_child
= /* child meets one of the 3 tests? */
491 (pidarg
== -1 || pidarg
== child
->mp_pid
||
492 -pidarg
== child
->mp_procgrp
);
494 return (parent_waiting
&& right_child
);
497 /*===========================================================================*
499 *===========================================================================*/
500 PRIVATE
void zombify(rmp
)
503 /* Zombify a process. First check if the exiting process is traced by a process
504 * other than its parent; if so, the tracer must be notified about the exit
505 * first. Once that is done, the real parent may be notified about the exit of
510 if (rmp
->mp_flags
& (TRACE_ZOMBIE
| ZOMBIE
))
511 panic(__FILE__
, "zombify: process was already a zombie", NO_NUM
);
513 /* See if we have to notify a tracer process first. */
514 if (rmp
->mp_tracer
!= NO_TRACER
&& rmp
->mp_tracer
!= rmp
->mp_parent
) {
515 rmp
->mp_flags
|= TRACE_ZOMBIE
;
517 t_mp
= &mproc
[rmp
->mp_tracer
];
519 /* Do not bother sending SIGCHLD signals to tracers. */
520 if (!wait_test(t_mp
, rmp
))
526 rmp
->mp_flags
|= ZOMBIE
;
529 /* No tracer, or tracer is parent, or tracer has now been notified. */
530 check_parent(rmp
, FALSE
/*try_cleanup*/);
533 /*===========================================================================*
535 *===========================================================================*/
536 PRIVATE
void check_parent(child
, try_cleanup
)
537 struct mproc
*child
; /* tells which process is exiting */
538 int try_cleanup
; /* clean up the child when done? */
540 /* We would like to inform the parent of an exiting child about the child's
541 * death. If the parent is waiting for the child, tell it immediately;
542 * otherwise, send it a SIGCHLD signal.
544 * Note that we may call this function twice on a single child; first with
545 * its original parent, later (if the parent died) with INIT as its parent.
549 p_mp
= &mproc
[child
->mp_parent
];
551 if (p_mp
->mp_flags
& EXITING
) {
552 /* This may trigger if the child of a dead parent dies. The child will
553 * be assigned to INIT and rechecked shortly after. Do nothing.
556 else if (wait_test(p_mp
, child
)) {
559 /* The 'try_cleanup' flag merely saves us from having to be really
560 * careful with statement ordering in exit_proc() and exit_restart().
562 if (try_cleanup
&& !(child
->mp_flags
& FS_CALL
))
566 /* Parent is not waiting. */
567 sig_proc(p_mp
, SIGCHLD
, TRUE
/*trace*/);
571 /*===========================================================================*
573 *===========================================================================*/
574 PRIVATE
void tell_parent(child
)
575 register struct mproc
*child
; /* tells which process is exiting */
577 int exitstatus
, mp_parent
;
578 struct mproc
*parent
;
580 mp_parent
= child
->mp_parent
;
582 panic(__FILE__
, "tell_parent: bad value in mp_parent", mp_parent
);
583 if(!(child
->mp_flags
& ZOMBIE
))
584 panic(__FILE__
, "tell_parent: child not a zombie", NO_NUM
);
585 if(child
->mp_flags
& TOLD_PARENT
)
586 panic(__FILE__
, "tell_parent: telling parent again", NO_NUM
);
587 parent
= &mproc
[mp_parent
];
589 /* Wake up the parent by sending the reply message. */
590 exitstatus
= (child
->mp_exitstatus
<< 8) | (child
->mp_sigstatus
& 0377);
591 parent
->mp_reply
.reply_res2
= exitstatus
;
592 setreply(child
->mp_parent
, child
->mp_pid
);
593 parent
->mp_flags
&= ~WAITING
; /* parent no longer waiting */
594 child
->mp_flags
&= ~ZOMBIE
; /* child no longer a zombie */
595 child
->mp_flags
|= TOLD_PARENT
; /* avoid informing parent twice */
598 /*===========================================================================*
600 *===========================================================================*/
601 PRIVATE
void tell_tracer(child
)
602 struct mproc
*child
; /* tells which process is exiting */
604 int exitstatus
, mp_tracer
;
605 struct mproc
*tracer
;
607 mp_tracer
= child
->mp_tracer
;
609 panic(__FILE__
, "tell_tracer: bad value in mp_tracer", mp_tracer
);
610 if(!(child
->mp_flags
& TRACE_ZOMBIE
))
611 panic(__FILE__
, "tell_tracer: child not a zombie", NO_NUM
);
612 tracer
= &mproc
[mp_tracer
];
614 exitstatus
= (child
->mp_exitstatus
<< 8) | (child
->mp_sigstatus
& 0377);
615 tracer
->mp_reply
.reply_res2
= exitstatus
;
616 setreply(child
->mp_tracer
, child
->mp_pid
);
617 tracer
->mp_flags
&= ~WAITING
; /* tracer no longer waiting */
618 child
->mp_flags
&= ~TRACE_ZOMBIE
; /* child no longer zombie to tracer */
619 child
->mp_flags
|= ZOMBIE
; /* child is now zombie to parent */
622 /*===========================================================================*
624 *===========================================================================*/
625 PRIVATE
void tracer_died(child
)
626 struct mproc
*child
; /* process being traced */
628 /* The process that was tracing the given child, has died for some reason.
629 * This is really the tracer's fault, but we can't let INIT deal with this.
632 child
->mp_tracer
= NO_TRACER
;
633 child
->mp_flags
&= ~TRACE_EXIT
;
635 /* If the tracer died while the child was running or stopped, we have no
636 * idea what state the child is in. Avoid a trainwreck, by killing the child.
637 * Note that this may cause cascading exits.
639 if (!(child
->mp_flags
& EXITING
)) {
640 sig_proc(child
, SIGKILL
, TRUE
/*trace*/);
645 /* If the tracer died while the child was telling it about its own death,
646 * forget about the tracer and notify the real parent instead.
648 if (child
->mp_flags
& TRACE_ZOMBIE
) {
649 child
->mp_flags
&= ~TRACE_ZOMBIE
;
650 child
->mp_flags
|= ZOMBIE
;
652 check_parent(child
, TRUE
/*try_cleanup*/);
656 /*===========================================================================*
658 *===========================================================================*/
659 PRIVATE
void cleanup(rmp
)
660 register struct mproc
*rmp
; /* tells which process is exiting */
662 /* Release the process table entry and reinitialize some field. */
665 rmp
->mp_child_utime
= 0;
666 rmp
->mp_child_stime
= 0;
670 PUBLIC
void _exit(int code
)
675 PUBLIC
void __exit(int code
)