In TNKernel, a task is a branch of the code that runs concurrently with another tasks from the programmer's point of view. At the physical level, tasks are actually executed using processor time sharing. Each task can be considered to be an independed program, which executes in its own context (processor registers, stack pointer, etc.).
When the currently running task loses its claim for executing (by the issuing of a system call or interrupt), a context switch is performed. The current context (processor registers, stack pointer, etc.) is saved and the context of another task is restored. This mechanism in the TNKernel is called the "dispatcher".
Generally, there are more than one executable task, and it is necessary to determine the order of the task switching (execution) by using some rules. "Scheduler" is a mechanism that controls the order of the task execution.
TNKernel uses a priority-based scheduling based on a priority level assigned to the each task. The smaller the value of the priority, the higher the priority level. TNKernel uses a 16 levels of priority for PIC24/dsPIC port and 32 levels for original ARM version.
Priorities 0 (highest) and 15(31) (lowest) are reserved by the system for the internal using. The user may create tasks with priorities from 1 to 14(30).
In TNKernel, more than one task can have the same (identical) priority.
There are four task states in TNKernel:
RUNNING
READY
RUNNING
and READY
states are marked as RUNNABLE
WAIT/SUSPEND
WAIT/SUSPEND
actually have one of three types:
WAITING |
The task execution is blocked until some synchronization action occurs, such as timeout expiration, semaphore available, event occurring, etc. |
SUSPENDED |
The task is forced to be blocked (switched to the non-executing state) by another task or itself |
WAITING_SUSPENDED |
Both WAITING and SUSPENDED states co-exist. In TNKernel, if a task leaves a WAITING state, but a SUSPENDED state exists, the task is not switched to the READY/RUNNING state. Similarly, if a task leaves SUSPENDED state, but a WAITING state exists, the task is not switched to the READY/RUNNING state. A task is switched to READY/RUNNING state only if there are neither WAITING nor SUSPENDED states flagged on it. |
DORMANT
One especial state when the task is not created/initialized yet is NON-EXISTENT
.
The following picture describe the task states switching rules:
The API fuctions that prompts for a state switsh is shown near the switch direction. For simply prefix tn_task_
and prefix i
(call from interrupt) is dropped.
In TNKernel, as long as the highest privilege task is running, no other task will execute unless the highest privilege task cannot execute (for instance, for being placed in the WAITING
state).
Among tasks with different priorities, the task with the highest priority is the highest privilege task and will execute.
Among tasks of the same priority, the task that entered into the runnable (RUNNING
or READY
) state first is the highest privilege task and will execute.
Example: Task A has priority 1, tasks B, C, D, E have priority 3, tasks F,G have priority 4, task I has priority 5. If all tasks are in the READY state, this is the sequence of tasks executing :
1. Task A - highest priority (priority 1)
2. Tasks B, C, D, E - in order of entering into runnable state for this priority (priority 3)
3. Tasks F, G - in order of entering into runnable state for this priority (priority 4)
4. Task I - lowest priority (priority 5)
In TNKernel, tasks with the same priority may be scheduled in round robin fashion by getting a predetermined time slice for each task with this priority.
In TNKernel, the task (timer_task
) with priority 0 (highest) is used for supporting the system tick timer functionality and the task (idle_task
) with priority 15(31) (lowest) is used for performing statistics.
TNKernel automatically creates these tasks at the system start.
Each task has an associated task control block (TCB), defined as:
typedef struct TN_TCB_S_STRUCT { TN_UWORD * task_stk; CDLL_QUEUE_S task_queue; CDLL_QUEUE_S timer_queue; CDLL_QUEUE_S block_queue; CDLL_QUEUE_S create_queue; CDLL_QUEUE_S mutex_queue; CDLL_QUEUE_S * pwait_queue; struct TN_TCB_S_STRUCT * blk_task; TN_UWORD * stk_start; TN_UWORD stk_size; void * task_func_addr; void * task_func_param; TN_UWORD base_priority; TN_UWORD priority; TN_UWORD id_task; TN_WORD task_state; TN_UWORD task_wait_reason; TN_WORD task_wait_rc; TN_UWORD tick_count; TN_UWORD tslice_count; TN_UWORD ewait_pattern; TN_UWORD ewait_mode; void * data_elem; TN_UWORD activate_count; TN_UWORD wakeup_count; TN_UWORD suspend_count; } TN_TCB_S;
task_stk |
Pointer to the task's top of stack |
task_queue |
Queue to include task in the ready/wait lists |
timer_queue |
Queue to include task in the timer(timeout,etc.) list |
block_queue |
Queue to include task in the blocked task list only used for mutexes priority ceiling protocol |
create_queue |
Queue is used to include task in create list only |
mutex_queue |
List of all mutexes locked by the tack |
pwait_queue |
Ptr to the object's (semaphor,event,etc.) wait list, the task is waiting for |
blk_task |
Store task blocking our task (for the mutexes priority ceiling protocol only) |
stk_start |
Base address of the task's stack space |
stk_size |
The task stack size (in sizeof (void*), not bytes) |
task_func_addr |
The task function pointer |
task_func_param |
The task function parameter pointer |
base_priority |
Task base priority |
priority |
Task current priority |
id_task |
ID for verification |
task_state |
Task state |
task_wait_reason |
Reason for the waiting |
task_wait_rc |
Waiting return code (reason why waiting finished) |
tick_count |
Remaining time until timeout |
tslice_count |
Time slice counter |
ewait_pattern |
Event wait pattern |
ewait_mode |
Event wait mode: _AND or _OR |
data_elem |
Location to store data queue entry, if the data queue is full |
activate_count |
Activation request count |
wakeup_count |
Wakeup request count |
suspend_count |
Suspension count - for statistic |
TN_DEBUG
defined. Nevertheless direct access to Task Control Block is not recommend for the system safety.
TNKernel has the following API functions for control tasks:
Function | Description |
---|---|
Creata and delete task | |
tn_task_create() | Create task |
tn_task_delete() | Delete task |
Reset task | |
tn_task_exit() | Exit from the current task |
tn_task_terminate() | Terminate task |
tn_task_activate() | Activate task |
tn_task_iactivate() | Activate task from interrupt |
Suspend and resume task | |
tn_task_suspend() | Suspend task |
tn_task_isuspend() | Suspend task from interrupt |
tn_task_resume() | Resume task |
tn_task_iresume() | Resume task from interrupt |
Sleep and wakeup task | |
tn_task_sleep() | Sleep task |
tn_task_wakeup() | Wakeup task |
tn_task_iwakeup() | Wakeup task from interrupt |
Forced release from WAITING |
|
tn_task_release_wait() | Forced release from WAITING state |
tn_task_irelease_wait() | Forced release from WAITING state in interrupt |
Change task priority | |
tn_task_change_priority() | Change task priority |
Get task reference | |
tn_task_reference() | Get task reference |
tn_task_ireference() | Get task reference from interrupt |