Controlling Interrupts
This lesson discusses the pros and cons of manipulating interrupts as a mechanism for locks.
One of the earliest solutions used to provide mutual exclusion was to disable interrupts for critical sections; this solution was invented for single-processor systems. The code would look like this:
void lock() {DisableInterrupts();}void unlock() {EnableInterrupts();}
Assume you are running on such a single-processor system. By turning off interrupts (using some kind of special hardware instruction) before entering a critical section, you ensure that the code inside the critical section will not be interrupted, and thus will execute as if it were atomic. When you are finished, you re-enable interrupts (again, via a hardware instruction), and thus the program proceeds as usual.
Positives of interrupts
The main positive of this approach is its simplicity. You certainly don’t have to scratch your head too hard to figure out why this works. Without interruption, a thread can be sure that the code it executes will execute and that no other thread will interfere with it.
Negatives of interrupts
The negatives, unfortunately, are many.
Privileged action
First, this approach requires to allow any calling thread to perform a privileged operation (turning interrupts on and off), and thus trust that this facility is not abused. As you already know, any time we are required to trust an arbitrary program, we are probably in trouble. Here, the trouble manifests in numerous ways: a greedy program could call lock()
at the beginning of its execution and thus monopolize the processor; worse, an errant or malicious program could call lock()
and go into an endless loop. In this latter case, the OS never regains control of the system, and there is only one recourse: restart the system. Using interrupt disabling as a general-purpose synchronization solution requires too much trust in applications.
No support for multiprocessors
Second, the approach does not work on multiprocessors. If multiple threads are running on different CPUs, and each tries to enter the same critical section, it does not matter whether interrupts are disabled; threads will be able to run on other processors and thus could enter the critical section. As multiprocessors are now commonplace, our general solution will have to do better than this.
Interrupts can get lost
Third, turning off interrupts for extended periods of time can lead to interrupts becoming lost, which can lead to serious systems problems. Imagine, for example, if the CPU missed the fact that a disk device has finished a read request. How will the OS know to wake the process waiting for said read?
Inefficient approach
Finally, and probably least important, this approach can be inefficient. Compared to normal instruction execution, code that masks or unmasks interrupts tends to be executed slowly by modern CPUs.
For these reasons, turning off interrupts is only used in limited contexts as a mutual-exclusion primitive. For example, in some cases, an operating system itself will use interrupt masking to guarantee atomicity when accessing its own data structures, or at least to prevent certain messy interrupt handling situations from arising. This usage makes sense, as the trust issue disappears inside the OS, which always trusts itself to perform privileged operations anyhow.
Get hands-on with 1400+ tech skills courses.