Lecture 13 - Disk Drives
Let's look at expanding Dijkstra's Algorithm.
Multi-Dimensional Banker's Alg.
Just like the 1-D version we keep a table of how many of each resource is:
- allocated
- needed
- what's left
In Use table:
A | 3 | 0 | 1 | 1 | |
B | 0 | 1 | 0 | 0 | |
C | 1 | 1 | 1 | 0 | |
D | 1 | 1 | 0 | 1 | |
E | 0 | 0 | 0 | 0 | |
Allocated | 5 | 3 | 2 | 2 | |
Resources Left (remaining need): |
A | 1 | 1 | 0 | 0 | |
B | 0 | 1 | 1 | 2 | |
C | 3 | 1 | 0 | 0 | |
D | 0 | 0 | 1 | 0 | |
E | 2 | 1 | 1 | 0 | |
Exists | 6 | 3 | 4 | 2 |
Free | 1 | 0 | 2 | 0 |
To determine the safety:
do {
pick a process all of whose needs can bee satisfied
pretend it finishes & releases
} until (all done or stuck)
For this the processes is:
is the only process where the free
vector agrees with it running. It runs and gives the following free: (2,1,21)- Now
can run (it's vector is smaller for each element) so run it. free(5,1,3,2) - Now
can run so run it. free(5,2,3,2) - Now
can run ...
Now say that
In Use table:
A | 3 | 0 | 1 | 1 | |
B | 0 | 1 | 1 | 0 | |
C | 1 | 1 | 1 | 0 | |
D | 1 | 1 | 0 | 1 | |
E | 0 | 0 | 0 | 0 | |
Allocated | 5 | 3 | 3 | 2 | |
Resources Left (remaining need): |
A | 1 | 1 | 0 | 0 | |
B | 0 | 1 | 0 | 2 | |
C | 3 | 1 | 0 | 0 | |
D | 0 | 0 | 1 | 0 | |
E | 2 | 1 | 1 | 0 | |
Exists | 6 | 3 | 4 | 2 |
Free | 1 | 0 | 1 | 0 | |
Doing the algorithm: |
is our only hope: free(2,1,1,1) is the only free one: free(5,1,2,2) - We can satisfy
. free(5,2,3,2) : ... (the rest are easily satisfied).
Consider another example where
In Use table:
A | 3 | 0 | 1 | 1 | |
B | 0 | 1 | 1 | 0 | |
C | 1 | 1 | 1 | 0 | |
D | 1 | 1 | 0 | 1 | |
E | 0 | 0 | 1 | 0 | |
Allocated | 5 | 3 | 4 | 2 | |
Resources Left (remaining need): |
A | 1 | 1 | 0 | 0 | |
B | 0 | 1 | 0 | 2 | |
C | 3 | 1 | 0 | 0 | |
D | 0 | 0 | 1 | 0 | |
E | 2 | 1 | 0 | 0 | |
Exists | 6 | 3 | 1 | 2 |
Free | 1 | 0 | 1 | 0 | |
... |
... uh we were talking about Devices
Here's the problem with devices; they are real things. They literally are numbered with sectors:
But of course unless you're actually using the device itself, we're not actually talking to the device. We're talking to some control register that acts as an intermediary between the programmer and the data itself.
The programmer can then read the status registers to see what data is there, and when it's valid.
How do we talk to one? There's a few ways to do that:
- Memory-Mapped IO (MMIO): memory addresses are written to via the device, and then we can read from that address.
- Pros: It works (and is simple at that).
- Cons: If you have cache systems, then they won't be up-to-date with the memory itself (since the disk writes directly to memory, not through the cache line).
How to Interact with the Device
It's simple:
- Write something to the control register (see the docs for what specifically to write. It's device dependent)
- Wait a while (keep checking the status register to see if it's done)
- Check status register (again)
- Respond appropriately
To check when it's done, just lock (poll) until the status register says it's ready. The problem is that:
- if you ask too frequently you are wasting CPU cycles
- if you ask too infrequently, you may have lost the data via another request
It's better to use interrupts/signals to solve this issue (Programmed IO):
- Issue a command
- Device interrupts when it's done
- OS deals with the interrupt and returns to normal operation.
Okay wait this is pretty annoying. Look at how many avenues it has to take to get that data to memory! It'd be better if it we could write directly to it:
3. Direct Memory Access (DMA): device controller itself does the data transfer
- Pros: It works! And it's really nice if it works!
- Cons: You have to put a lot of trust in the device controller. It also makes the disk controller more complicated, and thus more expensive.
We'll have to talk more about how we'll use DMA and write software using it.