The kernel's services previously referred to as "signalling services", which include object types such as semaphores, events, and mutexes, are now referred to as "synchronization services". This term more clearly conveys their use as a means to synchronize the operation of two or more entities within the kernel. Change-Id: Id24b96f2daf7d866a2d134c222e3d0b6fc568f5a Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
369 lines
16 KiB
ReStructuredText
369 lines
16 KiB
ReStructuredText
.. _kernel_fundamentals:
|
|
|
|
Kernel Fundamentals
|
|
###################
|
|
|
|
This section provides a high level overview of the concepts and capabilities
|
|
of the Zephyr kernel.
|
|
|
|
Organization
|
|
************
|
|
|
|
The central elements of the Zephyr kernel are its *microkernel* and underlying
|
|
*nanokernel*. The kernel also contains a variety of auxiliary subsystems,
|
|
including a library of device drivers and networking software.
|
|
|
|
Applications can be developed using both the microkernel and the nanokernel,
|
|
or using the nanokernel only.
|
|
|
|
The nanokernel is a high performance, multi-threaded execution environment
|
|
providing a basic set of kernel features. The nanokernel is ideal for systems
|
|
with extremely limited memory (the kernel itself requires as little as 2 KB!)
|
|
or simple multi-threading requirements (for example, a set of interrupt
|
|
handlers and a single background task). Examples of such systems include:
|
|
embedded sensor hubs, environmental sensors, simple LED wearables, and
|
|
store inventory tags.
|
|
|
|
The microkernel supplements the capabilities of the nanokernel to provide
|
|
a much richer set of kernel features. The microkernel is suitable for systems
|
|
with larger amounts of memory (50 to 900 KB), multiple communication devices
|
|
(like WIFI and Bluetooth Low Energy), and the need to support multiple
|
|
data processing tasks. Examples of such systems include: fitness wearables,
|
|
smart watches, and IoT wireless gateways.
|
|
|
|
Related sections:
|
|
|
|
* :ref:`common_kernel_services`
|
|
* :ref:`nanokernel`
|
|
* :ref:`microkernel`
|
|
|
|
Multi-threading
|
|
***************
|
|
|
|
The Zephyr kernel supports multi-threaded processing using three types
|
|
of execution contexts.
|
|
|
|
* A *task context* is a preemptible thread, and is normally used to perform
|
|
processing that is lengthy or complex. Task scheduling is priority based,
|
|
so that the execution of a higher priority task preempts the execution
|
|
of a lower priority task. The kernel also supports an optional round-robin
|
|
time slicing capability so that equal priority tasks can execute in turn,
|
|
without the risk of any one task monopolizing the CPU.
|
|
|
|
* A *fiber context* is a lightweight, non-preemptible thread, and is typically
|
|
used for device drivers and performance critical work. Fiber scheduling is
|
|
priority based, so that a higher priority fiber is scheduled for execution
|
|
before a lower priority fiber; however, once a fiber is scheduled it remains
|
|
scheduled until it performs an operation that blocks its own execution.
|
|
Fiber execution takes precedence over task execution, so tasks only execute
|
|
when no fiber can be scheduled.
|
|
|
|
* The *interrupt context* is a special kernel context that is used to execute
|
|
Interrupt Service Routines (ISRs). The interrupt context takes
|
|
precedence over all other contexts, so tasks and fibers only execute
|
|
when no ISR needs to run. (See below for more on interrupt handling.)
|
|
|
|
The Zephyr microkernel does not limit the number of tasks or fibers used
|
|
in an application; however, an application that uses only the nanokernel
|
|
is limited to a single task.
|
|
|
|
Related sections:
|
|
|
|
* :ref:`Fiber Services <fiber_services>`
|
|
* :ref:`Task Services <tasks>`
|
|
|
|
Interrupts
|
|
**********
|
|
|
|
The Zephyr kernel supports the handling of hardware interrupts and software
|
|
interrupts by interrupt handlers, known as *Interrupt Service Routines* (ISRs).
|
|
Interrupt handling takes precedence over task and fiber processing, so that
|
|
an ISR preempts the currently scheduled task or fiber whenever it needs
|
|
to run. The kernel also supports nested interrupt handling, allowing a higher
|
|
priority ISR to interrupt the execution of a lower priority ISR should the
|
|
need arise.
|
|
|
|
The nanokernel supplies ISRs for a few interrupt sources (IRQs), such as the
|
|
hardware timer device and system console device. The ISRs for all other IRQs
|
|
are supplied either by device drivers or application code. Each ISR can
|
|
be registered with the kernel at compile time, but can also be registered
|
|
dynamically once the kernel is up and running. Zephyr supports ISRs that
|
|
are written entirely in C, but also permits the use of assembly language.
|
|
|
|
In situations where an ISR cannot complete the processing of an interrupt in a
|
|
timely manner by itself the kernel's synchronization and data passing mechanisms
|
|
can be used to hand off the remaining processing to a fiber or task.
|
|
In addition, the microkernel provides a *task IRQ* object type that streamlines
|
|
the handoff to a task in a manner that does not require the device driver
|
|
or application code to supply an ISR at all.
|
|
|
|
Related sections:
|
|
|
|
* :ref:`Nanokernel Interrupt Services <nanokernel_interrupts>`
|
|
* :ref:`Microkernel Interrupt Services <microkernel_task_irqs>`
|
|
|
|
Clocks and Timers
|
|
*****************
|
|
|
|
Kernel clocking is based on time units called *ticks*, whose duration is
|
|
configurable. A 64-bit *system clock* counts the number of ticks that have
|
|
elapsed since the kernel started executing.
|
|
|
|
Zephyr also supports a higher resolution *hardware clock*, which can be used
|
|
to measure durations requiring sub-tick interval precision.
|
|
|
|
The nanokernel allows a fiber or thread to perform time-based processing
|
|
based on the system clock. This can be done either by using a nanokernel API
|
|
that supports a *timeout* argument, or by using a *timer* object that can
|
|
be set to expire after a specified number of ticks.
|
|
|
|
The microkernel also allows tasks to perform time-based processing using
|
|
timeouts and timers. Microkernel timers have additional capabilities
|
|
not provided by nanokernel timers, such as a periodic expiration mode.
|
|
|
|
Related sections:
|
|
|
|
* :ref:`kernel_clocks`
|
|
* :ref:`Nanokernel Timer Services <nanokernel_timers>`
|
|
* :ref:`Microkernel Timers Services <microkernel_timers>`
|
|
|
|
Synchronization
|
|
***************
|
|
|
|
The Zephyr kernel provides four types of objects that allow different
|
|
contexts to synchronize their execution.
|
|
|
|
The microkernel provides the object types listed below. These types are
|
|
primarily designed to be used by tasks, and have only a limited ability
|
|
to be used by fibers and ISRs.
|
|
|
|
* A *semaphore* is a counting semaphore, which indicates how many units
|
|
of a particular resource are available.
|
|
|
|
* An *event* is a binary semaphore, which simply indicates if a particular
|
|
resource is available or not.
|
|
|
|
* A *mutex* is a reentrant mutex with priority inversion protection. It is
|
|
similar to a binary semaphore, but contains additional logic to ensure that
|
|
only the owner of the associated resource can release it and to expedite the
|
|
execution of a lower priority thread that holds a resource needed by a
|
|
higher priority thread.
|
|
|
|
The nanokernel provides the object types listed below. These types are
|
|
primarily designed to be used by fibers, and have only a limited ability
|
|
to be used by tasks and ISRs.
|
|
|
|
* A *nanokernel semaphore* is a counting semaphore, which indicates
|
|
how many units of a particular resource are available.
|
|
|
|
Each of these types has specific capabilities and limitations that affect
|
|
its suitability for a given situation. For more details, see the documentation
|
|
for each specific object type.
|
|
|
|
Related sections:
|
|
|
|
* :ref:`Microkernel Synchronization Services <microkernel_synchronization>`
|
|
* :ref:`Nanokernel Synchronization Services <nanokernel_synchronization>`
|
|
|
|
Data Passing
|
|
************
|
|
|
|
The Zephyr kernel provides six types of objects that allow different
|
|
contexts to exchange data.
|
|
|
|
The microkernel provides the object types listed below. These types are
|
|
designed to be used by tasks, and cannot be used by fibers and ISRs.
|
|
|
|
* A *microkernel FIFO* is a queuing mechanism that allows tasks to exchange
|
|
fixed-size data items in an asychronous, "first in, first out" manner.
|
|
|
|
* A *mailbox* is a queuing mechanism that allows tasks to exchange
|
|
variable-size data items in a synchronous, "first in, first out" manner.
|
|
Mailboxes also support asynchronous exchanges, and allow tasks to exchange
|
|
messages both anonymously and non-anonymously using the same mailbox.
|
|
|
|
* A *pipe* is a queuing mechanism that allows a task to send a stream
|
|
of bytes to another task. Both asynchronous and synchronous exchanges
|
|
can be supported by a pipe.
|
|
|
|
The nanokernel provides the object types listed below. These types are
|
|
primarily designed to be used by fibers, and have only a limited ability
|
|
to be used by tasks and ISRs.
|
|
|
|
* A *nanokernel FIFO* is a queuing mechanism that allows contexts to exchange
|
|
variable-size data items in an asychronous, "first in, first out" manner.
|
|
|
|
* A *nanokernel LIFO* is a queuing mechanism that allows contexts to exchange
|
|
variable-size data items in an asychronous, "last in, first out" manner.
|
|
|
|
* A *nanokernel stack* is a queuing mechanism that allows contexts to exchange
|
|
32-bit data items in an asynchronous, "last in, first out" manner.
|
|
|
|
Each of these types has specific capabilities and limitations that affect
|
|
its suitability for a given situation. For more details, see the documentation
|
|
for each specific object type.
|
|
|
|
Related sections:
|
|
|
|
* :ref:`Microkernel Data Passing Services <microkernel_data>`
|
|
* :ref:`Nanokernel Data Passing Services <nanokernel_data>`
|
|
|
|
Dynamic Memory Allocation
|
|
*************************
|
|
|
|
The Zephyr kernel requires all system resources to be defined at compile-time,
|
|
and therefore provides only limited support for dynamic memory allocation.
|
|
This support can be used in place of C standard library calls like
|
|
:c:func:`malloc()` and :c:func:`free()`, albeit with certain differences.
|
|
|
|
The microkernel provides two types of objects that allow tasks to dynamically
|
|
allocate memory blocks. These object types cannot be used by fibers or ISRs.
|
|
|
|
* A *memory map* is a memory region that supports the dynamic allocation and
|
|
release of memory blocks of a single fixed size. An application can have
|
|
multiple memory maps, whose block size and block capacity can be configured
|
|
individually.
|
|
|
|
* A *memory pool* is a memory region that supports the dynamic allocation and
|
|
release of memory blocks of multiple fixed sizes. This allows more efficient
|
|
use of available memory when an application requires blocks of different
|
|
sizes. An application can have multiple memory pools, whose block sizes
|
|
and block capacity can be configured individually.
|
|
|
|
The nanokernel does not provide any support for dynamic memory allocation.
|
|
|
|
For additional information see:
|
|
|
|
* :ref:`Microkernel Memory Maps <memory_maps>`
|
|
* :ref:`Microkernel Pools <memory_pools>`
|
|
|
|
Public and Private Microkernel Objects
|
|
**************************************
|
|
|
|
Microkernel objects, such as semaphores, mailboxes, or tasks,
|
|
can usually be defined as a public object or a private object.
|
|
|
|
* A *public object* is one that is available for general use by all parts
|
|
of the application. Any code that includes :file:`zephyr.h` can interact
|
|
with the object by referencing the object's name.
|
|
|
|
* A *private object* is one that is intended for use only by a specific
|
|
part of the application, such as a single device driver or subsystem.
|
|
The object's name is visible only to code within the file where the object
|
|
is defined, hiding it from general use unless the code which defined the
|
|
object takes additional steps to share the name with other files.
|
|
|
|
Aside from the way they are defined, and the resulting visibility of
|
|
the object's name, a public object and a private object of the same type
|
|
operate in exactly the same manner using the same set of APIs.
|
|
|
|
In most cases, the decision to make a given microkernel object a public
|
|
object or a private object is simply a matter of convenience. For example,
|
|
when defining a server-type subsystem that handles requests from multiple
|
|
clients it usually makes sense to define public objects.
|
|
|
|
.. note:
|
|
Nanokernel object types can only be defined as private objects.
|
|
This means a nanokernel object can only be accessed by code
|
|
outside the file in which the object is defined if it is defined
|
|
using a global variable.
|
|
|
|
Microkernel Server
|
|
******************
|
|
|
|
The microkernel performs most operations involving microkernel objects
|
|
using a special *microkernel server* fiber, called :c:func:`_k_server`.
|
|
|
|
When a task invokes an API associated with a microkernel object type,
|
|
such as :c:func:`task_fifo_put()`, the associated operation is not
|
|
carried out directly. Instead, the following sequence of steps typically
|
|
occurs:
|
|
|
|
#. The task creates a *command packet*, which contains the input parameters
|
|
needed to carry out the desired operation.
|
|
|
|
#. The task enqueues the command packet on the microkernel server's
|
|
*command stack*. The kernel then preempts the task and schedules
|
|
the microkernel server.
|
|
|
|
#. The microkernel server dequeues the command packet from its command
|
|
stack and performs the desired operation. All output parameters for the
|
|
operation, such as the return code, are saved in the command packet.
|
|
|
|
#. When the operation is complete the microkernel server attempts
|
|
to fetch a command packet from its now empty command stack
|
|
and becomes blocked. The kernel then schedules the requesting task.
|
|
|
|
#. The task processes the command packet's output parameters to determine
|
|
the results of the operation.
|
|
|
|
The actual sequence of steps may vary from the above guideline in some
|
|
instances. For example, if the operation causes a higher priority task
|
|
to become runnable the requesting task is not scheduled for execution by
|
|
the kernel until *after* the higher priority task is first scheduled.
|
|
In addition, a few operations involving microkernel objects do not require
|
|
the use of a command packet at all.
|
|
|
|
While this indirect execution approach may seem somewhat inefficient,
|
|
it actually has a number of important benefits.
|
|
|
|
* All operations performed by the microkernel server are inherently free
|
|
from race conditions, since they are processed serially by a single fiber
|
|
that cannot be preempted by tasks or other fibers. This means the
|
|
microkernel server can manipulate any of the microkernel objects in the
|
|
system during any operation without having to take additional steps
|
|
to prevent interference by other contexts.
|
|
|
|
* Microkernel operations have minimal impact on interrupt latency, since
|
|
interrupts are never locked for a significant period to prevent race
|
|
conditions.
|
|
|
|
* The microkernel server can easily decompose complex operations into two or
|
|
more simpler operations by creating additional command packets and enqueuing
|
|
them on the command stack.
|
|
|
|
* It has the potential to reduce the overall memory footprint of the system,
|
|
since tasks using microkernel objects only have to reserve space on their
|
|
stacks for the first step of the above sequence, rather than for all steps
|
|
required to perform the operation.
|
|
|
|
Standard C Library
|
|
******************
|
|
|
|
The Zephyr kernel currently provides only the minimal subset of the
|
|
standard C library require to meet the kernel's own needs, primarily
|
|
in the areas of string manipulation and display.
|
|
|
|
Applications that require a more extensive C library can either submit
|
|
contributions that enhance the existing library or substitute
|
|
a replacement library.
|
|
|
|
Device Driver Library
|
|
*********************
|
|
|
|
The Zephyr kernel supports a variety of device drivers. The specific set of
|
|
device drivers available for an application's platform configuration varies,
|
|
based on the presence of the associated hardware components and of
|
|
corresponding device driver software.
|
|
|
|
Device drivers which are present on all supported platform configurations
|
|
are listed below.
|
|
|
|
* Interrupt controller: This device driver is used by the nanokernel's
|
|
interrupt management subsystem.
|
|
|
|
* Timer: This device driver is used by the kernel's system clock and
|
|
hardware clock subsystem.
|
|
|
|
* Serial communication: This device driver is used by the kernel's
|
|
system console subsystem.
|
|
|
|
* Random number generator: This device driver provides a source of random
|
|
numbers. (**IMPORTANT**: Certain implementations of this device driver
|
|
do not generate sequences of values that are truly random.)
|
|
|
|
Networking Library
|
|
******************
|
|
|
|
[This section briefly outlines the networking subsystems.]
|