eCos - Embedded Cygnus Operating System


< Prev Contents Next >

2.2 What is Configurability?

Components can exhibit a range of different behaviors. A scheduler component may or may not support time slicing; it may or may not support multiple priorities; it may or may not perform error checking on arguments passed to the scheduler routines. In the context of a desktop application, a button may contain some text or it may contain a picture; the text may be displayed in a variety of fonts; the foreground and background color may vary. When an application uses a component it must have some way of specifying the desired behavior. The component supplier has no way of knowing exactly how a particular component will end up being used and is therefore incapable of specifying the behavior.

One way to control the behavior is at run-time. The application creates an instance of a button object, and then instructs this object to display either text or a picture. No special effort by the application developer is required, since a button can always support all desired behaviors. There is of course a major disadvantage in terms of the size of the final application image: The code that gets linked with the application provides support for all possible behaviors, even if the application does not require it.

Another approach is to control the behavior at link-time, typically by using inheritance in an object-oriented language. The button library provides an abstract base class Button and derived classes TextButton and PictureButton. If an application only uses text buttons then it will only create objects of type TextButton, and the code for the PictureButton class does not get used. In many cases this approach works rather well and reduces the final image size, but there are limitations.

eCos configurability allows the behavior of components to be controlled much earlier in the development cycle: When the component source code gets compiled and turned into a library. Using the Configuration Tool, it is possible to specify the desired behavior of a Button class, for example, that only text buttons will be used. The Configuration Tool will then compile the code for the Button class and build a library specifically for this application. The library will then contain only the code that is required by this application, and nothing else. A different application with different requirements would need its own version of the library, compiled to meet its requirements.

In theory, compile-time configurability should give the best possible results in terms of code size, because it allows code to be controlled at the individual statement level rather than at the function or object level. As an example, take a package to support multi-threading. A standard routine within such a package allows applications to kill threads asynchronously: In POSIX 1003.1c the routine for this is pthread_cancel(); the equivalent routine in ITRON is ter_tsk(). These routines themselves tend to involve a significant amount of code, but that is not the real problem. Other parts of the system require extra code and data for the kill routine to be able to function correctly. For example, if a thread is blocked while waiting on a mutex and is killed off by another thread, then the kill operation may have to do two things: Remove the thread from the mutex's queue of waiting threads; and undo the effects, if any, of priority inheritance. The implementation requires extra fields in the thread data structure so that the kill routine knows about the thread's current state, and extra code in the mutex routines to fill in and clear these extra fields correctly.

Most embedded applications do not require the ability to kill off a thread asynchronously, and hence the kill routine will not get linked into the final application image. Without compile-time configurability this would still mean that the mutex code and similar parts of the system contain code and data that serve no useful purpose in this application. The eCos approach allows the user to select that the thread kill functionality is not required, and all the components can adapt to this when the Configuration Tool compiles them. For example, the code in the mutex lock routine contains statements to support the killing of threads, but these statements will only get compiled in if that functionality is required. The overall result is that the final application image contains only the code and data that is really needed for the application to work, and nothing else.

Compile-time configurability is not intended to replace the other approaches to component architectures, but to complement them. There will be times when run-time selection of behavior is desirable: for example an application may need to be able to change the baud rate of a serial line, and the system must then provide a way of doing this at run-time. There will also be times when link-time selection is desirable: For example, a C library might provide two different random number routines rand() and lrand48(); these do not affect other code so there is no good reason for the C library component not to provide both of these, and allow the application code to use none, one, or both of them as appropriate. Any unused functions will just get eliminated at link-time. Compile-time selection of behavior is another option, and it can be the most powerful one of the three, and the best suited to embedded systems development.


< Prev Contents Next >