(read chapter 2 of the dinosaur book)
Basic Computer Architecture
Most computers have a common set of components. The CPU (or several CPUs), some fast memory (RAM), some slow memory (Hard Drive), a video display (and video memory), a keyboard, mouse, etc.
When the operating system starts up, it executes what's known as a boot strap program. This program is stored in ROM, and performs some basic system check (memory check, etc.) and on a PC, loads the first 512 bytes of first boot device into a specific memory location. (boot strap program somehow has to load some piece of the kernel which can take over loading the OS, it might load it from the hard drive, cd-rom, floppy, network, or some other way).
From then on, the operating system kicks in, and loads drivers, etc. It then sits back and waits for things to happen. The thing it's waiting for are interrupts.
Most operating systems are interrupt driven. They do nothing until an interrupt occurs, once one occurs, they handle it, and continue doing nothing. (note that while an operating system may be doing nothing, some user program can be executing).
Interrupts are code fragments which are executed whenever some event happens or whenever they're quested. There can be hardware and software interrupts (meaning, that they're generated by either hardware or software).
For example, when you press a keyboard button, a hardware interrupt is generated, which the operating system handles as a key press (and translates keyboard codes into ASCII codes, etc.) The resulting key press is then sent to some user program as input.
In older operating systems (DOS), interrupts were used directly by nearly all programs. A program would invoke an interrupt to perform such things as opening files, writing output to screen, and to get any operating system services. This was accomplished via the 'int' call.
Interrupts, depending on the context, are sometimes referred to as system calls, and if they're generated by an error event (divide by zero, etc.) then they're called traps. The terminology here gets a bit shifty. Traps can be a way for an operating system to regain control, etc.
CPU can also generate quite a few interrupts, to let the software know what is happening. The Intel manual has a list of all Intel CPU generated interrupts (and all Intel clones).
The code within an interrupt handler can do anything. It can perform IO, etc.
Because there is usually a limited number of possible interrupts, most operating systems have an interrupt table (or interrupt vector as some call it), where entries in the table represent addresses of interrupt routines. The number of the interrupt entry in the table is the number the interrupt has. For example, if someone calls interrupt 7, then the CPU will call the address stored in the 7th entry in the interrupt table.
This interrupt table (or interrupt vector) was usually stored in the beginning of memory, but that is not a requirement with modern CPUs).
Now a days, it is the job of the operating system to manage this table, in the DOS era, it was very common for application programs to take over some interrupts (and some may not even release them back to the operating system after program termination).
How Interrupts Work
Interrupts do what their name suggest, they interrupt a currently executing process, handle whatever they're supposed to handle, and return control back to the previously executing process. The interrupted process usually has no idea it has been interrupted (unless that's the process that requested the interrupt in the first place).
Depending on the architecture, this interruption is handled in many different ways. Some simple microcontrollers just store a return address in a special place in memory, and can only execute one interrupt at a time. Modern CPUs can usually handle many interrupts at the same time, and queue interrupts (if one occurs while another is being processed, it will wait until the other one is finished). There are also various ways to disable interrupts, or to mark certain interrupts to be ignored.
The most common approach: whenever the CPU gets an interrupt, it stores the address of the currently executing code onto the stack, and jumps to the interrupt code. After the interrupt is finished, the operating system pops the stack (where the address of the interrupted code was stored), and jumps back to there. The code doesn't know it was interrupted. This approach allows an interrupt to be handled while another interrupt is executing (depending on whether that interrupt can be interrupted or not).
Input / Output Basics
Input and output happen through the use of controllers. These controllers expose some registers (and sometimes memory), which when set to appropriate values, cause the controllers to perform some requested IO operations on the actual hardware.
This setting of registers to appropriate values is usually performed at the very low level of BIOS or device drivers. The operating system may know how to work with some standard hardware (like keyboard, hard drive, cd-rom, plain VGA graphics, etc.), but usually more complex hardware (like new video cards, sound cards, etc., require their own drivers).
So, usually, whenever some program requests some IO operation, instead of accessing the controller registers directly, it issues an interrupt to the operating system, BIOS, or driver to perform that operation.
Many controllers have their own small specialized processors (some of which can be programmed), so in many cases, they can control the hardware without direct participation of the CPU. So when data is written to the hard drive, the CPU isn't directly involved (again, in the old days, it was involved, but then it was realized that the CPU was more useful than waiting for hardware to write data).
Anyway. Whenever the user program (or operating system) issues an interrupt to do IO, the interrupt has several choices. It can either return immediately (and let IO happen on it's own under the control of the controller), or wait for the IO to complete and then return.
When the interrupt waits for the IO operating to complete, is it called synchronous IO. The calling process blocks until the interrupt finishes waiting for the IO completion.
When the interrupt returns immediately, but allows IO to proceed in the background, it is called asynchronous IO. Once the IO terminates, the process can be notified by another interrupt.
The choice of which approach to use depends on the particular situation and the type of hardware. Usually, it's more efficient for the CPU to do something else while waiting for IO completion.
DMA (Direct Memory Access)
Many devices work at or nearly at (or sometimes faster) than the CPU. If we use interrupts to read or write data to those devices, there wouldn't be much CPU cycles left for anything else.
Imagine a screen filled with pixels, usual screen resolution is 1024 x 768. That's 786432 pixels, most of the time of 16 bit or 32 bit variety, which makes for a lot of data per screen. Now, imagine each of those pixels plotted by a separate interrupt call (as is done with some primitive BIOS calls). Now, on some games, the screen refreshes around 30 frames per second, which would be 23,592,960 interrupts calls per second. Obviously, allowing for interrupt jumps, and memory transfer, if such a system was designed, all it would do is spend it's time jumping around interrupts, without doing much of anything else.
To solve this bulk transfer problem, DMA was invented. DMA setups up the controller to read or write a specific memory location. Whenever you write something to that memory, the controller is invoked (either directly or indirectly) and whatever is in memory is written to the hardware.
This is how many video modes on the PC work. If you are in the plain text mode in DOS, if you write characters to memory location 0xB8000, then those characters will magically appear on the screen (without any interrupt calls, nothing, just write characters to that memory). If you are in video mode 0x13, then whatever bytes you write to location 0xA0000, will be treated as pixels, and show on the screen.
Anyway. Many devices utilize the DMA architecture, including CD-ROMs, Video, etc.
There are several types of memory the computer system utilizes. One form that's directly accessible by the CPU is called RAM (Random Access Memory). Currently executing code is stored in RAM.
There usually isn't enough RAM to go around (everybody always needs more RAM), so there are a few other larger and more permanent storage options available. Most common ones are Hard Drive, CD-ROM, DVD, Tape Drives, etc.
The CPU can only access RAM and its internal registers. It cannot directly access other storage media. Before a CPU can access anything, that data/code must be moved to RAM or CPU registers.
The IO controller's registers are accessed through special IO instructions. Similar types of instructions can be used to write sequences of bytes to the device. The CPUs provide a separate memory space for these special IO ('in' and 'out') instructions.
DMA (talked about earlier) is mapped to some region of RAM (like writing things to memory 0xB8000 would make characters appear on the screen).
Most secondary storage uses magnetic tape or disks to store data. Floppies and Hard Drives are very similar (except one is metallic). Hard Drives may have many disks, and many read heads (little hardware components that read magnetic data stored on the disk).
The disks spin, while the read head floats over the surface, waiting for data to spin under its current location. If data is located on a different cylinder, the heads have to seek to the correct cylinder. Refer to Figure 2.5 on page 37 in the dinosaur book.
Original computer systems (and operating systems) were designed to be used by a single user. Security and protection was not of great concern. Once computers started being used by a large group of people, protecting one program from another (and protecting the system from user programs) became necessary.
Thus, a new idea came about. The operating system would have ultimate control of the system. It would control who can do what and when. There are several types of protection, IO protection, Memory protection, and CPU protection.
The operating system may specify a mask of particular IO ports which are open for a specific task (or mode of the user). For example, if the operating system doesn't want the user to write to a COM port, the operating system would disable the COM port for that process.
CPUs have a special IO mask just for this purpose.
Protecting memory is very important. If memory is not protected, any program can overwrite other programs (and their data). It can view other program's memory (a password entry program would no longer be secure). Worse still, any program can overwrite the operating system itself. These were major problems with DOS and early Windows, and were largely solved under UNIX (Linux, etc.) and Win32 (ignoring bugs).
Usually, the CPU has mechanisms to protect memory, so all the operating system has to do is to enable and use those mechanisms.
In order to maintain security, the operating system has to maintain control of the CPU. It cannot allow any program to simply take over the CPU and not release control of it. This was a major problem under early DOS and Windows (even down to Windows95/98/ME), where one misbehaving program could literally freeze the machine (or parts of an operating that froze the machine).
Go through the project ``Adding a System Call to the Linux Kernel'' at the end of Chapter 2.