Operating Systems
2000-01 Fall
M 5:00-6:50
Ciww 109
Allan Gottlieb
gottlieb@nyu.edu
http://allan.ultra.nyu.edu/~gottlieb
715 Broadway, Room 1001
212-998-3344
609-951-2707
email is best
================ Start Lecture #12
================
Note:
I improved the presentation of disk controllers given last time
(unfortunately, this was very easy to do). In particular, I suggest
you read the new small section entitled ``Using a controller''. Both
the giant page and the page for lecture-11 now include this new
section.
5.2: Principles of I/O Software
As with any large software system, good design and layering is
important.
5.2.1: Goals of the I/O Software
Device independence
We want to have most of the OS, unaware of the characteristics of
the specific devices attached to the system. Indeed we also want the
OS to be largely unaware of the CPU type itself.
Due to this device independence, programs are
written to read and write generic devices and then at run time
specific devices are assigned. Writing to a disk has differences from
writing to a terminal, but Unix cp and DOS copy do not see these
differences. Indeed, most of the OS, including the file system code,
is unaware of whether the device is a floppy or hard disk.
Uniform naming
Recall that we discussed the value
of the name space implemented by file systems. There is no dependence
between the name of the file and the device on which it is stored. So
a file called IAmStoredOnAHardDisk might well be stored on a floppy disk.
Error handling
There are several aspects to error handling including: detection,
correction (if possible) and reporting.
- Detection should be done as close to where the error occurred as
possible before more damage is done (fault containment). This is not
trivial.
- Correction is sometimes easy, for example ECC memory does this
automatically (but the OS wants to know about the error so that it can
schedule replacement of the faulty chips before unrecoverable double
errors occur).
Other easy cases include successful retries for failed ethernet
transmissions. In this example, while logging is appropriate, it is
quite reasonable for no action to be taken.
- Error reporting tends to be awful. The trouble is that the error
occurs at a low level but by the time it is reported the
context is lost. Unix/Linux in particular is horrible in this area.
Creating the illusion of synchronous I/O
- I/O must be asynchronous for good performance. That is
the OS cannot simply wait for an I/O to complete. Instead, it
proceeds with other activities and responds to the notification when
the I/O has finished.
- Users (mostly) want no part of this. The code sequence
Read X
Y <-- X+1
Print Y
should print a value one greater than that read. But if the
assignment is performed before the read completes, the wrong value is
assigned.
- Performance junkies sometimes do want the asynchrony so that they
can have another portion of their program executed while the I/O is
underway. That is they implement a mini-scheduler in their
application code.
Sharable vs dedicated devices
For devices like printers and tape drives, only one user at a time
is permitted. These are called serially reusable
devices, and are studied next chapter.
Devices like disks and Ethernet ports can be shared by processes
running concurrently.
Layering
Layers of abstraction as usual prove to be effective. Most systems
are believed to use the following layers (but for many systems, the OS
code is not available for inspection).
- User level I/O routines.
- Device independent I/O software.
- Device drivers.
- Interrupt handlers.
We give a bottom up explanation.
5.2.2: Interrupt Handlers
We discussed an interrupt handler before when studying page faults.
Then it was called ``assembly language code''.
In the present case, we have a process blocked on I/O and the I/O
event has just completed. So the goal is to make the process ready.
Possible methods are.
- Releasing a semaphore on which the process is waiting.
- Sending a message to the process.
- Inserting the process table entry onto the ready list.
Once the process is ready, it is up to the scheduler to decide when
it should run.
5.2.3: Device Drivers
The portion of the OS that ``knows'' the characteristics of the
controller.
The driver has two ``parts'' corresponding to its two access
points. Recall the following figure from the beginning of the course.
- Accessed by the main line OS via the envelope in response to an I/O
system call. The portion of the driver accessed in this way is
sometimes call the ``top'' part.
- Accessed by the interrupt handler when the I/O completes (this
completion is signaled by an interrupt). The portion of the driver
accessed in this way is sometimes call the ``bottom'' part.
Tanenbaum describes the actions of the driver assuming it is
implemented as a process (which he recommends). I give both that view
point and the self-service paradigm in which the driver is invoked by
the OS acting in behalf of a user process (more precisely the process
shifts into kernel mode).
Driver in a self-service paradigm
- The user (A) issues an I/O system call.
- The main line, machine independent, OS prepares a
generic request for the driver and calls (the top part of)
the driver.
- If the driver was idle (i.e., the controller was idle), the
driver writes device registers on the controller ending with a
command for the controller to begin the actual I/O.
- If the controller was busy (doing work the driver gave it
previously), the driver simply queues the current request (the
driver dequeues this request below).
- The driver jumps to the scheduler indicating that the current
process should be blocked.
- The scheduler blocks A and runs (say) B.
- B starts running.
- An interrupt arrives (i.e., an I/O has been completed).
- The interrupt handler invokes (the bottom part of) the driver.
- The driver informs the main line perhaps passing data and
surely passing status (error, OK).
- The top part is called to start another I/O if the queue is
nonempty. We know the controller is free. Why?
Answer: We just received an interrupt saying so.
- The driver jumps to the scheduler indicating that process A should
be made ready.
- The scheduler picks a ready process to run. Assume it picks A.
- A resumes in the driver, which returns to the main line, which
returns to the user code.
Driver as a process (Tanenbaum) (less detailed than above)
- The user issues an I/O request. The main line OS prepares a
generic request (e.g. read, not read using Buslogic BT-958 SCSI controller)
for the driver and the driver is awakened (perhaps a message is sent to
the driver to do both jobs).
- The driver wakes up.
- If the driver was idle (i.e., the controller is idle), the
driver writes device registers on the controller ending with a
command for the controller to begin the actual I/O.
- If the controller is busy (doing work the driver gave it), the
driver simply queues the current request (the driver dequeues this
below).
- The driver blocks waiting for an interrupt or for more
requests.
- An interrupt arrives (i.e., an I/O has been completed).
- The driver wakes up.
- The driver informs the main line perhaps passing data and
surely passing status (error, OK).
- The driver finds the next work item or blocks.
- If the queue of requests is non-empty, dequeue one and
proceed as if just received a request from the main line.
- If queue is empty, the driver blocks waiting for an
interrupt or a request from the main line.
5.2.4: Device-Independent I/O Software
The device-independent code does most of the functionality, but not
necessarily most of the code since there can be many drivers
all doing essentially the same thing in slightly different ways due to
slightly different controllers.
- Naming. Again an important O/S functionality.
Must offer a consistent interface to the device drivers.
- In Unix this is done by associating each device with a
(special) file in the /dev directory.
- The inodes for these files contain an indication that these
are special files and also contain so called major and minor
device numbers.
- The major device number gives the number of the driver.
(These numbers are rather ad hoc, they correspond to the position
of the function pointer to the driver in a table of function
pointers.)
- The minor number indicates for which device (e.g., which scsi
cdrom drive) the request is intended
- Protection. A wide range of possibilities are
actually done in real systems. Including both extreme examples of
everything is permitted and nothing is permitted (directly).
- In ms-dos any process can write to any file. Presumably, our
offensive nuclear missile launchers do not run dos.
- In IBM and other mainframe OS's, normal processors do not
access devices. Indeed the main CPU doesn't issue the I/O
requests. Instead an I/O channel is used and the mainline
constructs a channel program and tells the channel to invoke it.
- Unix uses normal rwx bits on files in /dev (I don't believe x
is used).
- Buffering is necessary since requests come in a
size specified by the user and data is delivered in a size specified
by the device.
- Enforce exclusive access for non-shared devices
like tapes.
5.2.5: User-Space Software
A good deal of I/O code is actually executed in user space. Some
is in library routines linked into user programs and some is in daemon
processes.
- Some library routines are trivial and just move their arguments
into the correct place (e.g., a specific register) and then issue a
trap to the correct system call to do the real work.
- Some, notably standard I/O (stdio) in Unix, are definitely not
trivial. For example consider the formatting of floating point
numbers done in printf and the reverse operation done in scanf.
- Printing to a local printer is often performed in part by a
regular program (lpr in Unix) and part by a daemon
(lpd in Unix).
The daemon might be started when the system boots or might be started
on demand. I guess it is called a daemon because it is not under the
control of any user. Does anyone know the
reason.
- Printing uses spooling, i.e., the file to be
printed is copied somewhere by lpr and then the daemon works with this
copy. Mail uses a similar technique (but generally it is called
queuing, not spooling).
Homework: 6, 7, 8.
5.3: Disks
The ideal storage device is
- Fast
- Big (in capacity)
- Cheap
- Impossible
Disks are big and cheap, but slow.
5.3.1: Disk Hardware
Show a real disk opened up and illustrate the components
- Platter
- Surface
- Head
- Track
- Sector
- Cylinder
- Seek time
- Rotational latency
- Transfer time
Overlapping I/O operations is important. Many controllers can do
overlapped seeks, i.e. issue a seek to one disk while another is
already seeking.
Despite what Tanenbaum says, modern disks cheat and do not have
the same number of sectors on outer cylinders as on inner one. Often
the controller ``cover for them'' and protect the lie.
Again contrary to Tanenbaum, it is not true that when one head is
reading from cylinder C, all the heads can read from cylinder C with
no penalty.
Choice of block size
- We discussed this before when studying page size.
- Current commodity disk characteristics (not for laptops) result in
about 15ms to transfer the first byte and 10K bytes per ms for
subsequent bytes (if contiguous).
- Rotation rate is 5400, 7600, or 10,000 RPM (15K just now
available).
- Recall that 6000 RPM is 100 rev/sec or one rev
per 10ms. So half a rev (the average time for to rotate to a
given point) is 5ms.
- Transfer rates around 10MB/sec = 10KB/ms.
- Seek time around 10ms.
- This favors large blocks, 100KB or more.
- But the internal fragmentation would be severe since many files
are small.
- Multiple block sizes have been tried as have techniques to try to
have consecutive blocks of a given file near each other.
- Typical block sizes are 4KB-8KB.
5.3.2: Disk Arm Scheduling Algorithms
These algorithms are relevant only if there are several I/O
requests pending. For many PCs this is not the case. For most
commercial applications, I/O is crucial.
- FCFS (First Come First Served): Simple but has long delays.
- Pick: Same as FCFS but pick up requests for cylinders that are
passed on the way to the next FCFS request.
- SSTF (Shortest Seek Time First): Greedy algorithm. Can starve
requests for outer cylinders and almost always favors middle requests.
- Scan (Look, Elevator): The method used by an old fashioned
jukebox (remember ``Happy Days'') and by elevators. The disk arm
proceeds in one direction picking up all requests until there are no
more requests in this direction at which point it goes back the other
direction. This favors requests in the middle, but can't starve any
requests.
- C-Scan (C-look, Circular Scan/Look): Similar to Scan but only
service requests when moving in one direction. When going in the
other direction, go directly to the furthest away request. This
doesn't favor any spot on the disk. Indeed, it treats the cylinders
as though they were a clock, i.e. after the highest numbered cylinder
comes cylinder 0.
- N-step Scan: This is what the natural implementation of Scan
gives.
- While the disk is servicing a Scan direction, the controller
gathers up new requests and sorts them.
- At the end of the current sweep, the new list becomes the next
sweep.
Minimizing Rotational Latency
Use Scan, which is the same as C-Scan. Why?
Because the disk only rotates in one direction.
Homework: 9, 10.