Contiki-OS in general
Contiki is a WSN platform that provides software and hardware. Contiki was created by Dr. Adam Dunkels in 2002. Now, the Contiki project involves both companies and contributors (wrote in 2012). This project has released open source software and hardware.
Motivation
Wireless sensor networks are composed of large numbers of tiny networked devices that communicate untethered. For large scale networks, it is important to be able to dynamically download code into the network.
To meet this requirements, Dr. Adam provides Contiki, a lightweight operating system with support for dynamic loading and replacement of individual programs and services.
Contiki is built around an event-driven kernel but provides optional preemptive multi-threading that can be applied to individual processes. The dynamic loading and unloading is feasible in a resource constrained environment, while keeping the base system lightweight and compact.
Tiny in size
An on-board battery or solar panel can only supply limited amounts of power. Moreover, the small physical size and low per-device cost limit the complexity of the system.
Typical sensor devices are equipped with 8-bit micro-controllers, code memory on the order of 100 kilobytes, and less than 20 kilobytes of RAM (wrote in 2006 in Contiki Doc).
Run-time code deployment
Wireless sensor networks are envisioned to be large scale, with hundreds or even thousands of nodes per network. When developing software for such a large sensor network, being able to dynamically download program code into the network is of great importance. Therefore, it is not feasible to physically collect and reprogram all sensor devices and in-site mechanisms are required.
Most operating systems for embedded systems require that a complete binary image of the entire system is built and downloaded into each device. The binary includes the operating system, system libraries, and the actual applications running on top of the system.
In contrast, Contiki has the ability to load and unload individual applications or services at run-time. In most cases, an individual application is much smaller than the entire system binary and therefore requires less energy when transmitted through a network. Additionally, the transfer time of an application binary is less than that of an entire system image.
Portability
As the number of different sensor device platforms increases, it is desirable to have a common software infrastructure that is portable across hardware platforms.
The currently available sensor platforms carry completely different sets of sensors and communication devices. Due to the application specific nature of sensor networks, Dr. Adam does not expect that this will change in the future(in 2006), this estimation is one of the reason why we have the upgraded version Contiki-NG now. The single unifying characteristic of today’s platforms is the CPU architecture which uses a memory model without segmentation or memory protection mechanisms.
Program code is stored in re-programmable ROM and data in RAM. Keep this in mind, the only abstraction provided by the base system is CPU multiplexing and support for loadable programs and services.
As a consequence of the application specific nature of sensor networks, we believe that other abstractions are better implemented as libraries or services and provide mechanisms for dynamic service management.
Event-driven systems
In severely memory constrained environments, a multi-threaded model of operation often consumes large parts of the memory resources. Each thread must have its own stack and because it in general is hard to know in advance how much stack space a thread needs, the stack typically has to be over provisioned. Furthermore, the memory for each stack must be allocated when the thread is created. The memory contained in a stack can not be shared between many concurrent threads, but can only be used by the thread to which is was allocated. Moreover, a threaded concurrency model requires locking mechanisms to prevent concurrent threads from modifying shared resources.
To provide concurrency without the need for per-thread stacks or locking mechanisms, event-driven systems have been proposed. In event-driven systems, processes are implemented as event handlers that run to completion. Because an event handler cannot block, all processes can use the same stack, effectively sharing the scarce memory resources between all processes. Also, locking mechanisms are generally not needed because two event handlers never run concurrently with respect to each other.
While event-driven system designs have been found to work well for many kinds of sensor network applications they are not without problems (A tiny virtual machine for sensor networks). The state driven programming model can be hard to manage for programmers(The Emergence of Networking Abstractions and Techniques in TinyOS). Also, not all programs are easily expressed as state machines. One example is the lengthy computation required for cryptographic operations (Security for Ubiquitous Computing). Typically, such operations take several seconds to complete on CPU constrained platforms. In a purely event-driven operating system a lengthy computation completely monopolizes the CPU, making the system unable to respond to external events. If the operating system instead was based on preemptive multi-threading this would not be a problem as a lengthy computation could be preempted.
To combine the benefits of both event-driven systems and preemptible threads, Contiki uses a hybrid model: the system is based on an event-driven kernel where preemptive multi-threading is implemented as an application library that is optionally linked with programs that explicitly require it.
System Overview
The system structure is shown below.
Code size of Contiki-OS is (Contiki-Doc in 2007)
Kernel
The Contiki kernel consists of a lightweight event scheduler that dispatches events to running processes and periodically calls processes’ polling handlers. All program execution is triggered either by events dispatched by the kernel or through the polling mechanism. The kernel does not preempt an event handler once it has been scheduled. Therefore, event handlers must run to completion.
The kernel supports two kind of events: asynchronous and synchronous events. Asynchronous events are a form of deferred procedure call: asychronous
events are enqueued by the kernel and are dispatched to the target process some time later. Synchronous events are similar to asynchronous’ but it immediately causes the target process to be scheduled.
All event scheduling in Contiki is done at a single level and events cannot preempt each other. Events can only be preempted by interrupts. Normally, interrupts are implemented using hardware interrupts but may also be implemented using an underlying real-time executive (the latter technique has previously been used to provide real-time guarantees for the Linux kernel(A Linux-based Real Time Operating System)). In order to be able to support an underlying real-time executive, Contiki never disables interrupts. Because of this, Contiki does not allow events to be posted by interrupt handlers as that would lead to race-conditions in the event handler.
In addition to the events, the kernel provides a polling mechanism. Polling can be seen as high priority events that are scheduled in-between each asynchronous event. Polling is used by processes that operate near the hardware to check for status updates of hardware devices.
Loadable programs
Loadable programs are implemented using a run-time relocation function and a binary format that contains relocation information. When a program is loaded into the system, the loader first tries to allocate sufficient memory space based on information provided by the binary. If memory allocation fails, program loading is aborted. After the program is loaded into memory, the loader calls the program’s initialization function. The initialization function may start or replace one or more processes.
Internal Services
In Contiki, a service is a process that implements functionality that can be used by other processes. A service can be seen as a form of a shared library. Services can be dynamically replaced at run-time and must therefore be dynamically linked. Typical examples of services includes communication protocol stacks, sensor device drivers, and higher level functionality such as sensor data handling algorithms.
Services are managed by a service layer conceptually situated directly next to the kernel. A service consists of a service interface and a process that implements the interface. The service interface consists of a version number and a function table with pointers to the functions that implement the interface.
Application programs using the service use a stub library to communicate with the service. The stub library is linked with the application and uses the service layer to find the service process. Once a service has been located, the service stub caches the process ID of the service process and uses this ID for all future requests.
How it works?
- Programs call services through the service interface stub and need not be aware of the fact that a particular function is implemented as a service. The first time the service is called, the service interface stub performs a service lookup in the service layer. If the specified service exists in the system, the lookup returns a pointer to the service interface.
- The version number in the service interface is checked with the version of the interface stub. In addition to the version number, the service interface contains pointers to the implementation of all service functions. The function implementations are contained in the service process. If the version number of the service stub match the number in the service interface, the interface stub calls the implementation of the requested function.
How to replace the service?
The process ID of the service process is used as a service identifier, it is crucial that the process ID is retained if the service process is replaced. For this reason, the kernel provides special mechanism for replacing a process and retaining the process ID.
- When a service is to be replaced, the kernel informs the running version of the service by posting a special event to the service process.
- In response to this event, the service must remove itself from the system.
Notice: Many services have an internal state that may need to be transferred to the new process.
The kernel provides a way to pass a pointer to the new service process, and the service can produce a state description that is passed to the new process, where the memory for holding the state must be allocated from a shared source, since the process memory is de-allocated when the old process is removed. Since the service state description is tagged with the version number of the service, so that an incompatible version of the same service will not try to load the service description.
Wireless Communication
In Contiki, communication is implemented as a service in order to enable run-time replacement. Implementing communication as a service also provides for multiple communication stacks to be loaded simultaneously. Furthermore, the communication stack may be split into different services which enables run-time replacement of individual parts of the communication stack.