300 lines
10 KiB
ReStructuredText
300 lines
10 KiB
ReStructuredText
.. _lifecycle_v2:
|
|
|
|
Lifecycle
|
|
#########
|
|
|
|
A :dfn:`thread` is a kernel object that is used for application processing
|
|
that is too lengthy or too complex to be performed by an ISR.
|
|
|
|
.. contents::
|
|
:local:
|
|
:depth: 2
|
|
|
|
Concepts
|
|
********
|
|
|
|
Any number of threads can be defined by an application. Each thread is
|
|
referenced by a :dfn:`thread id` that is assigned when the thread is spawned.
|
|
|
|
A thread has the following key properties:
|
|
|
|
* A **stack area**, which is a region of memory used for the thread's stack.
|
|
The **size** of the stack area can be tailored to conform to the actual needs
|
|
of the thread's processing. Special macros exist to create and work with
|
|
stack memory regions.
|
|
|
|
* A **thread control block** for private kernel bookkeeping of the thread's
|
|
metadata. This is an instance of type :c:type:`struct k_thread`.
|
|
|
|
* An **entry point function**, which is invoked when the thread is started.
|
|
Up to 3 **argument values** can be passed to this function.
|
|
|
|
* A **scheduling priority**, which instructs the kernel's scheduler how to
|
|
allocate CPU time to the thread. (See :ref:`scheduling_v2`.)
|
|
|
|
* A set of **thread options**, which allow the thread to receive special
|
|
treatment by the kernel under specific circumstances.
|
|
(See :ref:`thread_options_v2`.)
|
|
|
|
* A **start delay**, which specifies how long the kernel should wait before
|
|
starting the thread.
|
|
|
|
* An **execution mode**, which can either be supervisor or user mode.
|
|
By default, threads run in supervisor mode and allow access to
|
|
privileged CPU instructions, the entire memory address space, and
|
|
peripherals. User mode threads have a reduced set of privileges.
|
|
This depends on the :option:`CONFIG_USERSPACE` option. See :ref:`usermode`.
|
|
|
|
.. _spawning_thread:
|
|
|
|
Thread Creation
|
|
===============
|
|
|
|
A thread must be created before it can be used. The kernel initializes
|
|
the thread control block as well as one end of the stack portion. The remainder
|
|
of the thread's stack is typically left uninitialized.
|
|
|
|
Specifying a start delay of :c:macro:`K_NO_WAIT` instructs the kernel
|
|
to start thread execution immediately. Alternatively, the kernel can be
|
|
instructed to delay execution of the thread by specifying a timeout
|
|
value -- for example, to allow device hardware used by the thread
|
|
to become available.
|
|
|
|
The kernel allows a delayed start to be canceled before the thread begins
|
|
executing. A cancellation request has no effect if the thread has already
|
|
started. A thread whose delayed start was successfully canceled must be
|
|
re-spawned before it can be used.
|
|
|
|
Thread Termination
|
|
==================
|
|
|
|
Once a thread is started it typically executes forever. However, a thread may
|
|
synchronously end its execution by returning from its entry point function.
|
|
This is known as **termination**.
|
|
|
|
A thread that terminates is responsible for releasing any shared resources
|
|
it may own (such as mutexes and dynamically allocated memory)
|
|
prior to returning, since the kernel does *not* reclaim them automatically.
|
|
|
|
.. note::
|
|
The kernel does not currently make any claims regarding an application's
|
|
ability to respawn a thread that terminates.
|
|
|
|
Thread Aborting
|
|
===============
|
|
|
|
A thread may asynchronously end its execution by **aborting**. The kernel
|
|
automatically aborts a thread if the thread triggers a fatal error condition,
|
|
such as dereferencing a null pointer.
|
|
|
|
A thread can also be aborted by another thread (or by itself)
|
|
by calling :cpp:func:`k_thread_abort()`. However, it is typically preferable
|
|
to signal a thread to terminate itself gracefully, rather than aborting it.
|
|
|
|
As with thread termination, the kernel does not reclaim shared resources
|
|
owned by an aborted thread.
|
|
|
|
.. note::
|
|
The kernel does not currently make any claims regarding an application's
|
|
ability to respawn a thread that aborts.
|
|
|
|
Thread Suspension
|
|
=================
|
|
|
|
A thread can be prevented from executing for an indefinite period of time
|
|
if it becomes **suspended**. The function :cpp:func:`k_thread_suspend()`
|
|
can be used to suspend any thread, including the calling thread.
|
|
Suspending a thread that is already suspended has no additional effect.
|
|
|
|
Once suspended, a thread cannot be scheduled until another thread calls
|
|
:cpp:func:`k_thread_resume()` to remove the suspension.
|
|
|
|
.. note::
|
|
A thread can prevent itself from executing for a specified period of time
|
|
using :cpp:func:`k_sleep()`. However, this is different from suspending
|
|
a thread since a sleeping thread becomes executable automatically when the
|
|
time limit is reached.
|
|
|
|
.. _thread_options_v2:
|
|
|
|
Thread Options
|
|
==============
|
|
|
|
The kernel supports a small set of :dfn:`thread options` that allow a thread
|
|
to receive special treatment under specific circumstances. The set of options
|
|
associated with a thread are specified when the thread is spawned.
|
|
|
|
A thread that does not require any thread option has an option value of zero.
|
|
A thread that requires a thread option specifies it by name, using the
|
|
:literal:`|` character as a separator if multiple options are needed
|
|
(i.e. combine options using the bitwise OR operator).
|
|
|
|
The following thread options are supported.
|
|
|
|
:c:macro:`K_ESSENTIAL`
|
|
This option tags the thread as an :dfn:`essential thread`. This instructs
|
|
the kernel to treat the termination or aborting of the thread as a fatal
|
|
system error.
|
|
|
|
By default, the thread is not considered to be an essential thread.
|
|
|
|
:c:macro:`K_FP_REGS` and :c:macro:`K_SSE_REGS`
|
|
These x86-specific options indicate that the thread uses the CPU's
|
|
floating point registers and SSE registers, respectively. This instructs
|
|
the kernel to take additional steps to save and restore the contents
|
|
of these registers when scheduling the thread.
|
|
(For more information see :ref:`float_v2`.)
|
|
|
|
By default, the kernel does not attempt to save and restore the contents
|
|
of these registers when scheduling the thread.
|
|
|
|
:c:macro:`K_USER`
|
|
If :option:`CONFIG_USERSPACE` is enabled, this thread will be created in
|
|
user mode and will have reduced privileges. See :ref:`usermode`. Otherwise
|
|
this flag does nothing.
|
|
|
|
:c:macro:`K_INHERIT_PERMS`
|
|
If :option:`CONFIG_USERSPACE` is enabled, this thread will inherit all
|
|
kernel object permissions that the parent thread had, except the parent
|
|
thread object. See :ref:`usermode`.
|
|
|
|
Implementation
|
|
**************
|
|
|
|
Spawning a Thread
|
|
=================
|
|
|
|
A thread is spawned by defining its stack area and its thread control block,
|
|
and then calling :cpp:func:`k_thread_create()`. The stack area must be defined
|
|
using :c:macro:`K_THREAD_STACK_DEFINE` to ensure it is properly set up in
|
|
memory.
|
|
|
|
The thread spawning function returns its thread id, which can be used
|
|
to reference the thread.
|
|
|
|
The following code spawns a thread that starts immediately.
|
|
|
|
.. code-block:: c
|
|
|
|
#define MY_STACK_SIZE 500
|
|
#define MY_PRIORITY 5
|
|
|
|
extern void my_entry_point(void *, void *, void *);
|
|
|
|
K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);
|
|
struct k_thread my_thread_data;
|
|
|
|
k_tid_t my_tid = k_thread_create(&my_thread_data, my_stack_area,
|
|
K_THREAD_STACK_SIZEOF(my_stack_area),
|
|
my_entry_point,
|
|
NULL, NULL, NULL,
|
|
MY_PRIORITY, 0, K_NO_WAIT);
|
|
|
|
Alternatively, a thread can be spawned at compile time by calling
|
|
:c:macro:`K_THREAD_DEFINE`. Observe that the macro defines
|
|
the stack area, control block, and thread id variables automatically.
|
|
|
|
The following code has the same effect as the code segment above.
|
|
|
|
.. code-block:: c
|
|
|
|
#define MY_STACK_SIZE 500
|
|
#define MY_PRIORITY 5
|
|
|
|
extern void my_entry_point(void *, void *, void *);
|
|
|
|
K_THREAD_DEFINE(my_tid, MY_STACK_SIZE,
|
|
my_entry_point, NULL, NULL, NULL,
|
|
MY_PRIORITY, 0, K_NO_WAIT);
|
|
|
|
User Mode Constraints
|
|
---------------------
|
|
|
|
This section only applies if :option:`CONFIG_USERSPACE` is enabled, and a user
|
|
thread tries to create a new thread. The :c:func:`k_thread_create()` API is
|
|
still used, but there are additional constraints which must be met or the
|
|
calling thread will be terminated:
|
|
|
|
* The calling thread must have permissions granted on both the child thread
|
|
and stack parameters; both are tracked by the kernel as kernel objects.
|
|
|
|
* The child thread and stack objects must be in an uninitialized state,
|
|
i.e. it is not currently running and the stack memory is unused.
|
|
|
|
* The stack size parameter passed in must be equal to or less than the
|
|
bounds of the stack object when it was declared.
|
|
|
|
* The :c:macro:`K_USER` option must be used, as user threads can only create
|
|
other user threads.
|
|
|
|
* The :c:macro:`K_ESSENTIAL` option must not be used, user threads may not be
|
|
considered essential threads.
|
|
|
|
* The priority of the child thread must be a valid priority value, and equal to
|
|
or lower than the parent thread.
|
|
|
|
Dropping Permissions
|
|
====================
|
|
|
|
If :option:`CONFIG_USERSPACE` is enabled, a thread running in supervisor mode
|
|
may perform a one-way transition to user mode using the
|
|
:cpp:func:`k_thread_user_mode_enter()` API. This is a one-way operation which
|
|
will reset and zero the thread's stack memory. The thread will be marked
|
|
as non-essential.
|
|
|
|
Terminating a Thread
|
|
====================
|
|
|
|
A thread terminates itself by returning from its entry point function.
|
|
|
|
The following code illustrates the ways a thread can terminate.
|
|
|
|
.. code-block:: c
|
|
|
|
void my_entry_point(int unused1, int unused2, int unused3)
|
|
{
|
|
while (1) {
|
|
...
|
|
if (<some condition>) {
|
|
return; /* thread terminates from mid-entry point function */
|
|
}
|
|
...
|
|
}
|
|
|
|
/* thread terminates at end of entry point function */
|
|
}
|
|
|
|
If CONFIG_USERSPACE is enabled, aborting a thread will additionally mark the
|
|
thread and stack objects as uninitialized so that they may be re-used.
|
|
|
|
Suggested Uses
|
|
**************
|
|
|
|
Use threads to handle processing that cannot be handled in an ISR.
|
|
|
|
Use separate threads to handle logically distinct processing operations
|
|
that can execute in parallel.
|
|
|
|
Configuration Options
|
|
*********************
|
|
|
|
Related configuration options:
|
|
|
|
* :option:`CONFIG_USERSPACE`
|
|
|
|
APIs
|
|
****
|
|
|
|
The following thread APIs are provided by :file:`kernel.h`:
|
|
|
|
* :c:macro:`K_THREAD_DEFINE`
|
|
* :cpp:func:`k_thread_create()`
|
|
* :cpp:func:`k_thread_abort()`
|
|
* :cpp:func:`k_thread_suspend()`
|
|
* :cpp:func:`k_thread_resume()`
|
|
* :c:macro:`K_THREAD_STACK_DEFINE`
|
|
* :c:macro:`K_THREAD_STACK_ARRAY_DEFINE`
|
|
* :c:macro:`K_THREAD_STACK_MEMBER`
|
|
* :c:macro:`K_THREAD_STACK_SIZEOF`
|
|
* :c:macro:`K_THREAD_STACK_BUFFER`
|