The basic building block of software written under an RTOS is the task. Tasks are very simple to write: under most RTOSs a task is simply a subroutine. At some point in your program, you snake one or more calls to a function in the RTOS that starts tasks, telling it which subroutine is the starting point for each task and some other parameters that we’ll discuss later, such as the tasks priorityt where the RTOS should find memory for the task’s stack, and so on.
Most RTOSs allow you to have as many tasks as you could reasonably want. Each task in an RTOS is always in one of three states:
1. Running - which means that the microprocessor is executing the instructions that make up this task. Unless yours is a multiprocessor system, there is only one microprocessor, and hence only one task that is in the running state at any given time.
2. Ready - which means that some other task is in the running state but that this task has things that it could do if the microprocessor becomes available. Any number of tasks can be in this state.
3. Blocked -which means that this task hasn’t got anything to do right now, even if the microprocessor becomes available.
Tasks get into this state because they are waiting for some external event. For example, a task that handles data coming in from a network will have nothing to do when there is no data. A task that responds to the user when he presses a button has nothing to do until the user presses the button. Any number ol tasks can be in this state as well. Most RTOSs seem to proffer a double handful of other task states. Included among the offerings are suspended, pended, waiting, dormant, and delayed. These usually just amount to fine distinctions among various subcategories of the blocked and ready states listed earlier. Here we’ll lump all task states into running, ready, and blocked.
You can find out how these three states correspond with those of your RTOS by reading the manual that comes with it.
The Scheduler
A part of the RTOS called the scheduler keeps track of the state of each task and decides which one task should go into the running state. Unlike the scheduler in Unix or Windows, the schedulers in most RTOSs are entirely simpleminded about which task should get the processor: they look at priorities you assign to the tasks, and among the tasks that are not in the blocked state, the one with the highest priority runs, and the rest of them wait in the ready state.
The scheduler will not fiddle with task priorities: if a high-priority task hogs the microprocessor for a long time while lower-priority tasks are waiting in the ready state, that’s too bad. The lower-priority tasks just have to wait; the scheduler assumes that you knew what you were doing when you set the task priorities. Here we’ll adopt the fairly common use of the verb block to mean “move into the blocked stat ", the verb run to mean “move into the running state" or “be in the running state", and the verb switch to mean “change which task is in the running state". A task will only block because it decides for itself that it has run out of things to do. Other tasks in the system or the scheduler cannot decide for a task that it needs to wait for something. As a consequence of this, a task has to be running just betbre it is blocked: it has to execute the instructions that figure out that theres nothing more to do. While a task is blocked, it never gets the microprocessor. Therefore, an interrupt routine or some other task in the system must be able to signal that whatever the task was waiting for has happened. Otherwise, the task will be blocked forever.
The shuffling of tasks between the ready and running states is entirely the work of the scheduler. Tasks can block themselves, and tasks and interrupt routines can move other tasks from the blocked state to the ready state, but the scheduler has control over the running state. (Of course, if a task is moved from the blocked to the ready state and has higher priority than the task that is running, the scheduler will move it to the running state immediately. We can argue about whether the task was ever really in the ready state at all, but this is a semantic argument. The reality is that some part of the application had to do something to the task - move it out of the blocked state - and then the scheduler had to make a decision).
Here are answers to some common questions about the scheduler and task states:
- How does the scheduler know when a task has become blocked or unblocked?
The RTOS provides a collection of functions that tasks can call to tell the scheduler what events they want to wait for and to signal that events have happened. We’ll be discussing these functions later on.
- What happens if all the tasks are blocked?
lf all the tasks are blocked, then the scheduler will spin in some tight loop somewhere inside of the RTOS, waiting for something to happen. If nothing ever happens, then that’s your fault. You must make sure that something happens sooner or later by having an interrupt routine that calls some RTOS function that unblocks a task. Otherwise, your software will not be doing very much.
- What two tasks with the same priority are ready?
The answer to this is all over the map, depending upon which RTOS you use. At least one system solves this problem by making it illegal to have two tasks with the same priority. Some other RTOSs will time slice between two such tasks. Some will run on of them until it blocks and then run the other. In this case, which of the two tasksit runs also depends upon the particular RTOS.
- If one task is running and another, higher priority task unblocks, does the task that is running get stopped and moved to the ready state right away?
A preemptive RTOS will stop a lower-priority task as soon as the higher-priority task unblocks. A nonpreemptive RTOS will only take the microprocessor away from the lower-priority task when that task blocks.