BSD 4_3 release
[unix-history] / usr / src / sys / sys / kern_fork.c
/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)kern_fork.c 7.1 (Berkeley) 6/5/86
*/
#include "../machine/reg.h"
#include "../machine/pte.h"
#include "../machine/psl.h"
#include "param.h"
#include "systm.h"
#include "map.h"
#include "dir.h"
#include "user.h"
#include "kernel.h"
#include "proc.h"
#include "inode.h"
#include "seg.h"
#include "vm.h"
#include "text.h"
#include "file.h"
#include "acct.h"
#include "quota.h"
/*
* fork system call.
*/
fork()
{
u.u_cdmap = zdmap;
u.u_csmap = zdmap;
if (swpexpand(u.u_dsize, u.u_ssize, &u.u_cdmap, &u.u_csmap) == 0) {
u.u_r.r_val2 = 0;
return;
}
fork1(0);
}
vfork()
{
fork1(1);
}
fork1(isvfork)
int isvfork;
{
register struct proc *p1, *p2;
register a;
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)
tablefull("proc");
if (p2==NULL || (u.u_uid!=0 && (p2->p_nxt == NULL || a>MAXUPRC))) {
u.u_error = EAGAIN;
if (!isvfork) {
(void) vsexpand((size_t)0, &u.u_cdmap, 1);
(void) vsexpand((size_t)0, &u.u_csmap, 1);
}
goto out;
}
p1 = u.u_procp;
if (newproc(isvfork)) {
u.u_r.r_val1 = p1->p_pid;
u.u_r.r_val2 = 1; /* child */
u.u_start = time;
u.u_acflag = AFORK;
return;
}
u.u_r.r_val1 = p2->p_pid;
out:
u.u_r.r_val2 = 0;
}
/*
* Create a new process-- the internal version of
* sys fork.
* It returns 1 in the new process, 0 in the old.
*/
newproc(isvfork)
int isvfork;
{
register struct proc *rpp, *rip;
register int n;
register struct file *fp;
static int pidchecked = 0;
/*
* 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.
*/
rpp = allproc;
again:
for (; rpp != NULL; rpp = rpp->p_nxt) {
if (rpp->p_pid == mpid || rpp->p_pgrp == mpid) {
mpid++;
if (mpid >= pidchecked)
goto retry;
}
if (rpp->p_pid > mpid && pidchecked > rpp->p_pid)
pidchecked = rpp->p_pid;
if (rpp->p_pgrp > mpid && pidchecked > rpp->p_pgrp)
pidchecked = rpp->p_pgrp;
}
if (!doingzomb) {
doingzomb = 1;
rpp = zombproc;
goto again;
}
}
if ((rpp = freeproc) == NULL)
panic("no procs");
freeproc = rpp->p_nxt; /* off freeproc */
rpp->p_nxt = allproc; /* onto allproc */
rpp->p_nxt->p_prev = &rpp->p_nxt; /* (allproc is never NULL) */
rpp->p_prev = &allproc;
allproc = rpp;
/*
* Make a proc table entry for the new process.
*/
rip = u.u_procp;
#ifdef QUOTA
rpp->p_quota = rip->p_quota;
rpp->p_quota->q_cnt++;
#endif
rpp->p_stat = SIDL;
timerclear(&rpp->p_realtimer.it_value);
rpp->p_flag = SLOAD | (rip->p_flag & (SPAGI|SOUSIG));
if (isvfork) {
rpp->p_flag |= SVFORK;
rpp->p_ndx = rip->p_ndx;
} else
rpp->p_ndx = rpp - proc;
rpp->p_uid = rip->p_uid;
rpp->p_pgrp = rip->p_pgrp;
rpp->p_nice = rip->p_nice;
rpp->p_textp = isvfork ? 0 : rip->p_textp;
rpp->p_pid = mpid;
rpp->p_ppid = rip->p_pid;
rpp->p_pptr = rip;
rpp->p_osptr = rip->p_cptr;
if (rip->p_cptr)
rip->p_cptr->p_ysptr = rpp;
rpp->p_ysptr = NULL;
rpp->p_cptr = NULL;
rip->p_cptr = rpp;
rpp->p_time = 0;
rpp->p_cpu = 0;
rpp->p_sigmask = rip->p_sigmask;
rpp->p_sigcatch = rip->p_sigcatch;
rpp->p_sigignore = rip->p_sigignore;
/* take along any pending signals like stops? */
if (isvfork) {
rpp->p_tsize = rpp->p_dsize = rpp->p_ssize = 0;
rpp->p_szpt = clrnd(ctopt(UPAGES));
forkstat.cntvfork++;
forkstat.sizvfork += rip->p_dsize + rip->p_ssize;
} else {
rpp->p_tsize = rip->p_tsize;
rpp->p_dsize = rip->p_dsize;
rpp->p_ssize = rip->p_ssize;
rpp->p_szpt = rip->p_szpt;
forkstat.cntfork++;
forkstat.sizfork += rip->p_dsize + rip->p_ssize;
}
rpp->p_rssize = 0;
rpp->p_maxrss = rip->p_maxrss;
rpp->p_wchan = 0;
rpp->p_slptime = 0;
rpp->p_pctcpu = 0;
rpp->p_cpticks = 0;
n = PIDHASH(rpp->p_pid);
rpp->p_idhash = pidhash[n];
pidhash[n] = rpp - proc;
multprog++;
/*
* 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++;
/*
* This begins the section where we must prevent the parent
* from being swapped.
*/
rip->p_flag |= SKEEP;
if (procdup(rpp, isvfork))
return (1);
/*
* Make child runnable and add to run queue.
*/
(void) splclock();
rpp->p_stat = SRUN;
setrq(rpp);
(void) spl0();
/*
* Cause child to take a non-local goto as soon as it runs.
* On older systems this was done with SSWAP bit in proc
* table; on VAX we use u.u_pcb.pcb_sswap so don't need
* to do rpp->p_flag |= SSWAP. Actually do nothing here.
*/
/* rpp->p_flag |= SSWAP; */
/*
* Now can be swapped.
*/
rip->p_flag &= ~SKEEP;
/*
* If vfork make chain from parent process to child
* (where virtal memory is temporarily). Wait for
* child to finish, steal virtual memory back,
* and wakeup child to let it die.
*/
if (isvfork) {
u.u_procp->p_xlink = rpp;
u.u_procp->p_flag |= SNOVM;
while (rpp->p_flag & SVFORK)
sleep((caddr_t)rpp, PZERO - 1);
if ((rpp->p_flag & SLOAD) == 0)
panic("newproc vfork");
uaccess(rpp, Vfmap, &vfutl);
u.u_procp->p_xlink = 0;
vpassvm(rpp, u.u_procp, &vfutl, &u, Vfmap);
u.u_procp->p_flag &= ~SNOVM;
rpp->p_ndx = rpp - proc;
rpp->p_flag |= SVFDONE;
wakeup((caddr_t)rpp);
}
/*
* 0 return means parent.
*/
return (0);
}