Next: UNIX: System Design
Up: 4560
Previous: Java: System Design
  Contents
Layers:
- user programs
- file system
- intermachine communication
- device manager and device drivers
- real-time clock manager
- interprocess communication
- process coordination
- process manager
- memory manager
- hardware
Written in C and runs on PC, Mac, Sun, LSI-11, etc.
XINU vs. VOS [265]
- VOS based on XINU
- VOS has all the same states plus READING and WRITING
- VOS checks for sleeping processes with each reschedule
- VOS has almost all of the same system calls
- VOS has similar process table
- VOS has the same queues
- VOS has no memory management, clock, device drivers, file system
- VOS has no interrupts
- VOS has only one process eligible to run at a time
- VOS runs on top of UNIX
- XINU ``is'' the operating system
- XINU is PRIO scheduling but VOS also has SJF
- VOS has a GUI
- display state transitions
- provide complete environment for running demo code
XINU: Initialization [266]
Initialization is the final step in design
- design the ``steady'' state first
- then design the ``transient''
- typical of ``function-oriented'' systems
- but ``object-oriented'' systems have constructors
- initialization is easier because of hidden data
XINU: Processes [267]
- Processes are referenced by their process id
- pid acts as an index of the saved state information in proctab
- highest priority process eligible for CPU service is executing
- among processes with equal priority, scheduling is round-robin
- current process does not appear on the ready list, but as currpid
- resched can only switch context from one process to another
- Null process just continues to call resched
- states: current, ready, receiving, sleeping, suspended, waiting
XINU: System Calls [268]
- processes: create, getpid, kill, resume, sleep, suspend, chprio, getprio
- messages: receive, send
- ports: pcount, pcreate, pdelete, preceive, preset, psend
- semaphores: scount, screate, sdelete, signal, sreset, wait
- memory: getmem, getstk
- devices: close, control, getc, getdev, init, open, putc, read, seek, write
XINU: Memory [269]
- getmem obtains memory from the heap
- finds the first block large enough for request
- create allocates a stack for a process
- user programs can also use the heap
- freemem returns memory to the heap
- blocks on the free list are ordered by increasing address
- scan and find the proper location
- adjacent free blocks are grouped into a larger block
- user programs must return memory to the heap
XINU: Interrupts [270]
- interrupts generated by clock, device controllers
- hardware calls interrupt handler when it finds an interrupt pending
- handler uses assembly language ``dispatcher''
- saves/restores registers
- indexes into the interrupt vector to get specific high-level routine
- interrupts disabled when dispatcher calls high-level routine
- high-level routine must keep disabled until changes complete
- system calls, like resume, ``disable'' and then ``restore'' interrupts
- do not want other processes changing process table, etc.
- but disable time must be short so devices are OK
XINU: Interrupts [271]
- process P is running when an interrupt occurs
- hardware uses P's stack to save registers
- P continues to run the interrupt dispatcher
- interrupts disabled by dispatcher
- high-level routine may resched process Q
- Q might pick up from the end of a system call: enable
- new interrupt comes in but goes onto Q's stack
- when P runs again, the context switch will turn off interrupts
- only one interrupt per process is stacked
- rescheduling during interrupt processing is safe provided
- routines leave global data in valid state before rescheduling
- no procedure enables interrupts unless it disabled them
XINU: Real-Time Clock [272]
- time-of-day clock: pulses and counts the pulses
- real-time clock: pulses and generates interrupts
- CPU reads the time-of-day clock if it wants current date/time
- real-time clock forces CPU to process an interrupt with each pulse
- hardware gives highest priority to clock interrupts
- used for preemption to prevent infinite loops
- used for round robin scheduling among equal priority processes
- resched sets ``preempt'' to QUANTUM
- QUANTUM is the ``granularity of preemption''
- clock interrupt routine decrements ``preempt''
- if zero, call resched
XINU: Sleeping Processes [273]
- real-time clock used for timed delay for sleeping processes
- cannot afford to search through long list of sleeping processes
- all processes kept on a delta list
- first process is the one with the least delay
- all other processes have deltas based on the preceding process
- clock just decrements the first process until its zero
- new sleepers inserted at proper place with proper delta
XINU: Device I/O [274]
- hide messy details in device drivers
- access must be fair and safe to shared devices
- provide uniform interface to all devices
- asynchronous I/O allows processes to continue
- overlap computation and I/O
- synchronous I/O blocks processes until I/O completed
- easier and works in most cases
- application calls high-level routine like putc with device descriptor
putc(int descrp, char ch)
- high-level routine indexes into device switch table using descriptor
devptr = &devtab[descrp];
- device table gives real device address and driver routine
- high-level routine calls the upper-half device driver
return( (*devptr->dvputc)(devptr,ch) );
Upper-Half TTY Output Device Driver [275]
- output function acts as a producer of chars
ttyputc(devptr, ch)
struct tty *iptr = &tty[devptr->dvminor];
- wait for space in the buffer (waiting for consumer)
wait(iptr->osem);
- put the character into the buffer
iptr->obuff[iptr->ohead++] = ch;
++iptr->ocnt;
if (iptr->ohead >= OBUFLEN) iptr->ohead = 0;
- send a message to tty lower-half process (which may be blocked)
sendn(iptr->oprocnum,TMSGOK);
Lower-Half TTY Output Device Driver [276]
- output function acts as a consumer of chars
PROCESS ttyoproc()
- infinite loop with a receive
for (;;)
receive();
- take the data out of the buffer
ch = iptr->obuff[iptr->otail++];
--iptr->ocnt;
if (iptr->otail >= OBUFLEN) iptr->otail = 0;
- signal the producer (upper-half) that space is available
signal(iptr->osem);
XINU: TTY Output Watermarks [277]
- very popular and fundamental technique
- producer runs faster and/or has higher priority than consumer
- application usually produces many chars at once IMPLIES buffer full
- with each signal from consumer, producer puts one more char
- forces a reschedule with EVERY character: too slow
- if the buffer fills past the high watermark
- consumer does not signal, just counts
- if the buffer empties below the low watermark
- consumer makes-up for all of the signals
- allows the producer to run awhile before the buffer fills again
XINU: TTY Input Device Drivers [278]
- upper-half input function acts as a consumer of chars
char ttygetc(devptr)
- lower-half input function acts as a producer of chars
PROCESS ttyiproc()
Upper-Half Disk Output Device Driver [279]
- upper-half output function
dswrite(devptr, buff, block)
- enqueues the request and returns
dskenq(drptr, devptr->dvioblk);
- enqueue forces disk arm to sweep low to high and back again
When adding a request for block B to the existing list of requests,
schedule it to be performed between requests for i and i+1 if the disk arm
will pass over block B on its way from i to i+1. If no such pair i and i+1 exist, add the new request to the end of the list.
- FIFO order must be preserved for requests on the same block
- if enqueue on empty list, then send message to lower-half:
dskenq(drptr, dsptr) {
if (dsptr->dreqlst == DRNULL) {
dsptr->dreqlst = drptr; /* enqueue */
drptr->drnext = DRNULL;
sendn(dsptr->dsprocnum);
} else OPTIMIZE ALL READS, WRITES, SEEKS }
Lower-Half Disk Device Driver [280]
- lower-half process handles all requests
PROCESS dsinter(dsptr,dsknum) {
for (;;) {
drptr = dsptr->dreqlst;
if (drptr == DRNUILL) receive(); /* if empty, receive */
dsptr->dreqlst = drptr->drnext; /* dequeue sequentially */
switch (drptr->drop) {
case DREAD: dread(drptr->drbuff,dsknum,drptr->drdba);
resume(drptr->drpid);
break;
case DWRITE:dwrite(drptr->drbuff,dsknum,drptr->drdba);
case DSYNC, DSEEK, ...
}
}
}
Disk Input Device Drivers [281]
- upper-half input function
dsread(devptr, buff, block) {
struct dreq *drptr;
drptr->drbuff = buff;
drptr->drdba = block;
drptr->drop = DREAD;
drptr->drpid = currpid;
dskenq(drptr, devptr->dvioblk);
suspend(currpid);
}
- lower-half disinter function reads and resumes upper-half
dread(drptr->drbuff,dsknum,drptr->drdba);
resume(drptr->drpid);
Next: UNIX: System Design
Up: 4560
Previous: Java: System Design
  Contents
Ted Billard
2001-11-17