Homework 5 Solutions

22C:116, Fall 1997

Douglas W. Jones

  1. Here is the modified pseudocode from Lecture 11 for a simple disk interrupt handler that works on a disk interface where the interface includes the RAM buffer to and from which disk I/O transfers are directed.
       disable( disk_interrupt )
          if request <> null then
             -- first copy to the user's buffer, if necessary
             if request.transfer-direction = read then
                for i = 1 to buffer size do
                   request.buffer-address[i] = ram-buffer[i]
                endfor
             endif
             -- then let the user know it's done
             V( request.semaphore )
          if not empty( disk-queue ) then
             request := dequeue( disk-queue )
             -- first copy to the user's buffer, if necessary
             if request.transfer-direction = write then
                for i = 1 to buffer size do
                   ram-buffer[i] = request.buffer-address[i]
                endfor
             endif
             -- then start I/O activity
             disk-address-registers := request.disk-address
             disk-command := request.transfer-direction
             enable( disk_interrupt )
             return
          else
             request := null
             return
          endif
    
    It's interesting to note that, if you don't have a general DMA mechanism, each call to the disk interrupt service routine may cause up to two buffers copy operations (finish a read and then start a write), and the average call will involve one copy. This changes the disk interrupt service routine from a few simple instructions to a potentially time consuming computation, and this, in turn, may have a serious impact on I/O bound applications. This performance penalty may also require use of interleaving on a system where the CPU and disk interface would otherwise be able to read successive sectors. (this is the basis of a good exam question.)

  2. a) The UNIX file system has, implicit in it, a class hierarchy. This is summarized below:
    			basic file
                              read
                              write
                              close
                            /       \
                       tty        random access file
                         ioctl      lseek
                                  /   |   \
                               mem    | raw disk
                               kmem   |   mount
                                      |
                                  normal disk file
                                      |
                                  directory
                                    open
    				link
    				rm
    
    All files support read, write and close operations, although some files (read only) will return errors on write, and others, write only, always return EOF on read.

    Interactive terminal files (called TTY after the old electromechanical Teletype terminals that dominated the industry in the late 1960's and early 1970's) support a broad range of ioctl() operations to set things like uart baud rate, line discipline, and so on.

    All random access files support an lseek operation.

    Mem and kmem are special random access files. The fact that they allow access to main memory as if it was a disk file adds no new operations.

    Raw disk files appear to the user as normal random access files; the fact that these files address the entire disk with no file system adds no new operations. However, the operation of mounting a file system on the raw device is new, and this operation distinguishes raw (or special) devices from all others.

    Conventional disk files appear to the user as normal random access files.

    Directories appear to the user as conventional disk files, with added operations to create links, remove links, and traverse paths in the directory during the open operation. The write operation on directories is restricted, but is available to the superuser, so this restriction is an issue of access control and not an issue of abstraction.

    b) Here is an approximate implementation hierarchy for the same classes:

       basic files
       random access files
         these are not implemented!  To use C++ terminology, all of the
         methods are virtual methods, so we could call this a virtual class.
    
       tty
       raw disk
       mem
       kmem
         all of the above implemented by themselves, with no dependance
         on the other classes we are considering
    
       normal disk files
         each is implemented in using a raw disk file
    
       directories
         each is implemented in using a normal disk file
    

  3. Here is the specification for a typical hardware real-time-clock:

    period
    a 16 bit register that can be automatically loaded into countdown.
    countdown
    a 16 bit counter that is decremented every 1.7 microseconds when in the run state. A real-time-clock interrupt will be requested whenever the counter is zero and the enable bit is one.
    control
    a control register with the following 1 bit fields:
    1. enable - when set, real-time-clock interrupts are enabled.
    2. run - when set, countdown will be periodically decremented.
    3. stop - when set, countdown equal zero will reset run.
    4. reload - when set, countdown equal zero will load countdown from period.
    Here is some fairly good pseudocode to implement a real-time clock service in terms of the hardware that was given:
         global timer_queue initially null pointer to a timer
    
         type timer is a record
            next pointer to timer
            ticks count until signal on this timer
            sem points to a semaphore
         end record
    
         delay_signal(t,s)
         -- causes s to be signalled after a delay of t ticks
         allocate timer record tr
         tr.sem = s
         if timer_queue is null -- no timer is currently counting
            tr.next = null
            timer_queue = tr
            
            -- start the timer in aperiodic mode
            countdown = t
            control = { enable, run, stop }
    	return
         else -- some timers are counting
            
            -- entering a critical section, disable interrupts
            control = { disable, run, stop }
    
            -- update our knowledge about the head of queue
            timer_queue.ticks = countdown  
    
            -- see if new timer belongs at head
            if t < timer_queue.ticks
               tr.next = timer_queue
               timer_queue.ticks -= t
               timer_queue = tr
    
               -- reset timer and reenable interrupts
               countdown = t
               control = { enable, run, stop }
    	   return
            else
               -- shuffle timer into queue
               temp = timer_queue
               loop
                  t -= temp.ticks
                  if temp.next = null
                     -- put timer at end of queue
                     tr.next = null
                     temp.next = tr
                     -- reenable if not already done
                     control = { enable, run, stop }
                     return
                  else if t < temp.next.ticks
                     -- put timer in middle of queue
                     tr.next = temp.next
                     temp.next = tr
                     temp.next.ticks -= t
                     -- reenable if not already done
                     control = { enable, run, stop }
                     return
                  endif
    
                  -- march down list
                  temp = temp.next
                  -- reenable if not already done
                  control = { enable, run, stop }
               endloop
            endif
         endif
    
         clock interrupt service routine
            -- stop the clock (may not be needed)
            control = { disable }
            -- assert that if interrupt, timer queue not null
            -- signal all processes waiting for this moment
            repeat
               signal( timer_queue.sem )
               timer_queue = timer_queue.next
            until timer_queue = null or timer_queue.ticks > 0
    
            if timer_queue not null
               -- schedule the next timer
               countdown = timer_queue.ticks
    
               -- restart the clock
               control = { enable, run, stop }
            endif
            return
    
    This pseudocode is not perfect! The timer may lose occasional clock ticks, but the probability of such errors is very low. Writing code that will not lose occasional clock ticks is difficult!