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?