SKEDSOFT

Real Time Systems

External Interrupts. Hardware interrupts provide an effective mechanism for notifying the application of the occurrences of external events and dealing with sporadic I/O activities. For applications that have such activities, handling external interrupts is an essential function of the kernel.

Depending on the source of the interrupt, the amount of time required to handle an interrupt varies. In particular, handling interrupts from DMA (Direct Memory Access) interfaces may take significant amounts of time. A network interface is an example. When there is an incoming message to be delivered, the interface raises an interrupt. In response to this interrupt, the protocol handler executes to identify the thread that is to receive the message, move the message to the address space of the receiving thread, perform handshakes, and so on. The required time can be large and varied. Service routines for disk and network devices can take hundreds of microseconds to tens of milliseconds to complete. For this reason, in most operating systems, interrupt handling is divided into two steps. Immediate Interrupt Service. The first step of interrupt handling is executed at an

interrupt priority level. All modern processors support some form of priority interrupt. The relationship between interrupt priority levels and normal thread priorities (i.e., software priorities) are depicted by Figure 12–3: The higher the box, the higher the priority. The number of interrupt priority levels depends on the hardware and is unimportant to our discussion here. It

suffices for us to note that all interrupt priority levels are higher than all thread priorities. In fact, interrupt priorities are higher than the priority at which the scheduler executes.

When an interrupt occurs, the processor pushes the program counter and status register on the interrupt stack and branch to the starting address of the kernel’s interrupt handling code.When the kernel executes, it disables interrupts temporarily so it can safely modify data structures that might otherwise also be modified by interrupt service routines. It then saves the processor state on the interrupt stack, enables interrupts, and calls the interrupt service routine of the interrupting device to service the device (e.g., reload registers in the device interface). When more than one device is attached to the same interrupt request line and hence may have raised the interrupt, the kernel calls their interrupt service routines in turn. (When polled, the interrupt service routine of a device that did not raise the interrupt returns immediately.) If during the execution of an interrupt service routine a higher-priority interrupt is raised, the processor and the kernel take care of the higher-priority interrupt in the same manner.

In Figure 12–2, the first step of interrupt handling is called immediate interrupt service. Again the immediate interrupt service routine executes at an interrupt priority. The total delay experienced by a device from the time it raises an interrupt to the time its interrupt service routine begins to execute (and hence the device begins to be serviced) is the sum of the following factors:

  1. the time the processor takes to complete the current instruction, do the necessary chores (e.g., flush the instruction pipeline and read the interrupt vector), and jump to the trap handler and interrupt dispatcher part of the kernel;
  2. the time the kernel takes to disable external interrupts;
  3. the time required to complete the immediate interrupt service routines of higher-priority interrupts if any;
  4. the time the kernel takes to save the context of the interrupted thread, identify the interrupting device, and get the starting address of the interrupt service routine; and
  5. the time the kernel takes to start the immediate interrupt service routine of the interrupting device.

The sum of the above factors is called the interrupt latency. It measures the responsiveness of the system to external events. We note that the first factor is very small because the operations are done by the interrupt handling hardware. Similarly, factors 2, 4, and 5 are relatively small (i.e., in order of a few or a few tens of microseconds) and deterministic. Factor 4 is typically shorter when the application and kernel execute in the same address space. By far, factor 3 can be the largest among these factors and contributes to variation in interrupt latency. This factor can be kept small only by making immediate interrupt handling routines as short as possible.

We pause to note three points here. First, the term interrupt service routine refers specifically to the device-dependent portion of the routine provided by the device driver. Many operating systems provide the portion of interrupt service code that are device-independent (i.e., the code for saving processor state before the device-dependent code executes). A bare-bone operating system for embedded applications may not provide such code. We must have this part of the code in the interrupt service routine. The advantage is speed; the processor jumps directed to interrupt service routines without going through the kernel.

Second, we assume here that the operating system polls all the devices that may raise interrupts at the same priority and thus identifies the interrupting device or devices. In embedded systems, numerous I/O devices may be attached to the same interrupt request line. A more effective alterative to polling (also called autovectoring) is vector interrupt: The interrupting device identifies itself by giving the processor an interrupt vector (i.e., the starting address of its interrupt service routine or its identifier) when its interrupt request is granted by the processor. The processor then branches to the address indicated by the vector after saving the program counter and status register. Not all I/O buses support vector interrupt. Hence your choice of vector interrupt limits your choice of I/O bus (e.g., to VME bus) and portability of your interrupt handling mechanism. For the sake of portability, an operating system typically polls even when the I/O bus supports vector interrupt. Ignoring an interrupt vector and polling the requesting device do no harm, while an interrupt handling mechanism that relies on vector interrupt capability does not work when the I/O bus and interrupting device do not have the capability.

Third, rather than getting ready to accept interrupt again as soon as possible as described here, operating systems provide the application with control over when interrupt is enabled again. Thus, the application can control the rate at which external interrupts are serviced and the amount of time spent in interrupt handling.
Scheduled Interrupt Service. Except for the simplest devices, the immediate step does not complete interrupt handling. Rather, it invokes another service routine to complete interrupt handling. We call the function executed in the second step of interrupt handling a scheduled interrupt handling routine. The execution of this routine is typically preemptable and should be scheduled at a suitable (software) priority in a priority-driven system. (As an example, in the real-time operating system LynxOS [Bunn], the priority of the kernel thread that executes a scheduled interrupt handling routine is the priority of the user thread that opened the interrupting device.) This is why Figure 12–2 shows that following immediate interrupt service, the scheduler executes and inserts the scheduled interrupt handling thread in the ready queue. The scheduler then lets the highest priority thread have the processor.