The vendors will tell you that there are substantial differences between their product and that of the competitors. Even given this ‘apparent and obvious’ superiority of product A over product B there are still common features that a customer might use in order to compare the products. Listed below are the main features of any RTOS (or micro-kernel).
Memory Allocation
An RTOS typically needs a mechanism to manage memory. Since a number of objects (such as tasks and queues) are created at run-time each RTOS provides a scheme to ensure correct allocation of memory. Since it is not ‘thread’ safe the standard C malloc function cannot simply be called and a custom method is provided. The efficiency and capability of these memory managers can vary between products. This could be an issue if your program dynamically creates and destroys a lot of objects at run-time.
Tasks
One of the most basic elements of an RTOS, the user’s code is written inside a task. Depending upon the RTOS the code may be written in a linear fashion (running once through to completion) or it may enter the typical while(1) loop. Some RTOSs allow for the dynamic creation and deletion of tasks whilst in others they must all be created at compilation time.
Another area of difference is task priorities. In order to select the next task to run the user assigns each task a priority, and the RTOS selects the highest one. Depending on the RTOS these priorities may be in ascending or descending order and may or may not be unique. In the latter case the user must ensure that each task has a different priority.
Different RTOS vendors may place restrictions on the total number of tasks that can be created (and this may vary between full and evaluation versions). As a guideline a typical RTOS based program may have up to 10-15 tasks operating at any one time although the primary limiting factor is usually the available memory on the device and response time.
Each task that is created will consume memory and this usually has to be defined when the task is created. The task’s stack is used to store all of its local variables, function parameters, return addresses and a copy of the processor registers when the tasks is ‘swapped out’. This can consume a considerable amount of memory and the different vendors have invented various methods to improve the situation.
One common solution is the use of ‘lightweight’ tasks which are variously called ‘co-routines’, ‘lightweight tasks’ or ‘fibres’ These simple tasks typically share a single stack and may be restricted in the functions that can be performed inside them or the way in which they have to be written.
Task Scheduling
Different RTOSs may use different scheduling systems and algorithms. The casual user often assumes that an RTOS will take of much of the detail of switching between tasks however this is only generally true in the case of a fully pre-emptive RTOS. In a co-operative RTOS the user must write their software so that the tasks execute and then yield control to other tasks at suitable points. An RTOS where many tasks can share the same priority will typically use a ‘round-robin’ scheduling algorithm to ensure that each one gets a share of the processors resources.
As stated most people want a pre-emptive RTOS but a co-operative one can operate just as efficiently and may use less processor resources.
Queues
Once the customer has broken the program down into suitable elements then they can be put into separate tasks. However, there is usually little benefit in doing this unless the tasks can communicate with each other.
A queue provides a simple first-in first-out structure that permits a programmer to send data from one task to another in a ‘safe’ manner. Since a task could be interrupted at any point it would be dangerous if task A tried to read data from a queue whilst task B was in the middle of writing data to it. The RTOS provides functions to ensure the integrity of the data.
The program can normally specify the size of the queue and also the size of each element in the queue at creation time. So you might create a queue that contains 100 chars OR you might create a queue that can contain 10 strings each one of which is 10 chars in length. In the first case you would read and write a single character to the queue whilst in the second case entire strings would be sent.
Different RTOSs may place restrictions on the number of queues that can be created as well as the maximum permissible size of data elements and the flexibility with which objects can be added or removed from queues.
Semaphores
When state information needs to be signaled between tasks this is usually done by a semaphore. The best way to think of it is as a flag (hence the name semaphore) that allows another task to become active. A task will typically wait, or pend, on a semaphore until it is set by another part of the system. Once signaled the RTOS will convey the state information and may cause the pending task to become active. The RTOS ensures that this is done in a reliable manner.
A related use for semaphores is when a resource is shared between multiple tasks. In an RTOS based system this is very common and multiple tasks may have to share many resources such as, displays, communication channels, memory objects and so on. A problem occurs when one task is accessing a resource and another task pre-empts it and changes that resource in some way. By using semaphores in this way we can prevent unwanted interactions and form a mutually exclusive operation.
An alternative option offered by some RTOSs is the mutex. Like a semaphore it is used to guard accesses to common resources or protect particular sequences of code so that they cannot be interrupted. A problem occurs if a high priority task enters a section of code and needs to access a resource currently locked by a lower priority tasks. A mutex can solve this problem by temporarily changing the priority of the task so that it can complete (called priority inheritance). Different RTOS vendors implement semaphores and mutexes in different ways and may only offer a restricted set of features.
Events/Flags
Events or event flags are a more complex way of signaling to a task from other places that a section of code should be executed. In the case of semaphores discussed previously setting one semaphore would allow one piece of code to execute. With event flags the setting of multiple items in combination can be used to trigger an action. It is possible to have a task pend upon several flags and only when they are all set will it become active and continue executing.
The degree of flexibility that can be used with events varies between RTOS vendors and they may not even exist in certain products, their functionality being replicated with alternative programming styles.
Ticks and Timers
Fundamental to the operation of most pre-emptive RTOSs is the concept of a tick timer or heartbeat. This timer operates at a moderate frequency (typically a few hundred hertz) and at each interval it interrupts the currently running task and allows the RTOS to switch to another higher priority task. This does not prevent a task completing in the meantime and causing a switch but it does provide the basic pre-emptive mechanism. If the heartbeat is set too fast then the application could spend all of its time switching in and out of the kernel whilst too slow and the system response may be impaired.
Most RTOSs contain the ability to run tasks and then cause them to delay or wait for a finite period of time. An extra option is the ability to run tasks at a predefined interval related to the tick rate. This provides the basic elements allowing timed operation of a program. In addition many RTOSs provide the capability for multiple software timers although they may be limited to the resolution of the tick timer. These timers can be used anywhere in the program and are typically used for timing longer events.
Hook Functions
Rather than directly editing the RTOS source code (which may not be possible if it is not provided) many RTOSs provide the ability to attach functions or code into parts of the kernel itself. These software hooks or callback functions are typically used to implement simple blocks of commands that may not require the creation of a separate task.
For instance, the debouncing of switch inputs may be more simply done by attaching a small piece of code to the periodic heartbeat function rather than creating a separate task to perform this function.
Software Compliance
Most RTOSs have a unique application programming interface that is specific to that product. Even given this there is still a large degree of commonality between the products and functions so whilst the names may vary it is often possible to migrate an application to another RTOS with little difficulty.
As this migration is so often desirable large software organizations have adopted standards to which software should be written so that applications can be easily ported. An example here is the POSIX® software API standard found on Linux based systems. Whilst not often found on an embedded RTOS, the POSIX call standard can be helpful when porting large public domain applications onto the smaller platforms (an example might be a web application written for ucLinux for instance).
Back to the top
|