Files
retrobsd/sys/kernel/kern_fork.c
Serge Vakulenko 585773955b Fix include paths in the kernel sources.
Max32 kernel successfully compiled with kconfig utility.
2015-08-31 00:21:41 -07:00

255 lines
6.5 KiB
C

/*
* Copyright (c) 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/map.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/inode.h>
#include <sys/file.h>
#include <sys/vm.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
int mpid; /* generic for unique process id's */
/*
* Create a new process -- the internal version of system call fork.
* It returns 1 in the new process, 0 in the old.
*/
int
newproc (isvfork)
int isvfork;
{
register struct proc *child, *parent;
register int n;
static int pidchecked = 0;
struct file *fp;
/*
* First, just locate a slot for a process
* and copy the useful info from this process into it.
* The panic "cannot happen" because fork has already
* checked for the existence of a slot.
*/
mpid++;
retry:
if (mpid >= 30000) {
mpid = 100;
pidchecked = 0;
}
if (mpid >= pidchecked) {
int doingzomb = 0;
pidchecked = 30000;
/*
* Scan the proc table to check whether this pid
* is in use. Remember the lowest pid that's greater
* than mpid, so we can avoid checking for a while.
*/
child = allproc;
again:
for (; child != NULL; child = child->p_nxt) {
if (child->p_pid == mpid || child->p_pgrp == mpid) {
mpid++;
if (mpid >= pidchecked)
goto retry;
}
if (child->p_pid > mpid && pidchecked > child->p_pid)
pidchecked = child->p_pid;
if (child->p_pgrp > mpid && pidchecked > child->p_pgrp)
pidchecked = child->p_pgrp;
}
if (!doingzomb) {
doingzomb = 1;
child = zombproc;
goto again;
}
}
child = freeproc;
if (child == NULL)
panic("no procs");
freeproc = child->p_nxt; /* off freeproc */
/*
* Make a proc table entry for the new process.
*/
parent = u.u_procp;
child->p_stat = SIDL;
child->p_realtimer.it_value = 0;
child->p_flag = SLOAD;
child->p_uid = parent->p_uid;
child->p_pgrp = parent->p_pgrp;
child->p_nice = parent->p_nice;
child->p_pid = mpid;
child->p_ppid = parent->p_pid;
child->p_pptr = parent;
child->p_time = 0;
child->p_cpu = 0;
child->p_sigmask = parent->p_sigmask;
child->p_sigcatch = parent->p_sigcatch;
child->p_sigignore = parent->p_sigignore;
/* take along any pending signals like stops? */
#ifdef UCB_METER
if (isvfork) {
forkstat.cntvfork++;
forkstat.sizvfork += (parent->p_dsize + parent->p_ssize) >> 10;
} else {
forkstat.cntfork++;
forkstat.sizfork += (parent->p_dsize + parent->p_ssize) >> 10;
}
#endif
child->p_wchan = 0;
child->p_slptime = 0;
{
struct proc **hash = &pidhash [PIDHASH (child->p_pid)];
child->p_hash = *hash;
*hash = child;
}
/*
* some shuffling here -- in most UNIX kernels, the allproc assign
* is done after grabbing the struct off of the freeproc list. We
* wait so that if the clock interrupts us and vmtotal walks allproc
* the text pointer isn't garbage.
*/
child->p_nxt = allproc; /* onto allproc */
child->p_nxt->p_prev = &child->p_nxt; /* (allproc is never NULL) */
child->p_prev = &allproc;
allproc = child;
/*
* Increase reference counts on shared objects.
*/
for (n = 0; n <= u.u_lastfile; n++) {
fp = u.u_ofile[n];
if (fp == NULL)
continue;
fp->f_count++;
}
u.u_cdir->i_count++;
if (u.u_rdir)
u.u_rdir->i_count++;
/*
* When the longjmp is executed for the new process,
* here's where it will resume.
*/
if (setjmp (&u.u_ssave)) {
return(1);
}
child->p_dsize = parent->p_dsize;
child->p_ssize = parent->p_ssize;
child->p_daddr = parent->p_daddr;
child->p_saddr = parent->p_saddr;
/*
* Partially simulate the environment of the new process so that
* when it is actually created (by copying) it will look right.
*/
u.u_procp = child;
/*
* Swap out the current process to generate the copy.
*/
parent->p_stat = SIDL;
child->p_addr = parent->p_addr;
child->p_stat = SRUN;
swapout (child, X_DONTFREE, X_OLDSIZE, X_OLDSIZE);
child->p_flag |= SSWAP;
parent->p_stat = SRUN;
u.u_procp = parent;
if (isvfork) {
/*
* Wait for the child to finish with it.
* RetroBSD: to make this work, significant
* changes in scheduler are required.
*/
parent->p_dsize = 0;
parent->p_ssize = 0;
child->p_flag |= SVFORK;
parent->p_flag |= SVFPRNT;
while (child->p_flag & SVFORK)
sleep ((caddr_t)child, PSWP+1);
if ((child->p_flag & SLOAD) == 0)
panic ("newproc vfork");
u.u_dsize = parent->p_dsize = child->p_dsize;
parent->p_daddr = child->p_daddr;
child->p_dsize = 0;
u.u_ssize = parent->p_ssize = child->p_ssize;
parent->p_saddr = child->p_saddr;
child->p_ssize = 0;
child->p_flag |= SVFDONE;
wakeup ((caddr_t) parent);
parent->p_flag &= ~SVFPRNT;
}
return(0);
}
static void
fork1 (isvfork)
int isvfork;
{
register int a;
register struct proc *p1, *p2;
a = 0;
if (u.u_uid != 0) {
for (p1 = allproc; p1; p1 = p1->p_nxt)
if (p1->p_uid == u.u_uid)
a++;
for (p1 = zombproc; p1; p1 = p1->p_nxt)
if (p1->p_uid == u.u_uid)
a++;
}
/*
* Disallow if
* No processes at all;
* not su and too many procs owned; or
* not su and would take last slot.
*/
p2 = freeproc;
if (p2==NULL)
log(LOG_ERR, "proc: table full\n");
if (p2==NULL || (u.u_uid!=0 && (p2->p_nxt == NULL || a>MAXUPRC))) {
u.u_error = EAGAIN;
return;
}
p1 = u.u_procp;
if (newproc (isvfork)) {
/* Child */
u.u_rval = 0;
u.u_start = time.tv_sec;
bzero(&u.u_ru, sizeof(u.u_ru));
bzero(&u.u_cru, sizeof(u.u_cru));
return;
}
/* Parent */
u.u_rval = p2->p_pid;
}
/*
* fork system call
*/
void
fork()
{
fork1 (0);
}
/*
* vfork system call, fast version of fork
*/
void
vfork()
{
fork1 (1);
}