home *** CD-ROM | disk | FTP | other *** search
- /*
- * _fork(), _wait(): create a new process, and wait for it to finish.
- * the parameter to _fork() controls the way memory is shared with the
- * parent; 0 means share all data and stack, non-zero means just share
- * stack up to the address given. Note that _fork(0) is callable only
- * when malloc's are being done from the heap. If _heapbase is
- * non-zero, _fork(0) fails and errno is set to EACCESS.
- *
- * written by Eric R. Smith and placed in the public domain.
- * use at your own risk.
- */
-
- #include <stdlib.h>
- #include <stddef.h> /* for size_t */
- #include <osbind.h>
- #include <basepage.h>
- #include <errno.h>
- #include <setjmp.h>
- #include "fork.h"
-
- struct _fork_block *_forks = 0;
- extern long _stksize;
-
- static short wait_return;
-
- /* newly forked processes start here */
- /* all we do is set up a temporary stack and longjmp() back to _fork() */
-
- static void
- in_fork(bp)
- long bp;
- {
- __asm__ volatile("movl %0,sp"::"r"(bp+500));
- longjmp(_forks->ctxt, 1);
- }
-
- /*
- * the guts of fork() and vfork(). _fork(0) is the same as fork();
- * _fork(x) gives a vfork(), and in this case "x" is the top address
- * we should save to. note that the parent is suspended until the child
- * exits (this is unlike Unix). also note that _fork(0) requires that
- * _heapbase be non-zero, i.e. that mallocs be done from the heap.
- * otherwise, we would have to keep track of all memory requests from
- * the system, and somehow put them all into the saved block. this is
- * doable, but would be a pain.
- */
-
- int
- _fork(save_to)
- char *save_to;
- {
- struct _fork_block *new;
- char *cpystart;
- long siz, *to, *from;
- long r;
- BASEPAGE *newbase;
- extern void *_heapbase;
-
- if (!save_to) {
- /* fork() requires stack based memory allocation */
- if (!_heapbase) {
- errno = EACCESS;
- return -1;
- }
- siz = ((char *)_heapbase - _base->p_dbase) + _stksize;
- cpystart = (char *)_base->p_dbase;
- }
- else {
- /*
- * for vfork, start copying just below where the stack is now, and
- * copy up to just past save_to, leaving some slush on either
- * side for return addresses, linkages, and saved regs
- */
- cpystart = ((char *)&save_to) - 128;
- siz = (save_to - cpystart) + 72;
- }
-
- /*
- * KLUDGE!
- * make sure that there is enough free memory handled by malloc, so that
- * it doesn't have to get any more from TOS if the "child" process
- * does a malloc() before it does an execve() (otherwise malloc() could
- * later return invalid memory blocks!) ++andreas
- */
- free(malloc((size_t)16*1024)); /* hope this is enough! */
-
- /* now malloc a _fork_block to describe this fork */
- if (!(new = (void *)Malloc(siz+sizeof(struct _fork_block)))) {
- errno = ENOMEM;
- return -1;
- }
-
-
- /* adjust size, since pointers are (long *) */
- siz = siz/sizeof(long);
-
- /* link the new fork block into the list and fill in some entries */
- new->next = _forks;
- new->start_addr = (long *)cpystart;
- new->data_size = siz;
- new->ppid = _base;
- _forks = new;
-
- /* make a new basepage for the child process */
-
- newbase = (BASEPAGE *)Pexec(PE_CBASEPAGE, 0L, "", 0L);
- if ((long)newbase < 0) {
- errno = -((int)newbase);
- _forks = _forks->next;
- Mfree(new);
- return -1;
- }
- new->pid = newbase;
-
- /* save the necessary data */
- to = new->data;
- from = (long *)cpystart;
- r = siz;
- while (r-- > 0)
- *to++ = *from++;
-
- /*
- make the new basepage just like ours, so that the child will be able to
- fork() too
- */
- newbase->p_dbase = _base->p_dbase;
- newbase->p_bbase = _base->p_bbase;
- newbase->p_dlen = _base->p_dlen;
- newbase->p_blen = _base->p_blen;
- newbase->p_tbase = (char *)in_fork;
- newbase->p_tlen = 0;
-
- if (r = setjmp(new->ctxt)) {
- /* the second return is caused by the child, from in_fork() */
- Mshrink(_base, 256L);
- return 0;
- }
- else {
- /* If the child stomps on the parent's stack, then Pexec won't return to
- * the right place. So we set to a temporary stack before the Pexec.
- */
- _base = newbase;
- r = (long)&(new->tmp_stack[62]);
- __asm__ volatile
- ("
- movel sp, a0;
- movel %0, sp;
- movel a0, sp@- "
- :
- : "r"(r)
- : "a0", "sp"
- );
- wait_return = Pexec(PE_GO, 0L, newbase, 0L);
- __asm__ volatile
- ("
- movel sp@+, a0;
- movel a0, sp "
- ::: "a0", "sp"
- );
-
- /* we're back in the parent here, so restore everything */
- new = *((struct _fork_block * volatile *)&_forks);
- _base = new->ppid;
- new->return_code = wait_return;
- /* restore data */
- to = new->start_addr;
- from = new->data;
- siz = new->data_size;
- while (siz-- > 0)
- *to++ = *from++;
- Mfree(newbase->p_env);
- Mfree(newbase);
- /* shrink the fork block to the minimum size (it can't be freed yet,
- * because wait() may need it)
- */
- Mshrink(new, sizeof(struct _fork_block));
- return BP_TO_PID(newbase);
- }
- }
-
- /*
- * wait() emulation; goes through the fork block structure and returns
- * the pid of a child we haven't waited for yet. if such a child exists,
- * and exit_code is non-zero, the integer pointed at by exit_code is
- * set to the child's exit status. if no child is found, -1 is returned
- * and errno is set.
- */
-
- int _wait(exit_code)
- int *exit_code;
- {
- struct _fork_block *f, *g;
- int r;
-
- g = 0;
- f = _forks;
- while (f) {
- if (f->ppid == _base) { /* is this one our child?? */
- r = BP_TO_PID(f->pid);
- if (exit_code)
- *exit_code = f->return_code;
- if (g)
- g->next = f->next;
- else
- _forks = f->next;
- Mfree(f);
- return r;
- }
- g = f;
- f = f->next;
- }
- errno = ENMFILES; /* FIX_ME:: should be ESRCH */
- return -1;
- }
-