Tom Kelliher, CS42
Sept. 16, 1996
Three issues to address
How can the communication be optimized?
Three possible states for a process:
How many in each state?
Kept in a process control block (PCB) for each process:
PCB updated during context switches (kernel in control).
Should a process be able to manipulate its PCB?
Determination of which process to run next (CPU scheduling).
Multiple queues for holding processes:
Consider a disk write:
Three types of schedulers:
Determines overall job mix:
Cleans up after poor long term scheduler decisions:
Decides which process to run next:
Time line schematic:
The following is from the Xinu system, designed by Douglas Comer.
/* declarations */ #define QUANTUM 10 /* clock ticks until preemption */ #define SP 6 /* reg. 6 is stack pointer */ #define PC 7 /* reg. 7 is program counter */ #define PS 8 /* proc. status stored in 8th loc. */ #define NPROC 10 /* max number of processes */ #define PRCURR '\bksl01' /* process is running */ #define PRFREE '\bksl02' /* table entry is free */ #define PRREADY '\bksl03' /* process is on ready queue */ #define PRRECV '\bksl04' /* process is waiting for message */ #define PRSLEEP '\bksl05' /* process is sleeping */ #define PRSUSP '\bksl06' /* process is suspended */ #define PRWAIT '\bksl07' /* process is on semaphore queue */ #define PNREGS 9 /* number of CPU registers */ #define PNMLEN 8 /* process name length */ /* process table entry */ struct pentry \{ char pstate; /* state of process */ short pprio; /* process priority */ short pregs[PNREGS]; /* saved registers: R0-R5, SP, PC, PS */ short psem; /* semaphore process waiting on */ short pmsg; /* message sent to process */ short phasmsg; /* nonzero iff pmsg is valid */ short pbase; /* base of runtime stack */ short pstklen; /* stack length */ short plimit; /* lowest extent of stack */ char name[PNMLEN]; /* process name */ short pargs; /* number of arguments */ short paddr; /* initial code address */ \}; extern struct pentry proctab[NPROC]; extern int currpid; /* currently executing process */ extern int rdyhead, rdytail; extern int preempt; /*----------------------------------------------------------------------------- * resched -- reschedule processor to highest priority ready process * * Notes: Upon entry, currpid gives current process id. * Proctab[currpid].pstate gives correct NEXT state for current * process if other than PRCURR. *----------------------------------------------------------------------------- */ int resched() \{ register struct pentry *optr; /* pointer to old process entry */ register struct pentry *nptr; /* pointer to new process entry */ /* no switch needed if current process priority HIGHER than next */ if ( ( (optr = &proctab[currpid])->pstate == PRCURR) && (lastkey(rdytail) < optr->pprio) ) return(OK); /* force context switch */ if (optr->pstate == PRCURR) \{ optr->pstate = PREADY; insert(currpid, rdyhead, optr->pprio); \} /* remove highest priority process at end of ready list */ nptr = &proctab[ (currpid = getlast(rdytail) ) ]; nptr->pstate = PRCURR; preempt = QUANTUM; /* reset preemption counter */ ctxsw(optr->pregs, nptr->pregs); /* do context switch */ /* the OLD process returns here when resumed */ return(OK); \} /*----------------------------------------------------------------------------- * ctxsw -- assembler routine for performing context switch, * saving/loading registers * * The stack contains three items upon entry to this routine: * * SP+4 => address of 9 word save area with new registers + PS * SP+2 => address of 9 word save area for old registers + PS * SP => return address * * The saved state consists of: the values of R0-R5 upon entry, SP+2, PC * equal to the return address, and the PS (i.e., the PC and SP are saved * as if the calling process had returned to its caller. *----------------------------------------------------------------------------- */ .globl _ctxsw /* declare name global */ _ctxsw: /* entry point to proc. */ mov r0,*2(sp) /* save old R0 in old register area */ mov 2(sp),r0 /* get address of old register area in R0 */ add $2,r0 /* increment to saved pos. of R1 */ mov r1,(r0)+ /* save R1-R5 in successive locations of old mov r2,(r0)+ /* process register save are */ mov r3,(r0)+ mov r4,(r0)+ mov r5,(r0)+ add $2,sp /* move SP beyond the return address, as if a */ /* return had occurred */ mov sp,(r0)+ /* save stack pointer */ mov -(sp),(r0)+ /* save caller's return address as PC */ mfps (r0) /* save processor status beyond registers */ mov 4(sp),r0 /* get address of start of new register area */ /* ready to load registers for the new process */ /* and abandon the old stack */ mov 2(r0),r1 /* load R1-R5 and SP from the new area */ mov 4(r0),r2 mov 6(r0),r3 mov 8.(r0),r4 /* dot following a number makes it decimal; */ mov 10.(r0),r5 /* otherwise it is octal */ mov 12.(r0),sp /* have switched stacks now */ mov 16.(r0),-(sp) /* push new process PS on new process stack */ mov 14.(r0),-(sp) /* push new process PC on new process stack */ mov (r0),r0 /* load R0 from new area */ rtt /* load PC, PS, and reset SP all at once */
Parent, child.
Where does the child's resources come from? By ``resources'' we mean:
Design questions:
Solutions to the ``copy the parent's address space'' problem:
For additional information see (on reserve in Mack):
W. R. Stevens, Advanced Programming in the Unix Environment, Addison Wesley, 1992. Chapter 8.
The fork() call:
Sample code:
if ((pid = fork() < 0) Error(); else if (pid == 0) Child(); else Parent();
Example uses:
exit()
Parent may progress in parallel, or wait until child exits.
Example of parent waiting for child:
if ((pid = fork()) < 0) Error(); else if (pid == 0) Child(); else wait(); /* parent waiting on child */Semantics?