Real-Time Operating System? Wossat?

A Real-Time Operating System (RTOS) is an OS that can (or should be able to) guarantee tasks will complete in a set amount of time. This capability is really quite important in certain aspects of day to day life, or more accurately the preservation of it. Just imagine what would happen if the safety systems of a nuclear power station couldn't be guaranteed to react to a catastrophic accident instantly. Not good. This is called hard real-time. Soft real-time is when the time limits aren't critical, for example updating the mouse position on your home computer doesn't have to be hard real-time. (some gamers may disagree with me here...)

What other methods are there? (or why not just write a big loop?)

The traditional method of dealing with this would be to write directly on the hardware. A loop could be written that would execute each required operation as and when it was needed.

A swift (psuedo C) example:

    loop forever
    {
        read_from_keyboard();
        read_from_mouse();
        write_to_display();
    }
    end loop
Each function in this code would have to check whether it was supposed to do anything, for example the read_from_keyboard() function wouldn't have to do anything unless a key was pressed on the keyboard.

This is relatively easy to do for simple systems (It's actually quite fun, at least, when you're writing something that's not going to risk anybodys life), but when the systems being written get larger and more complex things get slightly more involved.

When you use a big loop there is no pre-emption, this can be good if no task is more important than the others, each task gets an equal go of the CPU. Some tasks, however, may be more important (or time critical) than the others. The higher priority task may have to wait for every other task to complete before it gets another go, which isn't much use.

Another method is to have each task examined for it's priority and whether it is ready to run (for example, a task may have to run when a certain interrupt has fired).

Another swift pseudocode example:

    loop forever
    {
        if something_on_keyboard
            read_from_keyboard();
        else if something_from_mouse
            read_from_mouse();
        else
            write_to_display();
    }

This is all well and good, but we still have no pre-emption, so this task that must run as soon after an interrupt fires as possible will still have to wait for the current task to finish (it might be a really big slow task too).

So, lets say we have a little remote controlled robot crawling about another planets surface (something like the Mars Pathfinder). It must receive data from earth and process that data. Lets say this is instructions for what to do next. It must also be able to carry out those instructions, and send information back to earth, such as photographic and experimental results. The following psuedocode should do this:

    loop forever
    {
        receive_instructions();
        act_on_instructions();
        send_data_to_earth();
    }
This is quite good, until we realise that if we're not listening for the instructions from earth when they're sent we're going to miss them. This could be sorted by only sending instructions when you know the robot's listening, but with a 20 minute lag between Earth and Mars that becomes a little bit of a problem. What if you've told it to do something then change your mind? It's going to do it anyway, because you'll have to wait for it to finish before you can send more instructions. You could always have points all through the program where you stop and listen for instructions, but that starts to get complicated. Problem.

Finally! Real-Time OS's

Real-Time Operating Systems are designed to make the life of the real time system designer that bit easier. They generally bear a close resemblance to modern desktop Operating Systems in that they allow multitasking and task pre-emption, but with real time guarantees. They also generally have built in support for hardware that would have to be coded by the RT system's programmers in the above examples.

The above space probe example could be simplified by using an RTOS. Each of the three parts, receiving data, performing the operations and sending data could be made into different tasks. Executing the instructions could be the lowest priority task, sending data could be medium priority and receiving data could be highest. This would mean the task that receives data would be able to pre-empt any of the other tasks in case of emergencies (for example, if the probe's about to drive off a cliff) and then the other tasks could react accordingly (stopping the probe from moving).

Inter-Process Communication?

We can also look back at our first keyboard/mouse/display example. To be honest just checking all of these in a big loop isn't really that bad an idea for most programs, but for the sake of demonstration we'll turn it into three tasks, one reads from the keyboard, one reads the position of the mouse and the other displays something relevent on the screen. The first thing to notice is that the keyboard and mouse tasks should be able to tell the display task what has been input and where the mouse is so that it can react accordingly. This requires some form of inter-process communication. In this example we could set up two message queues, one for keyboard, one for mouse, to pass the data into the display task. Alternatively we could just have one message queue and both input tasks pass their data using the same one. Easy peasy. Other IPC primitives would be things like flags (one task sets a flag and another task reads it) or shared memory pools.

Shared Resources

Shared resources are any sort of resource available to the tasks that may be access by more than one task. This could be a memory pool (just a chunk of memory set aside for storing/passing data), a hardware device, or one of many millions of other things. All of these shared resources require some sort of protection so that different tasks can't perform operations on the same data at the same time. Imagine you paid in a cheque to your bank, and just as the computer was putting it through you took ten quid out of an ATM. The computer reads how much money you have in your account, then subtracts ten, at this point another task reads how much you have in your account, adds the value of the cheque, and writes the total amount back into your account. Then the first task writes how much it thinks your account should have back into your account, effectively overwriting the second tasks operation. Now the bank has one very unhappy customer, and one very unhappy customer has lost some money. So what the account needs is a little protection. This is when our little friend, mutual exclusion appears.

The first task takes control of the account (using semaphores or similar) and reads the value from the account then subtracts ten from it. Now our second task tries to take control of the account, but fails, because the first task has control, so it waits. The first task finishes off and the second task can now take control. Everything works wonderfully and nobody's lost any money!

The end?

RTOS's also provide a good many other useful things. They also often offer a variety of CPU scheduling schemes, such as round robin, or purely priority based.

There are a good many different RTOS's available. Some examples are:

Each of these RTOS's have their own strengths and weaknesses. Some of them cost a fortune to use, what with licensing the kernel and suchlike, but are considered very good, whereas others are considered dire by everyone that uses them, but are (relatively) cheap and widely used.