Download Real-Time and Embedded Guide

Transcript
Chapter 14. Design principles
measurable; one components steers the inputs to “actuators” that can change the value of (some of) the
relevant physical signals. Chapter 16 gives a more detailed description.
The control application is used in the following Chapters to illustrate the theory of good design with
practical examples.
14.2. Loose coupling
Only the simplest of applications can be programmed as one single task, with nothing else but
straightforward communication with peripheral hardware. Most other projects need multiple
components, and hence synchronization and data exchange between them. Many developers make the
mistake of putting too much coupling between their components. That means that, in the implementation
of one software component, they use knowledge about the implementation internals of other software
components, or about the specific architecture (and operating system!) in which the components are
used. Some typical examples are:
•
A task in one component suspends and restarts a task in another component. This implicitly means
that the first task knows (or rather, pretends to know!) when it is appropriate and safe to influence the
activities of the other task.
•
Component A uses a finite state machine to structure its internal working; component B bases its
interaction with component A on deciding which state it wants A to go to. This means that the
implementation of A cannot be changed without changing B also.
•
Task X delays itself during 100 milliseconds, in order to allow task Y to be scheduled and get started.
This means that the “proper” synchronization of X and Y depends on platform and configuration
dependent timing; and this timing tends to change drastically when functionality is added, or hardware
is updated.
•
There is only one interrupt service routine in the application, and the programmer doesn’t do the effort
of splitting its implementation into a real ISR (that does nothing but the really essential processing)
and a DSR (that takes care of the further processing of the interrupt, Section 3.4). Again, this is a
software structure that is error prone when updating the system or migrating it to other hardware.
•
Application programmer S raises the priority of a task, because it doesn’t run “fast enough” within the
current situation. She also switches to priority inheritance (Section 4.8) for some of the system’s
critical section. Her colleague H adds his part of the system, and also feels the need to raise the
priorities of “his” tasks and critical sections. This phenomenon reflects an implicit use of knowledge
about the operating system, and, in practice, often leads to a race that eventually ends in most tasks
running at the highest priorities, which, of course, reduces the usefulness of priority-based scheduling.
(TODO: make this list of bad examples as exhaustive as possible.)
The solution to these coupling problems is, of course, quite simple: avoid every form of coupling. Or
rather, strive for loose coupling, because complete decoupling is only possible for tasks that have
nothing to do with each other. This loose coupling advice, however, is difficult to translate into concrete
guidelines. It’s one of these things that make good (software) engineering stand out from the rest, and
makes program design into an “art”. Understanding what exactly causes the problems in the list of
examples given above, is already a good beginning; as is understanding the relevant software patterns in
133