# /* */ #include "../param.h" #include "../user.h" #include "../proc.h" #include "../text.h" #include "../systm.h" #include "../file.h" #include "../inode.h" #include "../buf.h" /* * Give up the processor till a wakeup occurs * on chan, at which time the process * enters the scheduling queue at priority pri. * The most important effect of pri is that when * pri<0 a signal cannot disturb the sleep; * if pri>=0 signals will be processed. * Callers of this routine must be prepared for * premature return, and check that the reason for * sleeping has gone away. */ sleep(chan, pri) { register *rp, s; s = PS->integ; rp = u.u_procp; if(pri >= 0) { if(issig()) goto psig; spl6(); rp->p_wchan = chan; rp->p_stat = SWAIT; rp->p_pri = pri; spl0(); if(runin != 0) { runin = 0; wakeup(&runin); } swtch(); if(issig()) goto psig; } else { spl6(); rp->p_wchan = chan; rp->p_stat = SSLEEP; rp->p_pri = pri; spl0(); swtch(); } PS->integ = s; return; /* * If priority was low (>=0) and * there has been a signal, * execute non-local goto to * the qsav location. * (see trap1/trap.c) */ psig: aretu(u.u_qsav); } /* * Wake up all processes sleeping on chan. */ wakeup(chan) { register struct proc *p; register c, i; c = chan; p = &proc[0]; i = NPROC; do { if(p->p_wchan == c) { setrun(p); } p++; } while(--i); } /* * Set the process running; * arrange for it to be swapped in if necessary. */ setrun(p) { register struct proc *rp; rp = p; rp->p_wchan = 0; rp->p_stat = SRUN; if(rp->p_pri < curpri) runrun++; if(runout != 0 && (rp->p_flag&SLOAD) == 0) { runout = 0; wakeup(&runout); } } /* * Set user priority. * The rescheduling flag (runrun) * is set if the priority is higher * than the currently running process. */ setpri(up) { register *pp, p; pp = up; p = (pp->p_cpu & 0377)/16; p =+ PUSER + pp->p_nice; if(p > 127) p = 127; if(p > curpri) runrun++; pp->p_pri = p; } /* * The main loop of the scheduling (swapping) * process. * The basic idea is: * see if anyone wants to be swapped in; * swap out processes until there is room; * swap him in; * repeat. * Although it is not remarkably evident, the basic * synchronization here is on the runin flag, which is * slept on and is set once per second by the clock routine. * Core shuffling therefore takes place once per second. * * panic: swap error -- IO error while swapping. * this is the one panic that should be * handled in a less drastic way. Its * very hard. */ sched() { struct proc *p1; register struct proc *rp; register a, n; /* * find user to swap in * of users ready, select one out longest */ goto loop; sloop: runin++; sleep(&runin, PSWP); loop: spl6(); n = -1; for(rp = &proc[0]; rp < &proc[NPROC]; rp++) if(rp->p_stat==SRUN && (rp->p_flag&SLOAD)==0 && rp->p_time > n) { p1 = rp; n = rp->p_time; } if(n == -1) { runout++; sleep(&runout, PSWP); goto loop; } /* * see if there is core for that process */ spl0(); rp = p1; a = rp->p_size; if((rp=rp->p_textp) != NULL) if(rp->x_ccount == 0) a =+ rp->x_size; if((a=malloc(coremap, a)) != NULL) goto found2; /* * none found, * look around for easy core */ spl6(); for(rp = &proc[0]; rp < &proc[NPROC]; rp++) if((rp->p_flag&(SSYS|SLOCK|SLOAD))==SLOAD && (rp->p_stat == SWAIT || rp->p_stat==SSTOP)) goto found1; /* * no easy core, * if this process is deserving, * look around for * oldest process in core */ if(n < 3) goto sloop; n = -1; for(rp = &proc[0]; rp < &proc[NPROC]; rp++) if((rp->p_flag&(SSYS|SLOCK|SLOAD))==SLOAD && (rp->p_stat==SRUN || rp->p_stat==SSLEEP) && rp->p_time > n) { p1 = rp; n = rp->p_time; } if(n < 2) goto sloop; rp = p1; /* * swap user out */ found1: spl0(); rp->p_flag =& ~SLOAD; xswap(rp, 1, 0); goto loop; /* * swap user in */ found2: if((rp=p1->p_textp) != NULL) { if(rp->x_ccount == 0) { if(swap(rp->x_daddr, a, rp->x_size, B_READ)) goto swaper; rp->x_caddr = a; a =+ rp->x_size; } rp->x_ccount++; } rp = p1; if(swap(rp->p_addr, a, rp->p_size, B_READ)) goto swaper; mfree(swapmap, (rp->p_size+7)/8, rp->p_addr); rp->p_addr = a; rp->p_flag =| SLOAD; rp->p_time = 0; goto loop; swaper: panic("swap error"); } /* * This routine is called to reschedule the CPU. * if the calling process is not in RUN state, * arrangements for it to restart must have * been made elsewhere, usually by calling via sleep. */ swtch() { static struct proc *p; register i, n; register struct proc *rp; if(p == NULL) p = &proc[0]; /* * Remember stack of caller */ savu(u.u_rsav); /* * Switch to scheduler's stack */ retu(proc[0].p_addr); loop: runrun = 0; rp = p; p = NULL; n = 128; /* * Search for highest-priority runnable process */ i = NPROC; do { rp++; if(rp >= &proc[NPROC]) rp = &proc[0]; if(rp->p_stat==SRUN && (rp->p_flag&SLOAD)!=0) { if(rp->p_pri < n) { p = rp; n = rp->p_pri; } } } while(--i); /* * If no process is runnable, idle. */ if(p == NULL) { p = rp; idle(); goto loop; } rp = p; curpri = n; /* * Switch to stack of the new process and set up * his segmentation registers. */ retu(rp->p_addr); sureg(); /* * If the new process paused because it was * swapped out, set the stack level to the last call * to savu(u_ssav). This means that the return * which is executed immediately after the call to aretu * actually returns from the last routine which did * the savu. * * You are not expected to understand this. */ if(rp->p_flag&SSWAP) { rp->p_flag =& ~SSWAP; aretu(u.u_ssav); } /* * The value returned here has many subtle implications. * See the newproc comments. */ return(1); } /* * Create a new process-- the internal version of * sys fork. * It returns 1 in the new process. * How this happens is rather hard to understand. * The essential fact is that the new process is created * in such a way that appears to have started executing * in the same call to newproc as the parent; * but in fact the code that runs is that of swtch. * The subtle implication of the returned value of swtch * (see above) is that this is the value that newproc's * caller in the new process sees. */ newproc() { int a1, a2; struct proc *p, *up; register struct proc *rpp; register *rip, n; p = NULL; /* * 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. */ retry: mpid++; if(mpid < 0) { mpid = 0; goto retry; } for(rpp = &proc[0]; rpp < &proc[NPROC]; rpp++) { if(rpp->p_stat == NULL && p==NULL) p = rpp; if (rpp->p_pid==mpid) goto retry; } if ((rpp = p)==NULL) panic("no procs"); /* * make proc entry for new proc */ rip = u.u_procp; up = rip; rpp->p_stat = SRUN; rpp->p_flag = SLOAD; rpp->p_uid = rip->p_uid; rpp->p_ttyp = rip->p_ttyp; rpp->p_nice = rip->p_nice; rpp->p_textp = rip->p_textp; rpp->p_pid = mpid; rpp->p_ppid = rip->p_pid; rpp->p_time = 0; /* * make duplicate entries * where needed */ for(rip = &u.u_ofile[0]; rip < &u.u_ofile[NOFILE];) if((rpp = *rip++) != NULL) rpp->f_count++; if((rpp=up->p_textp) != NULL) { rpp->x_count++; rpp->x_ccount++; } u.u_cdir->i_count++; /* * Partially simulate the environment * of the new process so that when it is actually * created (by copying) it will look right. */ savu(u.u_rsav); rpp = p; u.u_procp = rpp; rip = up; n = rip->p_size; a1 = rip->p_addr; rpp->p_size = n; a2 = malloc(coremap, n); /* * If there is not enough core for the * new process, swap out the current process to generate the * copy. */ if(a2 == NULL) { rip->p_stat = SIDL; rpp->p_addr = a1; savu(u.u_ssav); xswap(rpp, 0, 0); rpp->p_flag =| SSWAP; rip->p_stat = SRUN; } else { /* * There is core, so just copy. */ rpp->p_addr = a2; while(n--) copyseg(a1++, a2++); } u.u_procp = rip; return(0); } /* * Change the size of the data+stack regions of the process. * If the size is shrinking, it's easy-- just release the extra core. * If it's growing, and there is core, just allocate it * and copy the image, taking care to reset registers to account * for the fact that the system's stack has moved. * If there is no core, arrange for the process to be swapped * out after adjusting the size requirement-- when it comes * in, enough core will be allocated. * Because of the ssave and SSWAP flags, control will * resume after the swap in swtch, which executes the return * from this stack level. * * After the expansion, the caller will take care of copying * the user's stack towards or away from the data area. */ expand(newsize) { int i, n; register *p, a1, a2; p = u.u_procp; n = p->p_size; p->p_size = newsize; a1 = p->p_addr; if(n >= newsize) { mfree(coremap, n-newsize, a1+newsize); return; } savu(u.u_rsav); a2 = malloc(coremap, newsize); if(a2 == NULL) { savu(u.u_ssav); xswap(p, 1, n); p->p_flag =| SSWAP; swtch(); /* no return */ } p->p_addr = a2; for(i=0; ip_addr); sureg(); }