Interrupt
context: Linux tries not to disable interrupts too much. When
Linux is running, at any moment there's an active thread on a CPU: So
an interrupt borrows what appears to be the context of that thread
until it finishes its business and returns.
(Even if the kernel is waiting in
a power-down mode, there's a thread that is executing the wait
instruction.)
Code called from an interrupt handler is in interrupt context, and
there are many things such code should not do. It can't do anything
that might have to wait for some other software activity, for example.
If your keyboard input routine is going to log all keystrokes to a
file, then you can't do that by calling the file output routine from
the interrupt handler. (Perhaps not a
good idea from a security point of view, but still . . .)
There are decent ways to do that: You can get the keyboard interrupt
to arrange to wake some Linux thread that obtains and logs the input,
for example.
Interrupt
service routine (ISR): The lowest-level interrupt code in the
device driver is generally called an ISR. In Linux you're encouraged to
keep this code short: If there's lots of work to do, you can consider
using some kind of "bottom half," as described in later in this series.
Scheduler: A
kernel subroutine. The OS maintains a list of threads that are ready to
run (they're not blocked on an
incomplete I/O transfer, for example), and that list is in
priority order.
The priority is dynamic, and is recalculated periodically - mostly
to ensure that long-running computations don't hog the CPU and prevent
it from responding to events. Applications can lower their own priority
to volunteer for a life in the background but can't usually raise it.
After any interrupt is handled, the scheduler will be called. If the
scheduler finds another thread is more worthy of running, it parks the
current thread and runs the winner.
Older Linux kernels were not preemptive: once a thread was running
in the kernel it was allowed to rununtil it either volunteered for
rescheduling (by waiting on something) or until control was just about
to pass back into userland - only then would the kernel contemplate a
thread switch.
A nonpreemtive kernel is easier to program. Your kernel code
sequence might have to worry about interrupt handlers running
unexpectedly while it was in flight, but you knew it could never be
unexpectedly caught halfway through something by some other mainstream
kernel code. But it led to excessive delays and inadequate
responsiveness.
The luxurious freedom from interference from parallel threads is
lost when you have an SMP kernel (where
two CPUs are simultaneously threading the same kernel).
To make the SMP kernel work properly, hundreds of possible
interactions need to be tracked down and protected with appropriate
locks. The SMP locks are (in almost all cases) exactly where you need
them to be to permit the scheduler to stop a running kernel thread and
run another: That's called kernel preemption.
It's now an important kernel programming discipline to recognize
code sequences where preemption must be temporarily inhibited. The
macros used to mark the start and end of that code have definitions
that change according to kernel configuration to work correctly on
uniprocessor or SMP systems.