Preemptive multitasking is what lies in the very heart of the operating system. Virtually every operating system uses it, making it possible to run multiple programs at once. Preemptive multitasking works by setting up a timer, and when it ticks a special interrupt routine is called that saves the state of the currently executing program and loads the state of another. This is called context switching, a technique making it look like programs run in parallel by distributing CPU time amongst them.
The way the OS does this is quite simple: CPU registers are stacked for the process that is "leaving", a scheduler algorithm is run to decide upon what process to run, and the CPU registers for that process is popped off its stack. Additionally the MPU registers are set for the now unstacked process using the values previously calculated and stored in the ProcessControl structure. By enabling and using the MPU it is possible to detect a lot of bugs/crashes and prevent rogue programs from making the system unstable. The function that implements the scheduler algorithm is also responsible for getting a process in and out from the signal processing state in some cases.
The scheduler is very basic, supporting only three hard priorities where a higher priority process always gets CPU time over a lower priority one. This is in contrast to the Linux kernel, for instance, where a "niceness" value is used to decide what process to give time to. All processes are set to medium priority when created except for the idle process, which is the only process set to low priority. This ensures that all processes get a fair share of CPU time. The high priority category is usually unused, but a process may change its priority using available API functions.
The figure above illustrates the different states that a process may be in, and the legal state changes. The states in the graph is as follows:
- R: Run
- H: Signal Handler
- B: Blocked (by a system call)
- T: Stopped (typically by hitting Ctrl+Z on the keyboard)
- S: Sleeping
- Z: Zombie (the program has terminated)
The kernel checks that state changes are legal with respect to the graph above. Illegal state changes will trigger a kernel panic, since these are always an indication of a kernel bug.
If you want to dig into the scheduler code, have a look at the files schedasm.s, scheduler.c and scheduler.h for the core parts. Although most of the relevant code are in these files it must be noted that it is not self-contained. Important code related to process loading, signals, interrupts, memory etc. are located elsewhere.