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 endifIt'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.)
basic file read write close / \ tty random access file ioctl lseek / | \ mem | raw disk kmem | mount | normal disk file | directory open link rmAll 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
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 returnThis 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!