xref: /csrg-svn/sys/kern/kern_fork.c (revision 29946)
123370Smckusick /*
229090Smckusick  * Copyright (c) 1982, 1986 Regents of the University of California.
323370Smckusick  * All rights reserved.  The Berkeley software License Agreement
423370Smckusick  * specifies the terms and conditions for redistribution.
523370Smckusick  *
6*29946Skarels  *	@(#)kern_fork.c	7.2 (Berkeley) 11/03/86
723370Smckusick  */
812791Ssam 
912791Ssam #include "../machine/reg.h"
1012791Ssam #include "../machine/pte.h"
1112791Ssam #include "../machine/psl.h"
1212791Ssam 
1317090Sbloom #include "param.h"
1417090Sbloom #include "systm.h"
1517090Sbloom #include "map.h"
1617090Sbloom #include "dir.h"
1717090Sbloom #include "user.h"
1817090Sbloom #include "kernel.h"
1917090Sbloom #include "proc.h"
2017090Sbloom #include "inode.h"
2117090Sbloom #include "seg.h"
2217090Sbloom #include "vm.h"
2317090Sbloom #include "text.h"
2417090Sbloom #include "file.h"
2517090Sbloom #include "acct.h"
2617090Sbloom #include "quota.h"
2712791Ssam 
2812791Ssam /*
2912791Ssam  * fork system call.
3012791Ssam  */
3112791Ssam fork()
3212791Ssam {
3312791Ssam 
3412791Ssam 	u.u_cdmap = zdmap;
3512791Ssam 	u.u_csmap = zdmap;
3612791Ssam 	if (swpexpand(u.u_dsize, u.u_ssize, &u.u_cdmap, &u.u_csmap) == 0) {
3712791Ssam 		u.u_r.r_val2 = 0;
3812791Ssam 		return;
3912791Ssam 	}
4012791Ssam 	fork1(0);
4112791Ssam }
4212791Ssam 
4312791Ssam vfork()
4412791Ssam {
4512791Ssam 
4612791Ssam 	fork1(1);
4712791Ssam }
4812791Ssam 
4912791Ssam fork1(isvfork)
5012791Ssam 	int isvfork;
5112791Ssam {
5212791Ssam 	register struct proc *p1, *p2;
5312791Ssam 	register a;
5412791Ssam 
5512791Ssam 	a = 0;
5616528Skarels 	if (u.u_uid != 0) {
5716528Skarels 		for (p1 = allproc; p1; p1 = p1->p_nxt)
5816528Skarels 			if (p1->p_uid == u.u_uid)
5912791Ssam 				a++;
6016528Skarels 		for (p1 = zombproc; p1; p1 = p1->p_nxt)
6116528Skarels 			if (p1->p_uid == u.u_uid)
6216528Skarels 				a++;
6312791Ssam 	}
6412791Ssam 	/*
6512791Ssam 	 * Disallow if
6612791Ssam 	 *  No processes at all;
6712791Ssam 	 *  not su and too many procs owned; or
6812791Ssam 	 *  not su and would take last slot.
6912791Ssam 	 */
7016528Skarels 	p2 = freeproc;
7112791Ssam 	if (p2==NULL)
7212791Ssam 		tablefull("proc");
7316528Skarels 	if (p2==NULL || (u.u_uid!=0 && (p2->p_nxt == NULL || a>MAXUPRC))) {
7412791Ssam 		u.u_error = EAGAIN;
7512791Ssam 		if (!isvfork) {
7626353Skarels 			(void) vsexpand((size_t)0, &u.u_cdmap, 1);
7726353Skarels 			(void) vsexpand((size_t)0, &u.u_csmap, 1);
7812791Ssam 		}
7912791Ssam 		goto out;
8012791Ssam 	}
8112791Ssam 	p1 = u.u_procp;
8212791Ssam 	if (newproc(isvfork)) {
8312791Ssam 		u.u_r.r_val1 = p1->p_pid;
8412791Ssam 		u.u_r.r_val2 = 1;  /* child */
8516716Ssam 		u.u_start = time;
8612791Ssam 		u.u_acflag = AFORK;
8712791Ssam 		return;
8812791Ssam 	}
8912791Ssam 	u.u_r.r_val1 = p2->p_pid;
9012791Ssam 
9112791Ssam out:
9212791Ssam 	u.u_r.r_val2 = 0;
9312791Ssam }
9412791Ssam 
9512791Ssam /*
9612791Ssam  * Create a new process-- the internal version of
9712791Ssam  * sys fork.
9812791Ssam  * It returns 1 in the new process, 0 in the old.
9912791Ssam  */
10012791Ssam newproc(isvfork)
10112791Ssam 	int isvfork;
10212791Ssam {
10312791Ssam 	register struct proc *rpp, *rip;
10412791Ssam 	register int n;
10512791Ssam 	register struct file *fp;
10616528Skarels 	static int pidchecked = 0;
10712791Ssam 
10812791Ssam 	/*
10912791Ssam 	 * First, just locate a slot for a process
11012791Ssam 	 * and copy the useful info from this process into it.
11112791Ssam 	 * The panic "cannot happen" because fork has already
11212791Ssam 	 * checked for the existence of a slot.
11312791Ssam 	 */
11416528Skarels 	mpid++;
11512791Ssam retry:
11612791Ssam 	if (mpid >= 30000) {
11716528Skarels 		mpid = 100;
11816528Skarels 		pidchecked = 0;
11912791Ssam 	}
12016528Skarels 	if (mpid >= pidchecked) {
12116528Skarels 		int doingzomb = 0;
12216578Ssam 
12316528Skarels 		pidchecked = 30000;
12416528Skarels 		/*
12516528Skarels 		 * Scan the proc table to check whether this pid
12616528Skarels 		 * is in use.  Remember the lowest pid that's greater
12716528Skarels 		 * than mpid, so we can avoid checking for a while.
12816528Skarels 		 */
12916528Skarels 		rpp = allproc;
13016528Skarels again:
13116528Skarels 		for (; rpp != NULL; rpp = rpp->p_nxt) {
13216578Ssam 			if (rpp->p_pid == mpid || rpp->p_pgrp == mpid) {
13316528Skarels 				mpid++;
13416528Skarels 				if (mpid >= pidchecked)
13516528Skarels 					goto retry;
13616528Skarels 			}
13716578Ssam 			if (rpp->p_pid > mpid && pidchecked > rpp->p_pid)
13816528Skarels 				pidchecked = rpp->p_pid;
13916578Ssam 			if (rpp->p_pgrp > mpid && pidchecked > rpp->p_pgrp)
14016528Skarels 				pidchecked = rpp->p_pgrp;
14116528Skarels 		}
14216528Skarels 		if (!doingzomb) {
14316528Skarels 			doingzomb = 1;
14416528Skarels 			rpp = zombproc;
14516528Skarels 			goto again;
14616528Skarels 		}
14712791Ssam 	}
14816528Skarels 	if ((rpp = freeproc) == NULL)
14912791Ssam 		panic("no procs");
15012791Ssam 
15116528Skarels 	freeproc = rpp->p_nxt;			/* off freeproc */
15216528Skarels 	rpp->p_nxt = allproc;			/* onto allproc */
15316528Skarels 	rpp->p_nxt->p_prev = &rpp->p_nxt;	/*   (allproc is never NULL) */
15416528Skarels 	rpp->p_prev = &allproc;
15516528Skarels 	allproc = rpp;
15616528Skarels 
15712791Ssam 	/*
15812791Ssam 	 * Make a proc table entry for the new process.
15912791Ssam 	 */
16012791Ssam 	rip = u.u_procp;
16112791Ssam #ifdef QUOTA
16212791Ssam 	rpp->p_quota = rip->p_quota;
16312791Ssam 	rpp->p_quota->q_cnt++;
16412791Ssam #endif
165*29946Skarels #if defined(tahoe)
166*29946Skarels 	rpp->p_ckey = rip->p_ckey;
167*29946Skarels 	rpp->p_dkey = 0;
168*29946Skarels #endif
16912791Ssam 	rpp->p_stat = SIDL;
17012791Ssam 	timerclear(&rpp->p_realtimer.it_value);
17112882Ssam 	rpp->p_flag = SLOAD | (rip->p_flag & (SPAGI|SOUSIG));
17212791Ssam 	if (isvfork) {
17312791Ssam 		rpp->p_flag |= SVFORK;
17412791Ssam 		rpp->p_ndx = rip->p_ndx;
17512791Ssam 	} else
17612791Ssam 		rpp->p_ndx = rpp - proc;
17712791Ssam 	rpp->p_uid = rip->p_uid;
17812791Ssam 	rpp->p_pgrp = rip->p_pgrp;
17912791Ssam 	rpp->p_nice = rip->p_nice;
18012791Ssam 	rpp->p_textp = isvfork ? 0 : rip->p_textp;
18112791Ssam 	rpp->p_pid = mpid;
18212791Ssam 	rpp->p_ppid = rip->p_pid;
18312791Ssam 	rpp->p_pptr = rip;
18412791Ssam 	rpp->p_osptr = rip->p_cptr;
18512791Ssam 	if (rip->p_cptr)
18612791Ssam 		rip->p_cptr->p_ysptr = rpp;
18712791Ssam 	rpp->p_ysptr = NULL;
18812791Ssam 	rpp->p_cptr = NULL;
18912791Ssam 	rip->p_cptr = rpp;
19012791Ssam 	rpp->p_time = 0;
19112791Ssam 	rpp->p_cpu = 0;
19212882Ssam 	rpp->p_sigmask = rip->p_sigmask;
19312882Ssam 	rpp->p_sigcatch = rip->p_sigcatch;
19412882Ssam 	rpp->p_sigignore = rip->p_sigignore;
19512882Ssam 	/* take along any pending signals like stops? */
19612791Ssam 	if (isvfork) {
19712791Ssam 		rpp->p_tsize = rpp->p_dsize = rpp->p_ssize = 0;
19812791Ssam 		rpp->p_szpt = clrnd(ctopt(UPAGES));
19912791Ssam 		forkstat.cntvfork++;
20012791Ssam 		forkstat.sizvfork += rip->p_dsize + rip->p_ssize;
20112791Ssam 	} else {
20212791Ssam 		rpp->p_tsize = rip->p_tsize;
20312791Ssam 		rpp->p_dsize = rip->p_dsize;
20412791Ssam 		rpp->p_ssize = rip->p_ssize;
20512791Ssam 		rpp->p_szpt = rip->p_szpt;
20612791Ssam 		forkstat.cntfork++;
20712791Ssam 		forkstat.sizfork += rip->p_dsize + rip->p_ssize;
20812791Ssam 	}
20912791Ssam 	rpp->p_rssize = 0;
21012791Ssam 	rpp->p_maxrss = rip->p_maxrss;
21112791Ssam 	rpp->p_wchan = 0;
21212791Ssam 	rpp->p_slptime = 0;
21312791Ssam 	rpp->p_pctcpu = 0;
21412791Ssam 	rpp->p_cpticks = 0;
21512791Ssam 	n = PIDHASH(rpp->p_pid);
21616528Skarels 	rpp->p_idhash = pidhash[n];
21712791Ssam 	pidhash[n] = rpp - proc;
21812791Ssam 	multprog++;
21912791Ssam 
22012791Ssam 	/*
22112791Ssam 	 * Increase reference counts on shared objects.
22212791Ssam 	 */
22321102Skarels 	for (n = 0; n <= u.u_lastfile; n++) {
22412791Ssam 		fp = u.u_ofile[n];
22512791Ssam 		if (fp == NULL)
22612791Ssam 			continue;
22712791Ssam 		fp->f_count++;
22812791Ssam 	}
22912791Ssam 	u.u_cdir->i_count++;
23012791Ssam 	if (u.u_rdir)
23112791Ssam 		u.u_rdir->i_count++;
23212791Ssam 
23312791Ssam 	/*
23412791Ssam 	 * This begins the section where we must prevent the parent
23512791Ssam 	 * from being swapped.
23612791Ssam 	 */
23712791Ssam 	rip->p_flag |= SKEEP;
23812791Ssam 	if (procdup(rpp, isvfork))
23912791Ssam 		return (1);
24012791Ssam 
24112791Ssam 	/*
24212791Ssam 	 * Make child runnable and add to run queue.
24312791Ssam 	 */
24426277Skarels 	(void) splclock();
24512791Ssam 	rpp->p_stat = SRUN;
24612791Ssam 	setrq(rpp);
24712791Ssam 	(void) spl0();
24812791Ssam 
24912791Ssam 	/*
25012791Ssam 	 * Cause child to take a non-local goto as soon as it runs.
25112791Ssam 	 * On older systems this was done with SSWAP bit in proc
25212791Ssam 	 * table; on VAX we use u.u_pcb.pcb_sswap so don't need
25312791Ssam 	 * to do rpp->p_flag |= SSWAP.  Actually do nothing here.
25412791Ssam 	 */
25512791Ssam 	/* rpp->p_flag |= SSWAP; */
25612791Ssam 
25712791Ssam 	/*
25812791Ssam 	 * Now can be swapped.
25912791Ssam 	 */
26012791Ssam 	rip->p_flag &= ~SKEEP;
26112791Ssam 
26212791Ssam 	/*
26312791Ssam 	 * If vfork make chain from parent process to child
26412791Ssam 	 * (where virtal memory is temporarily).  Wait for
26512791Ssam 	 * child to finish, steal virtual memory back,
26612791Ssam 	 * and wakeup child to let it die.
26712791Ssam 	 */
26812791Ssam 	if (isvfork) {
26912791Ssam 		u.u_procp->p_xlink = rpp;
27012791Ssam 		u.u_procp->p_flag |= SNOVM;
27112791Ssam 		while (rpp->p_flag & SVFORK)
27212791Ssam 			sleep((caddr_t)rpp, PZERO - 1);
27312791Ssam 		if ((rpp->p_flag & SLOAD) == 0)
27412791Ssam 			panic("newproc vfork");
27512791Ssam 		uaccess(rpp, Vfmap, &vfutl);
27612791Ssam 		u.u_procp->p_xlink = 0;
27712791Ssam 		vpassvm(rpp, u.u_procp, &vfutl, &u, Vfmap);
27812791Ssam 		u.u_procp->p_flag &= ~SNOVM;
27912791Ssam 		rpp->p_ndx = rpp - proc;
28012791Ssam 		rpp->p_flag |= SVFDONE;
28112791Ssam 		wakeup((caddr_t)rpp);
28212791Ssam 	}
28312791Ssam 
28412791Ssam 	/*
28512791Ssam 	 * 0 return means parent.
28612791Ssam 	 */
28712791Ssam 	return (0);
28812791Ssam }
289