zephyr/doc/object/object_basic_tasks.rst

298 lines
17 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Tasks
#####
Properties of Tasks
*******************
A Tiny Mountain task is an execution thread that implements part or all
of the application functionality using the Tiny Mountain objects
described in detail by the Nanokernel Objects and Microkernel Objects
documents. Tasks are cooperatively scheduled, and will run until they
explicitly yield, call a blocking interface or are preempted by a
higher priority task.
Defining Tasks
**************
Microkernel tasks are statically defined in the Tiny Mountain project
file (which has a file extension of .vpf). The number of tasks in a
project file is limited only by the available memory on the platform. A
task definition in the project file must specify its name, priority,
entry point, task group, and stack size. As shown below:
.. code-block:: console
% TASK NAME PRIO ENTRY STACK GROUPS
% ===============================================
TASK philTask 5 philDemo 1024 [EXE]
TASK phi1Task0 6 philEntry 1024 [PHI]
Task groups must be specified. If a task does not belong to any groups
an empty list can be specified; i.e. :literal:`[]`. A task can change
groups at runtime, but the project file defines the group the task
belongs to when it begins running. Task groups are statically
allocated, and need to be defined in the project file. For example, the
PHI group from the example above would be defined as:
.. code-block:: console
% TASKGROUP NAME
% ==============
TASKGROUP PHI
To write scalable and portable task code, observe the following
guidelines:
#. Define the task entry point prototype in the project file.
#. Use the C calling convention.
#. Use C linkage style.
.. note::
To maximize portability, use Tiny Mountain -defined objects, such
as memory maps or memory pools, instead of user-defined array
buffers.
Task Behavior
*************
When a task calls an API to operate on a Tiny Mountain 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 Application Program Interfaces
***********************************
The task APIs allow starting, stopping, suspending, resuming, aborting,
changing its priority, changing its entry point, and changing
groups.This table lists all task- and task-group-related application
program interfaces. For more information on each of those application
program interfaces see the application program interfaces documentation.
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| **Call** | **Description** |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`scheduler_time_slice_set()` | Specifies the time slice period for round\-robin task scheduling. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_abort()` | Aborts a task. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_abort_handler_set()` | Installs or removes an abort handler. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_resume()` | Marks a task as runnable. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_entry_set()` | Sets a tasks entry point. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_priority_set()` | Sets a tasks priority. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_sleep()` | Marks a task as not runnable until a timeout expires. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_start()` | Starts processing a task. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_suspend()` | Marks all tasks in a group as not runnable. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_yield()` | Yields the CPU to an equal\-priority task. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_node_id_get()`, isr_node_id_get()` | Get the tasks node ID.From an ISR call :c:func:`isr_node_id_get()`, from a task, call :c:func:`task_node_id_get()`. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_group_abort()` | Aborts a group of tasks. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_group_join()` | Adds a task to a group. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_group_leave()` | Removes a task from a group. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_group_resume()` | Resumes processing of a group. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_group_start()` | Starts processing of a group. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_group_suspend()` | Marks all tasks in a group as not runnable. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_group_mask_get()`, :c:func:`isr_task_group_mask_get()` | Gets the tasks group type.From an ISR call :c:func:`isr_task_group_mask_get()`, from a task, call :c:func:`task_group_mask_get()`. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_id_get()`, :c:func:`isr_task_id_get()` | Gets the tasks ID.From an ISR call :c:func:`isr_task_id_get()`, from a task, call :c:func:`task_id_get()`. |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| :c:func:`task_priority_get()`, :c:func:`isr_task_priority_get()` | Gets the tasks priority.From an ISR call :c:func:`isr_task_priority_get()`, from a task, call :c:func:`task_priority_get()` |
+----------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
A task can find its own ID using :c:func:`task_id_get()`. The task's own
name can be used interchangeably as the ID, however since the task's
name is chosen by the user it can be changed. Using
:c:func:`task_id_get()` is the safest way to reference a tasks name.
.. todo:: Add high level information about other APIs.
Task Implementation
*******************
Use Tiny Mountain 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/task_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 in Tiny Mountain 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.
Task Scheduling
---------------
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
^^^^^^^^^^^^^^^
Tiny Mountain 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.
Time-Slicing
------------
Time-slicing, enabled through the :c:func:`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, Tiny Mountain saves the context of the task
that is swapped out and restores the context of the task that is
swapped in.