Overview
- structure of I/O software
- deadlocks
- Minix system task
- Minix clock task
- Minix virtual memory
structure of I/O software
- many devices have identical (or nearly identical) interfaces
- these devices should be handled by a single device driver
- devices with similar functions but different interfaces (e.g.
floppies, hard disks, and CD drives) should be handled by a
device-independent layer that uses the device drivers to
perform its operations
- ultimately, all this is in the service of user-level software
that knows what operations need to be performed
Device Drivers
- a device driver should accept device-independent requests
and convert them to commands to the device controller
- for example, a device-independent request might be to write
a disk block
- the device driver has to program the device controller to write the
disk
- after the operation is complete (perhaps as signalled by
an interrupt), the device driver should check for errors, and perhaps retry
the operation or return an error code
- the device driver can then free any resources, e.g. memory buffers
device-independent software
- a file system should not be designed only for one type of disk device
- a networking stack should not be designed only for one type of interface
- a windowing system should work with a variety of displays and graphic
cards
- the device independent software should provide high-level
operations such as naming, file abstractions, storage allocation,
reliable transmission, routing, etc -- it is these abstractions that
ultimately give the operating system API
user software I/O
- user software knows what contents to put in what files, what files
to read, what data to send, what to display on the screen, etc
- some I/O functions, such as spooling print files, running routing
protocols, or network services, is usually implemented by user-level
processes known as daemons or servers
deadlocks
- formal definition:
A set of processes is deadlocked if each process in the set is
waiting for an event that only another process in the set can cause
- the event is usually the release of a resource, typically
a lock
- this definition generalizes the common definition, which is a
cycle of resource requests among processes
conditions for deadlock
- mutual exclusion: resources cannot be assigned to more than a finite
number of processes at any given moment
- hold and wait: processes holding resources will wait if their requests
cannot be satisfied immediately
- no preemption: resources cannot be taken away from processes that
own them
- circular wait: there must be a cycle of resource requests among
the processes
solutions for deadlock
- don't try too hard to avoid it. E.g. if the situation is really
unlikely, and other things cause greater unreliability, don't waste
effort trying to avoid deadlock
- prevent processes holding resources from waiting, e.g. by requesting
all resources at once, or by failing (gracefully) any process whose
request cannot be granted
- allocate all resources in a fixed order, e.g. all printers before
all disk drives, and devices of one class in ascending order. This works
if (a) all processes obey this, and (b) such an order can be defined
- if resource requests are known in advance, can compute in advance
(using the Banker's algorithm whether granting a request would
make the system unsafe, and therefore refusing or delaying the request
PC Clock Hardware
- quartz crystal oscillator drives counter
- when counter reaches maximum, reloaded with contents of a register,
and also interrupts
- for example, 32.000KHz quartz, 8-bit counter loaded with 224 = 256-32,
when overflows, 1ms has passed, reload with 224
- the initial contents of the register controls the frequency of interrupts,
e.g. for one interrupt every 2ms, load with 192
- real-time clock is a separate device, uses a very low-power
counter similar to those found on digital watches, battery keeps it
running when main power is off
Minix Clock Driver
- very understandable driver
- runs in kernel address space, to avoid overhead of regular
communications mechanisms
- interrupt handler does not conform to the Minix model:
- interrupt handler is specific rather than generic
- interrupt handler often does not send a message to the driver
- interrupt handler does substantial work
- all of these are in the interest of speed on slow systems
- functions:
- maintain the time of day
- preempt processes
- account for CPU usage and support profiling
- provide alarm signals for user processes and watchdog timers
for other parts of the system
Time of day support
- 32-bit counter in seconds rolls over in about 136 years
- 32-bit counter in ticks (1/60 of a second) rolls over in about 2 years
- time is usually counted in seconds from Jan 1st, 1970 (Unix, Minix, etc)
or Jan 1st, 1980 (Windows) -- this is the epoch
- options:
- keep 64-bit counter in ticks
- keep 32-bit counter in seconds and another variable to record the
fractional ticks
- use boot time as the epoch and keep a 32-bit count of ticks. Separately
record the boot time (in seconds) as a 32-bit variable. Minix uses this
- getting the time of day: add realtime / HZ to the boot time
- setting the time of day: save the difference between the parameter
and realtime / HZ as the new "boot" time
Supporting Alarms and Watchdogs
- alarm (22) in a user program tells the system to send
a signal to the process unless the alarm has been cleared within 22 seconds
- in Minix, drivers and servers can set watchdog timers which result in
sending a message when the time expires
- in Minix, servers can request synchronous alarms before calling
receive, so the call to
receive is guaranteed to eventually terminate
- most of the support for signals is in the servers, but the
clock task must send a message to the process manager (if PM is receiving,
otherwise just set some bits) to tell it to signal the process
- watchdogs simply require sending a message (notify)
- the clock driver is slightly more general, and calls an arbitrary
function, which happens to always be cause_alarm in the
system task
- this function can never block, otherwise the clock task would
be blocked and pre-emption would not be performed, though interrupts would
still happen and realtime would still be correct.
Minix System Task
- coordination between the drivers, servers, user processes, and
the kernel is essential
- in Minix, kernel functions needed by the servers or drivers
are placed in the system task
- like all tasks, the system task receives messages, executes appropriate
code, and returns results, without blocking
- a special call, cause_sig (p. 199), sends a signal
to a user process:
- a bit is set in the process's pending signal bitset
- if the process was executable, it is dequeued
- a notification is sent to the process manager
- a number of calls perform the kernel portion of process management
system calls:
fork,
exec,
exit,
times,
kill.
- other calls perform tasks on behalf of the process manager or file
server, for example mapping memory to a process,
copying data between adress spaces, converting
virtual to physical addresses, tracing a process execution,
and rebooting or shutting down the system
Supporting Fork and Exec
- fork (p. 729) requires copying the proc table entry (the PM has already
selected the child process ID), splitting the remaining quantum among
parent and child, changing the return value for the
child (which is returned in the reply message), and clearing the usage times
- pending and enabled signals should only be kept by the parent
- exec resets the stack pointer and places a new PC in
the process table, then readies the process for execution. Someone
else must map the new code segments to the memory before calling this
code
- exit does bookkeeping and removes the process from any queues for
any messages it was trying to send, then cancels signals and sets the
slot to free -- someone else is responsible for closing file descriptors,
deallocating memory, etc
Minix kernel tasks
- the clock task and the system task are separately schedulable,
but run in kernel space
- these are essentially threads of the kernel "process"
- so the minix kernel has three threads: the message passing code,
which includes the scheduler/message passing, the clock thread,
and the system thread
- each of these threads executes briefly, then suspends again
Virtual Memory in Minix
- umap_local, umap_remote,
umap_bios, starting on p. 760
- a virtual address vir_addr corresponds to a physical
address pa based on two per-segment fields, mem_vir
and mem_phys (see lines 10015 and 10018.)
- possible segments include text, data, and stack
- vir_addr - mem_vir is the offset o into the segment
- p = o + mem_phys is the physical address
- computation on lines 10015-10019
- all computations aligned on "page" (click) boundaries

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.5 License.