mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-08 18:02:46 +00:00
Instead of loading all indexed doxygen symbols in one page, we use the defined groups to load the API documentation in the specific sections and reduce the trash coming from doxygen to just what we need. Change-Id: I030e3de33e8cc26871f95cd45a50af0cae1bb942 Signed-off-by: Anas Nashif <anas.nashif@intel.com>
312 lines
12 KiB
ReStructuredText
312 lines
12 KiB
ReStructuredText
.. _nanokernel_fibers:
|
|
|
|
Fiber Services
|
|
##############
|
|
|
|
Concepts
|
|
********
|
|
|
|
A fiber is a lightweight, non-preemptible thread of execution that implements
|
|
a portion of an application's processing. It is is normally used when writing
|
|
device drivers and other performance critical work.
|
|
|
|
Fibers can be used by microkernel applications, as well as by nanokernel
|
|
applications. However, fibers can only interact with microkernel object types
|
|
to a very limited degree; for more information see
|
|
:ref:`Microkernel Fiber Services <microkernel_fibers>`.
|
|
|
|
An application can use any number of fibers. Each fiber is anonymous, and
|
|
cannot be directly referenced by other fibers or tasks once it has started
|
|
executing. The properties that must be specified when a fiber is spawned
|
|
include:
|
|
|
|
* a memory region that is used for its stack and for other execution context
|
|
information,
|
|
* a function that is invoked when the fiber starts executing,
|
|
* a pair of arguments that are passed to that entry point function,
|
|
* a priority that is used by the nanokernel scheduler, and
|
|
* a set of options that apply to the fiber.
|
|
|
|
The kernel may automatically spawn zero or more system fibers during system
|
|
initialization. The specific set of fibers spawned depends on the kernel
|
|
capabilities that have been configured by the application and by the
|
|
platform configuration used to build the application image.
|
|
|
|
Fiber Lifecycle
|
|
===============
|
|
|
|
A fiber can be spawned by another fiber, by a task, or by the kernel itself
|
|
during system initialization. A fiber typically becomes executable immediately;
|
|
however it is possible to delay the scheduling of a newly spawned fiber
|
|
for a specified time period---for example, to allow device hardware which
|
|
the fiber uses to become available. The kernel also supports a delayed start
|
|
cancellation capability, which prevents a newly spawned fiber from executing
|
|
if the fiber becomes unnecessary before its full delay period is reached.
|
|
|
|
Once a fiber is started it normally executes forever. A fiber may terminate
|
|
itself gracefully by simply returning from its entry point function. If it
|
|
does, it is the fiber's responsibility to release any system resources it may
|
|
own (such as a nanokernel semaphore being used in a mutex-like manner) prior
|
|
to returning, since the kernel does *not* attempt to reclaim them so they can
|
|
be reused.
|
|
|
|
A fiber may also terminate non-gracefully by *aborting*. The kernel
|
|
automatically aborts a fiber when it generates a fatal error condition,
|
|
such as dereferencing a null pointer. A fiber can also explicitly abort itself
|
|
using :c:func:`fiber_abort()`. As with graceful fiber termination, the kernel
|
|
does not attempt to reclaim system resources owned by the fiber.
|
|
|
|
.. note::
|
|
The kernel does not currently make any claims regarding an application's
|
|
ability to restart a terminated fiber.
|
|
|
|
Fiber Scheduling
|
|
================
|
|
|
|
The nanokernel's scheduler selects which of the system's threads is allowed
|
|
to execute; this thread is known as the *current context*. The nanokernel's
|
|
scheduler permits threads to execute only when there is no ISR that needs
|
|
to execute, since ISR execution takes precedence.
|
|
|
|
When executing threads, the nanokernel's scheduler gives fiber execution
|
|
precedence over task execution. The scheduler preempts task execution
|
|
whenever a fiber needs to execute, but never preempts the execution of a fiber
|
|
to allow another fiber to execute---even if it is a higher priority fiber.
|
|
|
|
The kernel automatically takes care of saving an executing fiber's CPU register
|
|
values when it performs a context switch to a different fiber, a task, or
|
|
an ISR, and restores these values when the fiber later resumes execution.
|
|
|
|
Fiber State
|
|
-----------
|
|
|
|
A fiber has an implicit *state* that determines whether or not it can be
|
|
scheduled for execution. The state records all factors that can prevent
|
|
the fiber from executing, such as:
|
|
|
|
* the fiber has not been spawned
|
|
* the fiber is waiting for it needs (e.g. a semaphore, a timeout, ...)
|
|
* the fiber has terminated
|
|
|
|
A fiber whose state has no factors that prevent its execution is said to be
|
|
*executable*.
|
|
|
|
Fiber Priorities
|
|
----------------
|
|
|
|
The kernel supports a virtually unlimited number of fiber priority levels,
|
|
ranging from 0 (highest priority) to 2^31-1 (lowest priority). Negative
|
|
priority levels must not be used.
|
|
|
|
A fiber's original priority cannot be altered up or down after it has been
|
|
spawned.
|
|
|
|
Fiber Scheduling Algorithm
|
|
--------------------------
|
|
|
|
The nanokernel's scheduler selects the highest priority executable fiber
|
|
to be the current context, if possible. If multiple executable fibers
|
|
of that priority are available the scheduler chooses the one that has been
|
|
waiting longest.
|
|
|
|
If no executable fibers exist the scheduler selects the current task
|
|
to be the current context. In a nanokernel application the current task is
|
|
the background task, while in a microkernel application it is the current task
|
|
selected by the microkernel's scheduler. The current task is always executable.
|
|
|
|
Once a fiber becomes the current context it remains scheduled for execution
|
|
by the nanokernel until one of the following occurs:
|
|
|
|
* The fiber is supplanted by another thread because it calls a kernel API
|
|
that blocks its own execution. (For example, the fiber attempts to take
|
|
a nanokernel semaphore that is unavailable.)
|
|
|
|
* The fiber terminates itself by returning from its entry point function.
|
|
|
|
* The fiber aborts itself by performing an operation that causes a fatal error,
|
|
or by calling :c:func:`fiber_abort()`.
|
|
|
|
Once the current task becomes the current context it remains scheduled for
|
|
execution by the nanokernel until is supplanted by a fiber.
|
|
|
|
.. note::
|
|
The current task is never directly supplanted by another task, since the
|
|
microkernel scheduler uses the microkernel server fiber to initiate a
|
|
change from one microkernel task to another.
|
|
|
|
Cooperative Time Slicing
|
|
------------------------
|
|
|
|
Due to the non-preemptive nature of the nanokernel's scheduler, a fiber that
|
|
performs lengthy computations may cause an unacceptable delay in the scheduling
|
|
of other fibers, including higher priority and equal priority ones. To overcome
|
|
such problems the fiber can choose to voluntarily relinquish the CPU from time
|
|
to time to permit other fibers to execute.
|
|
|
|
A fiber can relinquish the CPU in two ways:
|
|
|
|
* Calling :c:func:`fiber_yield()` places the fiber back in the nanokernel
|
|
scheduler's list of executable fibers and then invokes the scheduler.
|
|
All executable fibers whose priority is higher or equal to that of the
|
|
yielding fiber are then allowed to execute before the yielding fiber is
|
|
rescheduled. If no such executable fibers exist, the scheduler immediately
|
|
reschedules the yielding fiber without context switching.
|
|
|
|
* Calling :c:func:`fiber_sleep()` blocks the execution of the fiber for
|
|
a specified time period. Executable fibers of all priorities are then
|
|
allowed to execute, although there is no guarantee that fibers whose
|
|
priority is lower than that of the sleeping task will actually be scheduled
|
|
before the time period expires and the sleeping task becomes executable
|
|
once again.
|
|
|
|
Fiber Options
|
|
=============
|
|
|
|
The kernel supports several *fiber options* that inform the kernel about
|
|
special treatment the fiber requires.
|
|
|
|
The set of kernel options associated with a fiber are specified when the fiber
|
|
is spawned. If the fiber uses multiple options they are separated using
|
|
:literal:`|`; i.e. the logical OR operator. A fiber that does not use any
|
|
options is spawned using an options value of 0.
|
|
|
|
The fiber options listed below are pre-defined by the kernel.
|
|
|
|
:c:macro:`USE_FP`
|
|
Instructs the kernel to save the fiber's x87 FPU and MMX floating point
|
|
context information during context switches.
|
|
|
|
:c:macro:`USE_SSE`
|
|
Instructs the kernel to save the fiber's SSE floating point context
|
|
information during context switches. (A fiber using this option
|
|
implicitly uses the :c:macro:`USE_FP` option too.)
|
|
|
|
|
|
Usage
|
|
*****
|
|
|
|
Defining a Fiber
|
|
================
|
|
|
|
The following properties must be defined when spawning a fiber:
|
|
|
|
*stack_name*
|
|
This specifies the memory region used for the fiber's stack and for
|
|
other execution context information. To ensure proper memory alignment,
|
|
it should have the following form:
|
|
|
|
.. code-block:: c
|
|
|
|
char __stack <stack_name>[<stack_size>];
|
|
|
|
*stack_size*
|
|
This specifies the size of the *stack_name* memory region, in bytes.
|
|
|
|
*entry_point*
|
|
This specifies the name of the fiber's entry point function,
|
|
which should have the following form:
|
|
|
|
.. code-block:: c
|
|
|
|
void <entry_point>(int arg1, int arg2)
|
|
{
|
|
/* fiber mainline processing */
|
|
...
|
|
/* (optional) normal fiber termination */
|
|
return;
|
|
}
|
|
|
|
*arguments*
|
|
This specifies the two arguments passed to *entry_point* when the fiber
|
|
begins executing. Non-integer arguments can be passed in by casting to
|
|
an integer type.
|
|
|
|
*priority*
|
|
This specifies the scheduling priority of the fiber.
|
|
|
|
*options*
|
|
This specifies the fiber's options.
|
|
|
|
|
|
Example: Spawning a Fiber from a Task
|
|
=====================================
|
|
|
|
This code shows how the currently executing task can spawn multiple fibers,
|
|
each dedicated to processing data from a different communication channel.
|
|
|
|
.. code-block:: c
|
|
|
|
#define COMM_STACK_SIZE 512
|
|
#define NUM_COMM_CHANNELS 8
|
|
|
|
struct descriptor {
|
|
...;
|
|
};
|
|
|
|
char __stack comm_stack[NUM_COMM_CHANNELS][COMM_STACK_SIZE];
|
|
struct descriptor comm_desc[NUM_COMM_CHANNELS] = { ... };
|
|
|
|
...
|
|
|
|
void comm_fiber(int desc_arg, int unused);
|
|
{
|
|
ARG_UNUSED(unused);
|
|
|
|
struct descriptor *desc = (struct descriptor *) desc_arg;
|
|
|
|
while (1) {
|
|
/* process packet of data from comm channel */
|
|
|
|
...
|
|
}
|
|
}
|
|
|
|
void comm_main(void)
|
|
{
|
|
...
|
|
|
|
for (int i = 0; i < NUM_COMM_CHANNELS; i++) {
|
|
task_fiber_start(&comm_stack[i][0], COMM_STACK_SIZE,
|
|
comm_fiber, (int) &comm_desc[i], 0,
|
|
10, 0);
|
|
}
|
|
|
|
...
|
|
}
|
|
|
|
APIs
|
|
****
|
|
|
|
The following APIs affecting the currently executing fiber are provided
|
|
by :file:`microkernel.h` and by :file:`nanokernel.h`:
|
|
|
|
+-----------------------------------+-----------------------------------------+
|
|
| Call | Description |
|
|
+-----------------------------------+-----------------------------------------+
|
|
| :cpp:func:`fiber_yield()` | Yields CPU to higher priority and |
|
|
| | equal priority fibers. |
|
|
+-----------------------------------+-----------------------------------------+
|
|
| :cpp:func:`fiber_sleep()` | Yields CPU for a specified time period. |
|
|
+-----------------------------------+-----------------------------------------+
|
|
| :cpp:func:`fiber_abort()` | Terminates fiber execution. |
|
|
+-----------------------------------+-----------------------------------------+
|
|
|
|
The following APIs affecting a specified fiber are provided
|
|
by :file:`microkernel.h` and by :file:`nanokernel.h`:
|
|
|
|
+------------------------------------------------+----------------------------+
|
|
| Call | Description |
|
|
+------------------------------------------------+----------------------------+
|
|
| | :c:func:`task_fiber_start()` | Spawns a new fiber. |
|
|
| | :c:func:`fiber_fiber_start()` | |
|
|
| | :c:func:`fiber_start()` | |
|
|
+------------------------------------------------+----------------------------+
|
|
| | :c:func:`task_fiber_delayed_start()` | Spawns a new fiber after |
|
|
| | :c:func:`fiber_fiber_delayed_start()` | a specified time period |
|
|
| | :c:func:`fiber_delayed_start()` | |
|
|
+------------------------------------------------+----------------------------+
|
|
| | :c:func:`task_fiber_delayed_start_cancel()` | Cancels spawning of a |
|
|
| | :c:func:`fiber_fiber_delayed_start_cancel()` | new fiber, if not already |
|
|
| | :c:func:`fiber_delayed_start_cancel()` | started. |
|
|
+------------------------------------------------+----------------------------+
|