Source Level Configuration
The best way to avoid the problems faced by existing configurable systems is,
quite literally, at the source. With the proper infrastructure and tools support,
source level configuration can be successful and manageable, and the techniques
used are simple and already well known to programmers. For most cases, using
the C preprocessor provides all the information a programmer needs. For example,
#if defined(_BUFFERED_IO) && (_BUFFER_SIZE > 0)
...
#else
...
#endif
This is well-established syntax for C and C++ programmers. But the C preprocessor
is flexible enough to also be used on linker scripts and assembler files. In
the case of Makefiles, the GNU make tool provides its own "include",
"if" and "ifdef"
primitives that can be used instead.
This is a simple and easy approach that can truly solve the problem. In particular
it is possible to apply very fine-grained configuration options to the code.
This is particularly appropriate for embedded systems since the vast majority
of embedded applications compile into static images, rather than dynamically
loading executables. In that context, there are many advantages:
With source level configuration, the code can be adapted perfectly to the level
of functionality the developer requires, no matter how fine-grained. If functionality
is not used, then it can simply be removed. If the developer knows, for example,
that there will only ever be four threads in the system, then the number of
threads supported by the kernel scheduler can be reduced. Or perhaps the programmer
may be able to reduce the size of I/O buffers, or even eliminate them altogether.
There are many more examples.
As a consequence, code and data can be removed; either explicitly because they
were directly dependent on the configuration option in question, or indirectly,
because the code/data was used in turn by the code that was removed, and so
that code is no longer needed, etc.
The upshot is that both ROM and RAM requirements are reduced - good news for
the embedded developer.
Some linkers, such as the GNU linker, are even capable of so-called "selective
linking", where unreferenced functions and data can be removed. This contrasts
with normal linkers that work on an object file basis if any symbol in an
object file is referenced, the whole file is pulled in. But selective linking
is still insufficient since it is an "all or nothing" approach only
whole functions can be removed. A good example is the printf()
function: a large amount of code is needed to format floating-point output (%e,
%f and %g format
specifiers). But since it is impossible to tell whether floating-point has been
used until run-time, the code must always be included. Due to this overhead,
on many embedded systems, libraries often provide an alternative that does not
support floating-point. But this is just an isolated workaround for the more
general problem that can only be solved by source-level configuration.
Of course with the complete removal of unused code and data, everything can
be faster. Code no longer has to check its configuration using variables to
determine the course of action to take. Code may now be able to fit into instruction
caches where it couldn't before. Even the download time of the program will
be less, thus accelerating the iterative development and testing cycles.
As a result, code can be more responsive, latencies can be reduced (which aids
determinism in a real-time system), and cheaper hardware may be used.
Simplicity can be a goal in itself - since less code is present, there is less
code that can go wrong. By using a minimal configuration in safety critical
systems, less code needs to be walked through, verified and tested.
Embedded systems have many different and varying requirements, and the features
required of an operating system vary greatly. Rather than the "one size
fits all" approach, developers can choose the exact behavior that fits
the application. This can be done at a very fine grain, unlike the relatively
coarse grain of existing configurable systems.
For example, most systems come with a general-purpose memory allocator, malloc.
But on some real-time systems, it is vital the performance is deterministic,
i.e. it must be able to allocate and free memory in a known bounded time. But
in other embedded applications, memory may be scarce, so efficient usage of
the memory becomes the highest priority. These two goals are not reconcilable
with a single malloc implementation. But source
level configuration allows the user the choice of which to use, rather than
imposing a design to suit all.
- Compatible with run-time configuration
Of course, you can still return to using global variables to control configuration.
In fact, once configuration is in place, adding this is simple - just look for
the existing configuration switches. For example, it is easy to change:
#ifdef _BUFFERED_IO
...
#endif
to:
#ifdef _BUFFERED_IO
if (_buffered_io) {
...
}
#endif
or even:
#ifdef _BUFFERED_IO
# ifdef _BUFFERED_IO_SET_AT_RUNTIME
if (_buffered_io) {
# endif
...
# ifdef _BUFFERED_IO_SET_AT_RUNTIME
}
# endif
#endif
Perhaps more significantly, if you already have code that is run-time configurable,
by looking for where these variables are tested, it may be easy to add compile-time
source code configuration switches using the converse of the above.
Linux, autoconf, and automake
Of course, source code configuration by itself is nothing new. The most prolific
example used in modern software is the GNU autoconf and automake system. Indeed
since Cygnus Solutions specializes in GNU software, at the outset of the design
of eCos there was discussion on whether it was suitable as infrastructure.
However, in common with other existing configuration infrastructures, there are a number of drawbacks:
- Every option that is to be set away from the default must be explicitly and individually mentioned on the command-line;
- All dependency checking must be done by hand;
- There is no easy method or interface for a user to navigate through more than a few configuration options;
- In particular, there is no easy way for users to navigate through a hierarchy of options.
Overall, the design goals of autoconf are not the same as that of a source
configurable embedded system - efforts are directed at portability between hosts
(and UNIX hosts at that), not configuration itself. As a configuration infrastructure,
it is neither scalable nor particularly appropriate.
There is also the specific case of the configuration of the Linux Operating
System kernel. It provides many options, and even provides a graphical utility
to navigate through these options. However, the system is a one-off, and again
only does manual dependency checking. And while the large number of options
may seem to imply that there is a lot of configurability, in fact the vast majority
of options deal simply with whether whole modules should be included or excluded
the level of configurability is very coarse-grained.
Benefits of the Open Source revolution
There is an obvious issue with moving towards configurable source code systems
- you must provide your source code so that people can configure it! For many
commercial organizations the thought of this "value leak" has traditionally
been an anathema.
But only software supplied with source can have the benefits of source configuration
discussed here. This is comparatively easy for the embedded market since most
applications are static. However, if dynamic program execution is required,
even in a configurable system, this does restrict choice since it is rarely
known what services a program may need.
Of course the obvious solution for many companies will simply be to supply
source code with their products when purchased. Or perhaps even supply the source
code for an extra fee. But there has been a recent massive surge of interest
in open source, and many companies are realizing the tremendous benefits of
open source software.
For example, Cygnus Solutions has benefited from eCos being both free and open
source as it was mutually beneficial to all concerned - the more things that
become open source, the more things can be source configurable. For this reason,
source configurable software is likely to be a growth area, and this is why
it is important that issues related to source configuration must be explored.
|