123370Smckusick /* 223370Smckusick * Copyright (c) 1982 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*26277Skarels * @(#)kern_fork.c 6.8 (Berkeley) 02/20/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) { 7612791Ssam (void) vsexpand(0, &u.u_cdmap, 1); 7712791Ssam (void) vsexpand(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 16512791Ssam rpp->p_stat = SIDL; 16612791Ssam timerclear(&rpp->p_realtimer.it_value); 16712882Ssam rpp->p_flag = SLOAD | (rip->p_flag & (SPAGI|SOUSIG)); 16812791Ssam if (isvfork) { 16912791Ssam rpp->p_flag |= SVFORK; 17012791Ssam rpp->p_ndx = rip->p_ndx; 17112791Ssam } else 17212791Ssam rpp->p_ndx = rpp - proc; 17312791Ssam rpp->p_uid = rip->p_uid; 17412791Ssam rpp->p_pgrp = rip->p_pgrp; 17512791Ssam rpp->p_nice = rip->p_nice; 17612791Ssam rpp->p_textp = isvfork ? 0 : rip->p_textp; 17712791Ssam rpp->p_pid = mpid; 17812791Ssam rpp->p_ppid = rip->p_pid; 17912791Ssam rpp->p_pptr = rip; 18012791Ssam rpp->p_osptr = rip->p_cptr; 18112791Ssam if (rip->p_cptr) 18212791Ssam rip->p_cptr->p_ysptr = rpp; 18312791Ssam rpp->p_ysptr = NULL; 18412791Ssam rpp->p_cptr = NULL; 18512791Ssam rip->p_cptr = rpp; 18612791Ssam rpp->p_time = 0; 18712791Ssam rpp->p_cpu = 0; 18812882Ssam rpp->p_sigmask = rip->p_sigmask; 18912882Ssam rpp->p_sigcatch = rip->p_sigcatch; 19012882Ssam rpp->p_sigignore = rip->p_sigignore; 19112882Ssam /* take along any pending signals like stops? */ 19212791Ssam if (isvfork) { 19312791Ssam rpp->p_tsize = rpp->p_dsize = rpp->p_ssize = 0; 19412791Ssam rpp->p_szpt = clrnd(ctopt(UPAGES)); 19512791Ssam forkstat.cntvfork++; 19612791Ssam forkstat.sizvfork += rip->p_dsize + rip->p_ssize; 19712791Ssam } else { 19812791Ssam rpp->p_tsize = rip->p_tsize; 19912791Ssam rpp->p_dsize = rip->p_dsize; 20012791Ssam rpp->p_ssize = rip->p_ssize; 20112791Ssam rpp->p_szpt = rip->p_szpt; 20212791Ssam forkstat.cntfork++; 20312791Ssam forkstat.sizfork += rip->p_dsize + rip->p_ssize; 20412791Ssam } 20512791Ssam rpp->p_rssize = 0; 20612791Ssam rpp->p_maxrss = rip->p_maxrss; 20712791Ssam rpp->p_wchan = 0; 20812791Ssam rpp->p_slptime = 0; 20912791Ssam rpp->p_pctcpu = 0; 21012791Ssam rpp->p_cpticks = 0; 21112791Ssam n = PIDHASH(rpp->p_pid); 21216528Skarels rpp->p_idhash = pidhash[n]; 21312791Ssam pidhash[n] = rpp - proc; 21412791Ssam multprog++; 21512791Ssam 21612791Ssam /* 21712791Ssam * Increase reference counts on shared objects. 21812791Ssam */ 21921102Skarels for (n = 0; n <= u.u_lastfile; n++) { 22012791Ssam fp = u.u_ofile[n]; 22112791Ssam if (fp == NULL) 22212791Ssam continue; 22312791Ssam fp->f_count++; 22412791Ssam } 22512791Ssam u.u_cdir->i_count++; 22612791Ssam if (u.u_rdir) 22712791Ssam u.u_rdir->i_count++; 22812791Ssam 22912791Ssam /* 23012791Ssam * This begins the section where we must prevent the parent 23112791Ssam * from being swapped. 23212791Ssam */ 23312791Ssam rip->p_flag |= SKEEP; 23412791Ssam if (procdup(rpp, isvfork)) 23512791Ssam return (1); 23612791Ssam 23712791Ssam /* 23812791Ssam * Make child runnable and add to run queue. 23912791Ssam */ 240*26277Skarels (void) splclock(); 24112791Ssam rpp->p_stat = SRUN; 24212791Ssam setrq(rpp); 24312791Ssam (void) spl0(); 24412791Ssam 24512791Ssam /* 24612791Ssam * Cause child to take a non-local goto as soon as it runs. 24712791Ssam * On older systems this was done with SSWAP bit in proc 24812791Ssam * table; on VAX we use u.u_pcb.pcb_sswap so don't need 24912791Ssam * to do rpp->p_flag |= SSWAP. Actually do nothing here. 25012791Ssam */ 25112791Ssam /* rpp->p_flag |= SSWAP; */ 25212791Ssam 25312791Ssam /* 25412791Ssam * Now can be swapped. 25512791Ssam */ 25612791Ssam rip->p_flag &= ~SKEEP; 25712791Ssam 25812791Ssam /* 25912791Ssam * If vfork make chain from parent process to child 26012791Ssam * (where virtal memory is temporarily). Wait for 26112791Ssam * child to finish, steal virtual memory back, 26212791Ssam * and wakeup child to let it die. 26312791Ssam */ 26412791Ssam if (isvfork) { 26512791Ssam u.u_procp->p_xlink = rpp; 26612791Ssam u.u_procp->p_flag |= SNOVM; 26712791Ssam while (rpp->p_flag & SVFORK) 26812791Ssam sleep((caddr_t)rpp, PZERO - 1); 26912791Ssam if ((rpp->p_flag & SLOAD) == 0) 27012791Ssam panic("newproc vfork"); 27112791Ssam uaccess(rpp, Vfmap, &vfutl); 27212791Ssam u.u_procp->p_xlink = 0; 27312791Ssam vpassvm(rpp, u.u_procp, &vfutl, &u, Vfmap); 27412791Ssam u.u_procp->p_flag &= ~SNOVM; 27512791Ssam rpp->p_ndx = rpp - proc; 27612791Ssam rpp->p_flag |= SVFDONE; 27712791Ssam wakeup((caddr_t)rpp); 27812791Ssam } 27912791Ssam 28012791Ssam /* 28112791Ssam * 0 return means parent. 28212791Ssam */ 28312791Ssam return (0); 28412791Ssam } 285