Threads and interrupt handling – Comtrol eCos User Manual

Page 30

Advertising
background image

Kernel Overview

The eCos common HAL package provides its own device driver API which contains some of the above synchro-
nization primitives. These allow the DSR for an interrupt handler to signal events to higher-level code. If the
configuration includes the eCos kernel package then the driver API routines map directly on to the equivalent
kernel routines, allowing interrupt handlers to interact with threads. If the kernel package is not included and the
application consists of just a single thread running in polled mode then the driver API is implemented entirely
within the common HAL, and with no need to worry about multiple threads the implementation can obviously be
rather simpler.

Threads and Interrupt Handling

During normal operation the processor will be running one of the threads in the system. This may be an application
thread, a system thread running inside say the TCP/IP stack, or the idle thread. From time to time a hardware
interrupt will occur, causing control to be transferred briefly to an interrupt handler. When the interrupt has been
completed the system’s scheduler will decide whether to return control to the interrupted thread or to some other
runnable thread.

Threads and interrupt handlers must be able to interact. If a thread is waiting for some I/O operation to complete,
the interrupt handler associated with that I/O must be able to inform the thread that the operation has completed.
This can be achieved in a number of ways. One very simple approach is for the interrupt handler to set a volatile
variable. A thread can then poll continuously until this flag is set, possibly sleeping for a clock tick in between.
Polling continuously means that the cpu time is not available for other activities, which may be acceptable for some
but not all applications. Polling once every clock tick imposes much less overhead, but means that the thread may
not detect that the I/O event has occurred until an entire clock tick has elapsed. In typical systems this could be as
long as 10 milliseconds. Such a delay might be acceptable for some applications, but not all.

A better solution would be to use one of the synchronization primitives. The interrupt handler could signal a
condition variable, post to a semaphore, or use one of the other primitives. The thread would perform a wait
operation on the same primitive. It would not consume any cpu cycles until the I/O event had occurred, and when
the event does occur the thread can start running again immediately (subject to any higher priority threads that
might also be runnable).

Synchronization primitives constitute shared data, so care must be taken to avoid problems with concurrent access.
If the thread that was interrupted was just performing some calculations then the interrupt handler could manipulate
the synchronization primitive quite safely. However if the interrupted thread happened to be inside some kernel call
then there is a real possibility that some kernel data structure will be corrupted.

One way of avoiding such problems would be for the kernel functions to disable interrupts when executing any
critical region. On most architectures this would be simple to implement and very fast, but it would mean that
interrupts would be disabled often and for quite a long time. For some applications that might not matter, but many
embedded applications require that the interrupt handler run as soon as possible after the hardware interrupt has
occurred. If the kernel relied on disabling interrupts then it would not be able to support such applications.

Instead the kernel uses a two-level approach to interrupt handling. Associated with every interrupt vector is an
Interrupt Service Routine or ISR, which will run as quickly as possible so that it can service the hardware. However
an ISR can make only a small number of kernel calls, mostly related to the interrupt subsystem, and it cannot make
any call that would cause a thread to wake up. If an ISR detects that an I/O operation has completed and hence
that a thread should be woken up, it can cause the associated Deferred Service Routine or DSR to run. A DSR is
allowed to make more kernel calls, for example it can signal a condition variable or post to a semaphore.

Disabling interrupts prevents ISRs from running, but very few parts of the system disable interrupts and then only
for short periods of time. The main reason for a thread to disable interrupts is to manipulate some state that is

30

Advertising