# /* */ #include "../defines.h" #include "../param.h" #include "../user.h" #include "../proc.h" #include "../text.h" #include "../file.h" #include "../inode.h" #include "../buf.h" #ifdef AUSAML #include "../lnode.h" #endif AUSAML #include "../systm.h" #ifdef ONCE #include "../seg.h" #endif ONCE #ifdef MAX_PROC struct proc *max_proc &proc[0]; #endif MAX_PROC #ifdef PROCESS_QUEUES struct proc *runq; #endif PROCESS_QUEUES /* * 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. */ #ifdef PROCESS_QUEUES sleep(chan, pri) { register *rp, s; #ifdef BETTER_PANIC extern char *panicstr; #endif BETTER_PANIC s = PS->integ; #ifdef BETTER_PANIC if(panicstr) { /* * panic has been called * Refuse to reschedule */ spl0(); idle(); PS->integ = s; return; } #endif BETTER_PANIC rp = u.u_procp; spl6(); rp->p_stat = SSLEEP; rp->p_wchan = chan; rp->p_pri = pri; if(pri >= 0) { if(issig()) { rp->p_wchan = 0; rp->p_stat = SRUN; spl0(); goto psig; } spl0(); swtch(); if(issig()) goto psig; } else { 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); } #else (PROCESS_QUEUES) 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; setrun(&proc[0]); /* no need for wakeup */ } 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); } #endif PROCESS_QUEUES /* * Wake up all processes sleeping on chan. */ wakeup(chan) { register struct proc *p; register c, i; c = chan; p = &proc[0]; #ifdef MAX_PROC i = max_proc - p + 1; #else i = NPROC; #endif MAX_PROC do { #ifdef PROCESS_QUEUES if(p->p_wchan == c && p->p_stat == SSLEEP) #else if(p->p_wchan == c) #endif PROCESS_QUEUES setrun(p); p++; } while(--i); } #ifdef PROCESS_QUEUES /* * Put a process on the runable queue. * The process is always put on the front of the queue, * but round-robin scheduling is assured by swtch, which * takes the last of eqivalent processes. */ setrq(p) register struct proc *p; { register struct proc *q; register s; s = PS->integ; spl6(); for(q = runq; q != NULL; q = q->p_link) { if(q == p) { printf("proc on q\n"); goto out; } } p->p_link = runq; runq = p; out: PS->integ = s; } #endif PROCESS_QUEUES /* * Set the process running; * arrange for it to be swapped in if necessary. * The rescheduling flag (runrun) * is set if the priority is better * than the currently running process. */ setrun(p) register struct proc *p; { p->p_wchan = 0; p->p_stat = SRUN; #ifdef PROCESS_QUEUES setrq(p); #endif PROCESS_QUEUES if((p->p_flag&SLOAD) == 0) { p->p_time = 0; if(runout != 0) { runout = 0; setrun(&proc[0]); } } else if(p->p_pri < curpri) runrun++; } /* * Set user priority. * * PRIORATE (the rate at which priority is aged * for cpu-bound processes) can be found in param.h */ setpri(up) register struct proc *up; /* fix000 */ { register p; p = (up->p_cpu & 0377)/PRIORATE; p =+ PUSER+up->p_nice; if(p > 127) p = 127; up->p_pri = p; return(p); /* fix025 See trap.c */ } /* * 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. * The runout flag is set whenever someone is swapped out. * Sched sleeps on it awaiting work. * * Sched sleeps on runin whenever it cannot find enough * core (by swapping out or otherwise) to fit the * selected swapped process. It is awakend when the * core situation changes and in any event once per second. */ #ifndef PROCESS_QUEUES 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: spl6(); /* fix019 */ runin++; sleep(&runin, PSWP); spl0(); /* fix019 */ loop: /* spl6(); /* fix019 */ n = -1; #ifndef MAX_PROC for(rp = &proc[0]; rp < &proc[NPROC]; rp++) #else for(rp = &proc[0]; rp <= max_proc; rp++) #endif MAX_PROC if(rp->p_stat==SRUN && (rp->p_flag&SLOAD)==0 && rp->p_time > n) { p1 = rp; n = rp->p_time; } if(n == -1) { spl6(); /* fix019 */ runout++; sleep(&runout, PSWP); spl0(); /* fix019 */ goto loop; } /* * see if there is core for that process */ /* spl0(); /* fix019 */ rp = p1; a = rp->p_size; if((rp=rp->p_textp) != NULL) { #ifdef LOWER_TEXT_SWAPS | SHARED_DATA xlock(rp); #endif LOWER_TEXT_SWAPS | SHARED_DATA 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(); /* fix019 */ #ifndef MAX_PROC for(rp = &proc[0]; rp < &proc[NPROC]; rp++) #else for(rp = &proc[0]; rp <= max_proc; rp++) #endif MAX_PROC 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; #ifndef MAX_PROC for(rp = &proc[0]; rp < &proc[NPROC]; rp++) #else for(rp = &proc[0]; rp <= max_proc; rp++) #endif MAX_PROC 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(); /* fix019 */ rp->p_flag =& ~SLOAD; xswap(rp, 1, 0); goto loop; /* * swap user in */ found2: if(rp != NULL) { if(rp->x_ccount == 0) { #ifdef LOWER_TEXT_SWAPS | SHARED_DATA rp->x_flag =| TXTBUSY; #endif LOWER_TEXT_SWAPS | SHARED_DATA swap(rp->x_daddr, a, rp->x_size, B_READ); rp->x_caddr = a; a =+ rp->x_size; #ifdef LOWER_TEXT_SWAPS | SHARED_DATA xrele(rp); #endif LOWER_TEXT_SWAPS | SHARED_DATA } rp->x_ccount++; } rp = p1; swap(rp->p_addr, a, rp->p_size, B_READ); mfree(swapmap, (rp->p_size+7)/8, rp->p_addr); rp->p_addr = a; rp->p_flag =| SLOAD; rp->p_time = 0; goto loop; } #else sched() { register struct proc *rp, *p; register outage, inage; int swapri, a; /* * find user to swap in; * of users ready, select one out longest */ loop: spl6(); outage = -1; for(rp = runq; rp != NULL; rp = rp->p_link) { if((rp->p_flag&SLOAD) == 0 && rp->p_time > outage) { p = rp; outage = rp->p_time; } } /* * If there is no one there, wait. */ if(outage < 0) { runout++; sleep(&runout, PSWP); goto loop; } spl0(); /* * See if there is core for that process; * if so, swap it in. */ if(swapin(p)) goto loop; /* * none found, so search for a candidate for swapping. */ /* spl6(); /* fix019 */ swapri = 0; /* this is a tunable value (max = 0) */ inage = 0; #ifdef MAX_PROC for(rp = &proc[0]; rp <= max_proc; rp++) #else for(rp = &proc[0]; rp < &proc[NPROC]; rp++) #endif MAX_PROC { if((rp->p_flag&(SSYS|SLOCK|SLOAD)) != SLOAD) continue; #ifdef ZOMBIE if(rp->p_stat == SZOMB) continue; #endif ZOMBIE #ifdef SHARED_DATA | LOWER_TEXT_SWAPS if(rp->p_textp && rp->p_textp->x_flag&TXTBUSY) continue; #endif SHARED_DATA | LOWER_TEXT_SWAPS if(rp->p_stat == SSLEEP || rp->p_stat == SSTOP) { a = (rp->p_pri>>1) + rp->p_time; if(a > swapri) { p = rp; swapri = a; } } else if(swapri <= 0 && rp->p_stat == SRUN && rp->p_time > inage) { p = rp; inage = rp->p_time; } } /* spl0(); /* fix019 */ /* * Swap found user out if sleeping, * or if he has spent at least 4 seconds in core and * the swapped-out process has spent at 2 seconds out. * Otherwise wait a bit and try again. */ if(swapri > 0 || (outage >= 2 && inage >= 4)) { p->p_flag =& ~SLOAD; xswap(p, 1, 0); goto loop; } spl6(); runin++; sleep(&runin, PSWP); goto loop; } /* * Swap a process in. * Allocate data and possible text separately. * It would be better to do largest first. */ swapin(p) register struct proc *p; { register struct text *xp; register int a; int x; if((a = malloc(coremap, p->p_size)) == NULL) return(0); if(xp = p->p_textp) { #ifdef SHARED_DATA | LOWER_TEXT_SWAPS xlock(xp); #endif SHARED_DATA | LOWER_TEXT_SWAPS if(xp->x_ccount == 0) { if((x = malloc(coremap, xp->x_size)) == NULL) { mfree(coremap, p->p_size, a); return(0); } #ifdef SHARED_DATA | LOWER_TEXT_SWAPS xp->x_flag =| TXTBUSY; #endif SHARED_DATA | LOWER_TEXT_SWAPS xp->x_caddr = x; swap(xp->x_daddr, x, xp->x_size, B_READ); #ifdef SHARED_DATA | LOWER_TEXT_SWAPS xrele(xp); #endif SHARED_DATA | LOWER_TEXT_SWAPS } xp->x_ccount++; } swap(p->p_addr, a, p->p_size, B_READ); mfree(swapmap, (p->p_size+7)/8, p->p_addr); p->p_addr = a; p->p_flag =| SLOAD; p->p_time = 0; return(1); } #endif PROCESS_QUEUES #ifdef PROCESS_QUEUES qswtch() { setrq(u.u_procp); swtch(); } #endif PROCESS_QUEUES /* * 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. */ #ifdef SWITCH_DISPLAY int swi_count; int lst_count; #endif SWITCH_DISPLAY #ifdef PROCESS_QUEUES swtch() { register n; register struct proc *p, *q; static struct proc *pp, *pq; #ifdef SWITCH_DISPLAY swi_count++; #endif SWITCH_DISPLAY /* * Remember stack of caller * and switch to schedulers stack. */ savu(u.u_rsav); retu(proc[0].p_addr); loop: spl6(); runrun = 0; pp = NULL; q = NULL; n = 128; /* * Search for highest-priority runnable process */ for(p = runq; p != NULL; p = p->p_link) { if(p->p_flag&SLOAD) { /* * Choose the last on queue * with equivalent p_pri */ if(p->p_pri <= n) { pp = p; pq = q; n = p->p_pri; } } q = p; } /* * If no process is runnable, idle. */ p = pp; if(p == NULL) { spl0(); idle(); goto loop; } q = pq; if(q == NULL) runq = p->p_link; else q->p_link = p->p_link; curpri = n; spl0(); /* * Switch to stack of the new process and set up * his segmentation registers. */ retu(p->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. */ if(p->p_flag&SSWAP) { p->p_flag =& ~SSWAP; aretu(u.u_ssav); } /* * The value returned here has many subtle implications. * See the newproc comments. */ return(1); } #else swtch() { static struct proc *p; register i, n; register struct proc *rp; #ifdef BETTER_PANIC extern char *panicstr; if(panicstr) { /* panicing... refuse to reschedule */ idle(); return; } #endif BETTER_PANIC #ifdef SWITCH_DISPLAY swi_count++; #endif SWITCH_DISPLAY #ifndef MAX_PROC if(p == NULL) #endif MAX_PROC #ifdef MAX_PROC if( (p == NULL) || (p > max_proc) ) #endif MAX_PROC 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 */ #ifndef MAX_PROC i = NPROC; #endif MAX_PROC #ifdef MAX_PROC i = max_proc - &proc[0] + 1; #endif MAX_PROC do { rp++; #ifndef MAX_PROC if(rp >= &proc[NPROC]) #endif MAX_PROC #ifdef MAX_PROC if(rp > max_proc) #endif MAX_PROC 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); } #endif PROCESS_QUEUES /* * 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 = rip->p_time; /* fix024 */ rpp->p_cpu = rip->p_cpu; /* fix024 */ #ifdef NEW_SLEEP rpp->p_stl = 0; #endif NEW_SLEEP #ifdef AUSAML rpp->p_lnode = rip->p_lnode; #endif AUSAML #ifdef TIME_LIMITS rpp->p_rtl = rip->p_rtl; #endif TIME_LIMITS #ifdef IGNORE_SIGNALS rpp->p_ignsig = rip->p_ignsig; #endif IGNORE_SIGNALS #ifdef MAX_PROC if( rpp > max_proc ) { max_proc = rpp; # ifdef HIGH_PROC if( rpp > high_proc ) { high_proc = rpp; high_nproc = rpp - &proc[0] + 1; } # endif HIGH_PROC } #endif MAX_PROC /* * 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; #ifdef PROCESS_QUEUES setrq(rpp); #endif PROCESS_QUEUES 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; #ifdef PROCESS_QUEUES qswtch(); #else swtch(); #endif PROCESS_QUEUES /* no return */ } p->p_addr = a2; for(i=0; i<n; i++) copyseg(a1+i, a2++); mfree(coremap, n, a1); retu(p->p_addr); sureg(); } #ifdef ONCE #include "../estabur.h" #endif