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.)
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
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!