zephyr/doc/kernel/microkernel/microkernel_tasks.rst
Rodrigo Caballero 4d8b60adca Doc: Change the Microkernel Objects to the Microkernel Services section.
Moves the Microkernel Object documentation to a new Microkernel Services
section within the Kernel Primer. Labels, cross-references, figures,
headings and filenames were updated to reflect the new structure. 

Change-Id: Ia2a91410a94caa8a97bb8211db5afc84b5dc0974
Signed-off-by: Rodrigo Caballero <rodrigo.caballero.abraham@intel.com>
2016-02-05 20:15:19 -05:00

436 lines
16 KiB
ReStructuredText

.. _tasks:
Task Services
#############
Properties of Tasks
*******************
A task is an execution thread that implements part or all
of the application functionality using the objects described in detail by the
Nanokernel Objects and Microkernel Objects sections. Tasks are cooperatively
scheduled, and will run until they explicitly yield, call a blocking interface
or are preempted by a higher priority task.
Task Groups
***********
TBD (how they are used; maximum of 32 groups; mention pre-defined task groups)
Task Behavior
*************
When a task calls an API to operate on a kernel object, it passes
an abstract object identifier called objectID. A task shall always
manipulate kernel data structures through the APIs and shall not
directly access the internals of any object, for example, the internals
of a semaphore or a FIFO.
Task Implementation
*******************
Use kernel objects and routine calls to interface a task with
other tasks running in the system. For example, achieve cooperation
between tasks by using synchronization objects, such as resources and
semaphores, or by passing parameters from one task to another using a
data-passing object.
Task Stack
==========
The compiler uses the task stack to store local task variables and to
implement parameter-passing between functions. Static and global
variables do not use memory from the stack. For more information about
defining memory segments, and the defaults used for different variable
types, consult the documentation for your compiler.
Task States
===========
Each task has a task state that the scheduler uses to determine whether
it is ready to run. This figure shows the possible task states and the
possible transitions. The most usual transitions are green,
bidirectional transitions are blue and uncommon transitions are marked
orange.
.. figure:: figures/microkernel_tasks_states.svg
:scale: 75 %
:alt: Possible Task States
Shows the possible states that a task might have and their transitions.
Starting and Stopping Tasks
---------------------------
Tasks are started in one of three ways:
+ Automatically at boot time if it is assigned to the EXE task group.
+ Another task issues a :c:func:`task_start()` for the task.
+ Another task issues a :c:func:`task_group_start()` for any task
group the task belongs to..
The scheduler manages the execution of a task once it is running. If the
task performs a return from the routine that started it, the task
terminates and its stack can be reused. This ensures that the task
terminates safely and cleanly.
Automatically Starting Tasks
----------------------------
Starting tasks automatically at boot utilizes the Task Grouping concept.
The EXE group at boot time will put all tasks belonging to the group in
a runnable state immediately after the kernel boots up.
Tasks Starting Other Tasks
--------------------------
.. todo:: Add details on how to start a task from within another task.
Tasks Scheduling Model
**********************
Once started, a task is scheduled for execution by the microkernel until
one of the following occurs:
* A higher-priority task becomes ready to run.
* The task completes.
* The task's time slice expires and another runnable task of equal
priority exists.
* The task becomes non-runnable.
Task Completion
===============
.. todo:: Add details on how tasks complete.
Task Priorities
===============
The kernel offers a configurable number of task priority levels. The
number ranges from 0 to :literal:`NUM_TASK_PRIORITIES-1`. The lowest
priority level ( :literal:`NUM_TASK_PRIORITIES-1` is reserved for use
by the microkernel's idle task. The priority of tasks is assigned
during the build process based upon the task definition in the project
file. The priority can be changed at any time, by either the task
itself or by another task calling :c:func:`task_priority_set()`.
If a task of higher priority becomes runnable, the kernel saves the
current tasks context and runs the higher-priority task. It is also
possible for a tasks priority to be temporarily changed to prevent a
condition known as priority inversion.
Priority Preemption
-------------------
The microkernel uses a priority-based preemptive scheduling algorithm
where the highest-priority task that is ready to run, runs. When a task
with a higher priority becomes runnable, the running task is
unscheduled and the task of higher priority is started. This is the
principle of preemption.
Suspended Tasks
===============
Tasks can suspend other tasks, or themselves, using
:c:func:`task_suspend()`. The task stays suspended until
:c:func:`task_resume()` or :c:func:`task_abort()` is called by another
task. Use :c:func:`task_abort()` and :c:func:`task_group_abort()` with
care, as none of the affected tasks may own or be using kernel objects
when they are called. The safest abort practice is for a task to abort
only itself.
Aborting a Task
---------------
Tasks can have an abort handler, C routines that run as a critical
section when a task is aborted. Since the routine runs as critical, it
cannot be preempted or unscheduled allowing the task to properly clean
up. Because of this, abort handlers cannot make kernel API calls.
To install an abort handler function use
:c:func:`task_abort_handler_set()`. This will bind the routine for
execution when :c:func:`task_abort()` is called, and run the abort
handler function immediately.
Task Time-Slicing
=================
Time-slicing, enabled through the :c:func:`sys_scheduler_time_slice_set()`
function, can share a processor between multiple tasks with the same
priority. When enabled, the kernel preempts a task that has run for a
certain amount of time, the time slice, and schedules another runnable
task with the same priority. The sorting of tasks of equal priority
order is a fundamental microkernel scheduling concept and is not
limited to cases involving :c:func:`task_yield()`.
The same effect as time-slicing can be achieved using
:c:func:`task_yield()`. When this call is made, the current task
relinquishes the processor if another task of the same priority is
ready to run. The calling task returns to the queue of runnable tasks.
If no other task of the same priority is runnable, the task that called
:c:func:`task_yield()` continues running.
.. note::
:c:func:`task_yield()` sorts the tasks in FIFO order.
Task Context Switches
=====================
When a task swap occurs, the kernel saves the context of the task
that is swapped out and restores the context of the task that is
swapped in.
Defining a Task
***************
Inside MDEF files
=================
The following parameters must be defined:
*name*
This specifies a unique name for the task.
*priority*
This specifies the scheduling priority of the task. A smaller
integer value indicates higher priority, with zero indicating
highest priority.
*entry_point*
This specifies the name of the task's entry point function,
which should have the following form:
.. code-block:: c
void <entry_point>(void)".
{
/* task mainline processing */
...
/* (optional) normal task termination */
return;
}
*stack_size*
This specifies the size of the task's stack, in bytes.
*groups*
This specifies the task groups the task belongs to. It consists
of a comma-separated list of task group names enclosed in square
brackets, with no embedded spaces. If a task does not belong
to any groups an empty list must be specified; i.e. :literal:`[]`.
Add an entry for a task in the project .MDEF file using the
following syntax:
.. code-block:: console
TASK %name %priority %entry_point %stack_size %groups
For example, the file :file:`projName.mdef` defines a system comprised
of six tasks as follows:
.. code-block:: console
% TASK NAME PRIO ENTRY STACK GROUPS
% ===================================================================
TASK MAIN_TASK 6 keypad_main 1024 [KEYPAD_TASKS,EXE]
TASK PROBE_TASK 2 probe_main 400 []
TASK SCREEN1_TASK 8 screen_1_main 4096 [VIDEO_TASKS]
TASK SCREEN2_TASK 8 screen_2_main 4096 [VIDEO_TASKS]
TASK SPEAKER1_TASK 10 speaker_1_main 1024 [AUDIO_TASKS]
TASK SPEAKER2_TASK 10 speaker_2_main 1024 [AUDIO_TASKS]
Inside Source Code
==================
In addition to defining tasks in MDEF file, it is also possible to
define tasks inside code. The macro ``DEFINE_TASK(...)`` can be
used for this purpose.
For example, the following code can be used to define a global task
``PRIV_TASK``.
.. code-block:: c
DEFINE_TASK(PRIV_TASK, priority, entry, stack_size, groups);
where the parameters are the same as tasks defined in MDEF file.
The task ``PRIV_TASK`` can be used in the same style as those
defined in MDEF file.
It is possible to utilize this task in another source file, simply
add:
.. code-block:: c
extern const ktask_t PRIV_TASK;
to that file. The task ``PRIV_TASK`` can be then used there.
Defining a Task Group
*************************
The following parameters must be defined:
*name*
This specifies a unique name for the task group.
Add an entry for a task in the project .MDEF file using the
following syntax:
.. code-block:: console
TASKGROUP %name
For example, the file :file:`projName.mdef` defines three new
task groups as follows:
.. code-block:: console
% TASKGROUP NAME
% ========================
TASKGROUP VIDEO_TASKS
TASKGROUP AUDIO_TASKS
TASKGROUP KEYPAD_TASKS
Example: Starting a Task from a Different Task
==============================================
This code shows how the currently executing task can start another task.
.. code-block:: c
void keypad_main(void)
{
/* begin system initialization */
...
/* start task to monitor temperature */
task_start(PROBE_TASK);
/* continue to bring up and operate system */
...
}
Example: Suspending and Resuming a Set of Tasks
===============================================
This code shows how the currently executing task can temporarily suspend
the execution of all tasks belonging to the designated task groups.
.. code-block:: c
void probe_main(void)
{
int was_overheated = 0;
/* continuously monitor temperature */
while (1) {
now_overheated = overheating_update();
/* suspend non-essential tasks when overheating is detected */
if (now_overheated && !was_overheated) {
task_group_suspend(VIDEO_TASKS | AUDIO_TASKS);
was_overheated = 1;
}
/* resume non-essential tasks when overheating abates */
if (!now_overheated && was_overheated) {
task_group_resume(VIDEO_TASKS | AUDIO_TASKS);
was_overheated = 0;
}
/* wait 10 ticks of system clock before checking again */
task_sleep(10);
}
}
APIs
****
The following APIs affecting the currently executing task
are provided by :file:`microkernel.h`.
+-------------------------------------+-----------------------------------------+
| Call | Description |
+=====================================+=========================================+
| :c:func:`task_id_get()` | Gets the task's ID. |
+-------------------------------------+-----------------------------------------+
| :c:func:`isr_task_id_get()` | Gets the task's ID from an ISR. |
+-------------------------------------+-----------------------------------------+
| :c:func:`task_priority_get()` | Gets the task's priority. |
+-------------------------------------+-----------------------------------------+
| :c:func:`isr_task_priority_get()` | Gets the task's priority from an ISR. |
+-------------------------------------+-----------------------------------------+
| :c:func:`task_group_mask_get()` | Gets the task's group memberships. |
+-------------------------------------+-----------------------------------------+
| :c:func:`isr_task_group_mask_get()` | Gets the task's group memberships from |
| | an ISR. |
+-------------------------------------+-----------------------------------------+
| :c:func:`task_yield()` | Yields CPU to equal-priority tasks. |
+-------------------------------------+-----------------------------------------+
| :c:func:`task_sleep()` | Yields CPU for a specified time period. |
+-------------------------------------+-----------------------------------------+
| :c:func:`task_abort_handler_set()` | Installs the task's abort handler. |
+-------------------------------------+-----------------------------------------+
The following APIs affecting a specified task
are provided by :file:`microkernel.h`.
+-------------------------------------------+----------------------------------+
| Call | Description |
+===========================================+==================================+
| :c:func:`task_priority_set()` | Sets a task's priority. |
+-------------------------------------------+----------------------------------+
| :c:func:`task_entry_set()` | Sets a task's entry point. |
+-------------------------------------------+----------------------------------+
| :c:func:`task_start()` | Starts execution of a task. |
+-------------------------------------------+----------------------------------+
| :c:func:`task_suspend()` | Suspends execution of a task. |
+-------------------------------------------+----------------------------------+
| :c:func:`task_resume()` | Resumes execution of a task. |
+-------------------------------------------+----------------------------------+
| :c:func:`task_abort()` | Aborts execution of a task. |
+-------------------------------------------+----------------------------------+
| :c:func:`task_group_join()` | Adds a task to the specified |
| | task group(s). |
+-------------------------------------------+----------------------------------+
| :c:func:`task_group_leave()` | Removes a task from the |
| | specified task group(s). |
+-------------------------------------------+----------------------------------+
The following APIs affecting multiple tasks
are provided by :file:`microkernel.h`.
+-------------------------------------------+---------------------------------+
| Call | Description |
+===========================================+=================================+
| :c:func:`sys_scheduler_time_slice_set()` | Sets the time slice period used |
| | in round-robin task scheduling. |
+-------------------------------------------+---------------------------------+
| :c:func:`task_group_start()` | Starts execution of all tasks |
| | in the specified task groups. |
+-------------------------------------------+---------------------------------+
| :c:func:`task_group_suspend()` | Suspends execution of all tasks |
| | in the specified task groups. |
+-------------------------------------------+---------------------------------+
| :c:func:`task_group_resume()` | Resumes execution of all tasks |
| | in the specified task groups. |
+-------------------------------------------+---------------------------------+
| :c:func:`task_group_abort()` | Aborts execution of all tasks |
| | in the specified task groups. |
+-------------------------------------------+---------------------------------+