CMSC 412

NOTE 3

Feb 27, 1999

Toy OS with Asynchronous IO

1. Overview

We modify the toy OS given in note 2 to provide asynchronous io; that is, when a user process makes an io request, the request is entered in a queue (ioRequestQ) and the process is free to resume execution. The queued requests are handled by an OS pcb process (called ioServer). [Asynchronous io service can be provided without introducing an OS pcb process.]

ioServer is a process that never terminates. It sleeps at ioWaitQ and wakes up for the following two events:

The OS has the following interrupt handlers: The system has the following (pcb) processes:

2. Data structures

3. Macros, Functions, Interrupt Handlers

 macro UpdateRunQPCB()                 // as in Note 2

 function Scheduler()                  // as in Note 2

 interruptHandler Syscall( PROC_TERM ) // as in Note 2


 interruptHandler Syscall( IO, params ) {
   if ioRequestQ is empty
      then // no io ongoing, ioServer asleep in ioWaitQ, wake it
           move PCB from ioWaitQ to readyQ ;
   add ioRequest( params ) to ioRequestQ ;
   Return_from_Interrupt ;
 }


 interruptHandler Syscall( IOWAIT ) {
   // wait in ioWaitQ.  Called only by ioServer
   UpdateRunQPCB() ;
   move runQ.PCB to ioWaitQ ;
   Scheduler() ;
 }


 interruptHandler Trap( INVALID_OP )   // as in Note 2

 interruptHandler HwIntrpt( TIMER )    // as in Note 2


 interruptHandler HwIntrpt(IO) {
   /* control here after io device interrupts. 
      Interrupt indicates completion of request at head of
      ioRequestQ. ioServer is asleep in ioWaitQ. Wake it up.
   move PCB from ioWaitQ to readyQ ;
   Return_from_Interrupt ;
 }


 function IoServerProgram() { 
   /* program executed by the OS process ioServer.
      runs in OS mode (to access io device) and with interrupts
      disabled (to ensure atomic access). Non-terminating.
   */

   forever do {
     if ioRequestQ is EMPTY
        then Syscall( IOWAIT ) ;

     // ioReqQ not empty (otherwise would still be asleep).
     if ioDevice.status is busy
        then Syscall(IOWAIT) ;

     // ioReqQ not empty, io device idle. Start io operation
     ioDevice.controlReg := ioRequestQ.head.pcb.ioParams ;
     Syscall(IOWAIT) ;

     // io operation completed, communicate this to user
     InformUserIoOver( ioRequest.head.params ) ;
     remove head request in ioRequestQ ;
     // handle next request if any
 }

4. Comments

  1. The above assumes that ioRequestQ is never full. Where is this assumption used? Modify the specs so that this assumption is not used.
  2. Examine the IoServerProgram carefully. It may not work under all conditions.
  3. What would happen if the body of Syscall( IOWAIT ) was called as a regular function or as a macro, rather than via a system call.
  4. The scheduling discipline for ioRequestQ (and for ioWaitQ in Note 2) can be chosen to optimize some criteria. We will see examples later in the course.
  5. Disabling interrupts to protect OS resources (e.g., ioRequestQ, ioWaitQ, etc.) is not always efficient. This also blocks interrupts whose processing does not involve these resources (e.g., a timer interrupt whose handling would have switched between user processes that are not currently doing io). This leads to poor performance.
  6. With the current OS structure, if a user process wants to wait for the io completion signal, it can do this only by busy waiting. To do a nonbusy wait would require introducing another process queue specifically for this condition, which then leads us to another issue, namely, should this queue be in user space or OS space (pros and cons?). We need more elegant langauge mechanisms (as opposed to OS mechanisms).