Interrupt Service Routines (ISRs)

From COMP15212 Wiki
Depends on InterruptsQueues

When an interrupt is taken the affected processor stops what it was doing and (effectively) performs an exception entry – similar to a system call. The processor then needs to determine the source of the interrupt and perform the relevant service before returning. It must be able to return as if it had never been away so it must not irretrievably alter the interrupt process’ context.

Exactly what occurs when an interrupt is taken depends on the particular processor but, in general, it will have saved a return address and any mode information which was changed (did it interrupt a user or kernel mode process, for example) but, often little else. The processor is in principle switching into a different context to process the interrupt; in practice a complete context switch (and another, back) are too expensive. Usually just a subset of the processor’s internal (register) state is saved to give a small amount of working room.

Note: if the interrupt service routine is written in a compiled language (likely to be C) the compiler needs to know that this is interrupt code so that it does not, inadvertently, change some state which it shouldn’t. This may involve saving “all” the processor state although the loading of a new context and changing page tables et alia can still be avoided.

Determining the interrupt source

There are usually more potential sources of interrupts than there is provision for on a particular processor. (ARM, for example, has two hardware interrupt inputs.) Therefore several (or “many”) inputs are logical-OR-ed together. The first issue is to sort out the particular interrupt to service.

Bear in mind that more than one interrupt may activate at the same time.

There are typically two ways of choosing an interrupt: many complex systems use a combination of both.

Hardware

A hardware interrupt controller can decide and provide a vector to the relevant service routine. The vector is a pointer to the code which has been determined and pre-programmed at initialisation. The controller can choose the highest (also pre-programmed) priority active interrupt and provide the appropriate vector; this means the processor can get to the relevant code quickly. The dialogue goes something like this:

Interrupt controller: Oi! I’ve got an interrupt.

Processor: Okay, I’ll take it. What is it?

Interrupt controller: Go to address <xyzzy>.

This can sort out the interrupts which need the lowest latency (fastest response) and get to the relevant code very quickly.

Software

There may not be a hardware controller or the controller may not be capable of holding vectors for all the needed interrupts. In this case the job is devolved to software. The processor needs to read the potential interrupt sources until it finds the highest priority active source, then look up and jump to the associated address with a switch-like construct. This is easily expandable but rather slower than using hardware assistance. It is better used for the less frequent, latency-tolerant interrupt sources.

Interrupt priority

Interrupt priority can be determined in hardware or software. Often it is not particularly important because interrupt service routines try to be short and fast and can get away with being atomic. Even then, priority may be relevant in choosing which of two or more active interrupts to service first.

A more sophisticated use of priority – relevant if there are some long, low-priority service routines – is in allowing one interrupt to pre-empt a running ISR. This reduces the latency for the high priority interrupt at the cost of significantly more complexity. Practically, this requires hardware support from an interrupt controller which can track the current priority and temporarily disable any interrupt sources at or below the current service level.

Because interrupts can interrupt ISRs for interrupts which have interrupted other ISRs already – and must return appropriately – there needs to be a priority stack maintained; typically this is supported automatically in the interrupt controller hardware.

Nested interrupts

Returning

When an ISR is finished it needs to restore the processor to the state it was in at the start, so the interrupted process doesn’t know it was there. It then returns. If interrupts have been ‘nested’ (as in the example above) then it must tell the controller that the current ISR is finished so the controller hardware can unstack and re-enable things appropriately.


Interrupt applications

Interrupts are used for many purposes but here are a couple of common classes.

Timer interrupts

A regular timer interrupt can be used to maintain a system clock; this, in turn can provide functions such as:

  • waking sleeping processes at the correct time.
  • scheduling regular I/O routines.
    • e.g. a keyboard is scanned at regular intervals which, by nature, can be several milliseconds apart. An interrupt can cause this to happen whilst the processor can spend most of its time elsewhere.

I/O transfers

Many I/O operations are streams of bytes or blocks; these are typically queued in software.

  • Data arrival times are probably uncertain. When data arrives it can cause an interrupt which can insert the data into an input queue. The ISR can then complete and the data can be removed as it is needed by the application.
  • Conversely, the output capacity may be inadequate for an application. Rather than slowing down, the application can output into a queue; as the output becomes available, the ISR can transfer data out of the queue: this is transparent to the application.

Unexpected events

Example: a USB device is inserted. The interface will (probably) interrupt to signal the fact and the relevant installation can be scheduled. This saves polling the USB interface as it reacts immediately, but only when needed.


Chaining ISRs

Sometimes one hardware interrupt may run several ISRs in turn. There are a couple of reasons this might be done:

  • Insufficient hardware signals, so some have to share.
  • Multiple services may be required by a single event.
    Example: a single timer tick may occur at regular intervals and be shared by multiple functions (rather than having dedicated hardware for each).

In these cases the dispatcher might run down a list of ISRs so each can check if it is needed. Items may be added to or deleted from the list dynamically: consider, for example, peripherals using USB


Also refer to: Operating System Concepts, 10th Edition: Chapter 1.2.1, pages 8-11


Articles on IO
Cacheability • Device Drivers • Direct Memory Access (DMA) • IO • Interprocess Communication • Interrupt Controller • Interrupt Service Routines (ISRs) • Interrupts • Libraries • Peripheral devices • Pipes • Queues • Queues Extra • Resources • Shell • Sockets • Spooling and Buffering • Starvation • Streams • System Calls • Thrashing • Timers • Using Peripherals • Virtualisation • Write Buffer