mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
* This completes task list in https://github.com/apache/nuttx/issues/11127. * This preserves selected content from cwiki and moves it to new docs. * Most pages are simple copy-paste with a simple RST formatting updates, with minor updates. * Content update / reorganization will follow later on when needed. * Files added (or updated title from cwiki -> current docs): * Documentation/implementation: * index. * cancellation_points. * Asynchronous vs. Synchronous Context Switches -> context_switches.rst. * ARMv7-M Hardfaults, SVCALL, and Debuggers -> hardfatuls.rst. * chip.h FAQ -> chip_h.rst. * Debug Output (SYSLOG) Issues -> syslog.rst. * Detaching File Descriptors -> file_descriptors.rst. * device_nodes.rst. * Dynamic Clocking -> power_management.rst. * ENOTTY ioctl() Return Value -> ioctl.rst. * memory_configurations.rst. * kernel_modules_vs_shared_libraries.rst. * NAKing USB OUT/IN Tokens -> usb.rst. * naming_arch_mcu_board_interfaces.rst. * naming_os_internals.rst. * nuttx_tasking.rst. * oneshot_timers_and_cpu_load.rst. * nuttx_initialization_sequence.rst. * short_time_delays.rst. * Signal Handler Tour -> signal_handlers.rst. * smp.rst. * syslog.rst. * Task Exit Sequence -> nuttx_tasking.rst. * tasks_vs_threads.rst. * tls.rst. * tickless_os.rst. * Why Can't Kernel Threads Have pthreads -> kernel_threads_vs_pthreads.rst. * Documentation/components/filesystem: * smartfs.rst. Signed-off-by: Tomasz 'CeDeROM' CEDRO <tomek@cedro.info>
380 lines
13 KiB
ReStructuredText
380 lines
13 KiB
ReStructuredText
.. _nuttx-tasking:
|
||
|
||
=============
|
||
NuttX Tasking
|
||
=============
|
||
|
||
An RTOS as a library
|
||
====================
|
||
|
||
What is an RTOS? NuttX, as with all RTOSs, is a collection of various features
|
||
bundled as a library. It does not execute except when either:
|
||
|
||
1. The application calls into the NuttX library code, OR
|
||
2. An interrupt occurs.
|
||
|
||
There is no meaningful way to represent an architecture that is implemented
|
||
as a library of user managed functions with a diagram.
|
||
You can however, pick any subsystem of an RTOS and represent
|
||
that in some fashion.
|
||
|
||
|
||
Kernel Threads
|
||
==============
|
||
|
||
There are some RTOS functions that are implemented by internal threads,
|
||
for instance :ref:`kernel-threads-vs-pthreads`, :ref:`tasks-vs-threads`,
|
||
:ref:`kernel-modules`.
|
||
|
||
.. todo:: Provide more content here :-)
|
||
|
||
|
||
The Scheduler
|
||
=============
|
||
|
||
Schedulers and Operating Systems
|
||
--------------------------------
|
||
|
||
An operating system is a complete environment for developing applications.
|
||
One important component of an operating system is the scheduler.
|
||
That logic that controls when tasks or threads execute.
|
||
|
||
Actually, more than that; the scheduler really determines what a task
|
||
or a thread is! Most tiny operating systems are really not operating
|
||
“systems” in the sense of providing a complete operating environment.
|
||
Rather these tiny operating systems consist really only of a scheduler.
|
||
That is how important the scheduler is.
|
||
|
||
Task Control Block (TCB)
|
||
------------------------
|
||
|
||
In NuttX a thread is any controllable sequence of instruction execution
|
||
that has its own stack.
|
||
Each task is represented by a data structure called a task control block
|
||
or TCB. That data structure is defined in the header file
|
||
``include/nuttx/sched.h``.
|
||
|
||
Task Lists
|
||
----------
|
||
|
||
These TCBs are retained in lists. The state of a task is indicated both
|
||
by the ``task_state`` field of the TCB and by a series of task lists.
|
||
Although it is not always necessary, most of these lists are prioritized
|
||
so that common list handling logic can be used (only the ``g_readytorun``,
|
||
the ``g_pendingtasks``, and the ``g_waitingforsemaphore`` lists
|
||
need to be prioritized).
|
||
|
||
All new tasks start in an initial, non-running state:
|
||
|
||
.. code-block:: c
|
||
|
||
volatile dq_queue_t g_inactivetasks;
|
||
|
||
* This is the list of all tasks that have been initialized, but not yet
|
||
activated. NOTE: This is the only list that is not prioritized.
|
||
|
||
* When the task is initialized, it is moved to a ready-to-run list.
|
||
There are two lists representing ready-to-run threads and several
|
||
lists representing blocked threads. Here are the ready-to-run threads:
|
||
|
||
.. code-block:: c
|
||
|
||
volatile dq_queue_t g_readytorun;
|
||
|
||
* This is the list of all tasks that are ready to run.
|
||
The head of this list is the currently active task;
|
||
the tail of this list is always the idle task.
|
||
|
||
.. code-block:: c
|
||
|
||
volatile dq_queue_t g_pendingtasks;
|
||
|
||
* This is the list of all tasks that are ready-to-run, but cannot be placed
|
||
in the ``g_readytorun`` list because:
|
||
|
||
1. They are higher priority than the currently active task at the head
|
||
of the ``g_readytorun`` list, AND
|
||
2. the currently active task has disabled pre-emption.
|
||
|
||
These tasks will stay in this holding list until pre-emption is again
|
||
enabled (or until the currently active task voluntarily relinquishes
|
||
the CPU).
|
||
|
||
* Tasks in the ``g_readytorun`` list may become blocked.
|
||
In this cased, their TCB will be moved to one of the blocked lists.
|
||
When the block task is ready-to-run, its TCB will be moved back to either
|
||
the ``g_readytorun`` to ``the g_pendingtasks`` lists, depending up
|
||
if pre-emption is disabled and upon the priority of the tasks.
|
||
|
||
Here are the blocked task lists:
|
||
|
||
.. code-block:: c
|
||
|
||
volatile dq_queue_t g_waitingforsemaphore;
|
||
|
||
* This is the list of all tasks that are blocked waiting for a semaphore.
|
||
|
||
.. code-block:: c
|
||
|
||
volatile dq_queue_t g_waitingforsignal;
|
||
|
||
* This is the list of all tasks that are blocked waiting for a signal
|
||
(only if signal support has not been disabled).
|
||
|
||
.. code-block:: c
|
||
|
||
volatile dq_queue_t g_waitingformqnotempty;
|
||
|
||
* This is the list of all tasks that are blocked waiting for a message queue
|
||
to become non-empty (only if message queue support has not been disabled).
|
||
|
||
.. code-block:: c
|
||
|
||
volatile dq_queue_t g_waitingformqnotfull;
|
||
|
||
* This is the list of all tasks that are blocked waiting for a message queue
|
||
to become non-full (only if message queue support has not been disabled).
|
||
|
||
.. code-block:: c
|
||
|
||
volatile dq_queue_t g_waitingforfill;
|
||
|
||
* This is the list of all tasks that are blocking waiting for a page fill
|
||
(only if on-demand paging is selected).
|
||
|
||
Reference: ``nuttx/sched/sched/sched.h``.
|
||
|
||
|
||
State Transition Diagram
|
||
========================
|
||
|
||
The state of a thread can then be easily represented with this simple state
|
||
transition diagram.
|
||
|
||
.. todo:: Provide State Transition Diagram.
|
||
|
||
|
||
Scheduling Policies
|
||
===================
|
||
|
||
In order to be a real-time OS, an RTOS must support ``SCHED_FIFO``.
|
||
That is, strict priority scheduling. The thread with the highest priority
|
||
runs.. Period. The thread with the highest priority is always associated
|
||
with the TCB at the head of the ``g_readytorun`` list.
|
||
|
||
NuttX supports one additional real-time scheduling policy: ``SCHED_RR``.
|
||
The RR stands for **round-robin** and this is sometimes called
|
||
**round-robin scheduling**. In this case, NuttX supports timeslicing.
|
||
If a task with ``SCHED_RR`` scheduling policy is running, then when each
|
||
timeslice elapses, it will give up the CPU to the next task that is
|
||
at the same priority.
|
||
|
||
.. note::
|
||
|
||
1. If there is only one task at this priority, ``SCHED_RR`` and
|
||
``SCHED_FIFO`` are the same, AND
|
||
2. ``SCHED_FIFO`` tasks are never pre-empted in this way.
|
||
|
||
|
||
Task IDs
|
||
========
|
||
|
||
Each task is represented not only by a TCB but also by a numeric task ID.
|
||
Given a task ID, the RTOS can find the TCB.
|
||
Given a TCB, the RTOS can find the task ID.
|
||
So they are functionally equivalent.
|
||
Only the task ID, however, is exposed at the RTOS/application interfaces.
|
||
|
||
|
||
NuttX Tasks
|
||
===========
|
||
|
||
Processes vs. Threads
|
||
---------------------
|
||
|
||
In larger system OS such as BSD, Linux, or Windows you will often hear
|
||
the name process used to refer to threads managed by the OS.
|
||
|
||
A process is more than a thread as we have been discussing so far.
|
||
A process is a protected environment that hosts one or more threads.
|
||
By environment we mean the set of resources set aside by the OS but
|
||
in the case of the protected environment of the process we are specifically
|
||
referring its address space.
|
||
|
||
.. note::
|
||
|
||
In order to implement the process' address space, the CPU must support
|
||
a memory management unit (MMU).
|
||
**The MMU is used to enforce the protected process environment.**
|
||
|
||
However, NuttX was designed to support the more resource constrained,
|
||
lower-end, deeply embedded MCUs. Those MCUs seldom have an MMU and,
|
||
as a consequence, can never support processes as are supported by BSD, Linux,
|
||
or Windows.
|
||
|
||
.. important:: NuttX does not support processes.
|
||
|
||
NuttX will support an MMU but it will not use the MMU to support processes.
|
||
NuttX operates only in a flat address space.
|
||
NuttX will use the MMU to control the instruction and data caches and
|
||
to support protected memory regions.
|
||
This may change in future, but this is how things are right now.
|
||
|
||
|
||
NuttX Tasks and Task Resources
|
||
------------------------------
|
||
|
||
All RTOSs support the notion of a task. A task is the RTOS's moral equivalent
|
||
of a process. Like a process, a task is a thread with an environment
|
||
associated with it.
|
||
|
||
This environment is like environment of the process but does not include
|
||
a private address space.
|
||
This environment is private and unique to a task.
|
||
Each task has its own environment.
|
||
|
||
This task environment consists of a number of resources
|
||
(as represented in the TCB). Of interest in this discussion are the following.
|
||
Note that any of these task resources may be disabled in the NuttX
|
||
configuration to reduce the NuttX memory footprint:
|
||
|
||
1. **Environment Variables**. This is the collection of variable assignments
|
||
of the form: ``VARIABLE=VALUE``.
|
||
|
||
2. **File Descriptors**. A file descriptor is a task specific number
|
||
that represents an open resource (a file or a device driver, for example).
|
||
|
||
3. **Sockets**. A socket descriptor is like a file descriptor, but
|
||
the open resource in this case is a network socket.
|
||
|
||
4. **Streams**. Streams represent standard C buffered I/O.
|
||
Streams wrap file descriptors or sockets to provide a new set of interface
|
||
functions for dealing with the standard C I/O (like ``fprintf()``,
|
||
``fwrite()``, etc.).
|
||
|
||
In NuttX, a task is created using the interface ``task_create()``.
|
||
|
||
NuttX Task Exit Sequence
|
||
------------------------
|
||
|
||
.. figure:: task_exit_sequence.png
|
||
:alt: Task Exit Sequence diagram.
|
||
|
||
Task Exit Sequence diagram.
|
||
|
||
|
||
The Pseudo File System and Device Drivers
|
||
=========================================
|
||
|
||
A full discussion of the NuttX file system belongs elsewhere,
|
||
see :ref:`nuttx-filesystem` for more details.
|
||
But in order to talk about task resources, we also need to have
|
||
a little knowledge of the NuttX file system.
|
||
|
||
NuttX implements a Virtual Files System (VFS) that may be used to communicate
|
||
with a number of different entities via the standard ``open()``, ``close()``,
|
||
``read()``, ``write()``, etc, interfaces.
|
||
Like other VFSs, the NuttX VFS will support file system mount points,
|
||
files, directories, device drivers, etc.
|
||
|
||
Also, as with other VFSs, the NuttX file system will support
|
||
pseudo-file systems, that is, file systems that appear as normal media
|
||
but are really presented under programmatic control.
|
||
In Linux, for example, you have the ``/proc`` and the ``/sys``
|
||
psuedo-file systems.
|
||
There is no physical media underlying the pseudo-file system.
|
||
|
||
The NuttX root file system is always a psuedo-file system.
|
||
This is just the opposite from Linux. With Linux the root file system
|
||
must always be some physical block device (if only an initrd ram disk).
|
||
Then once you have mounted the physical root file system, you can mount
|
||
other file systems – including Linux pseudo-filesystems like ``/proc``
|
||
or ``/sys``.
|
||
|
||
With NuttX, the root file system is always
|
||
a pseudo-file system that does not require any underlying block driver
|
||
or physical device.
|
||
Then you can mount real filesystem in the pseudo-filesystem.
|
||
|
||
This arrangement makes life much easier for the tiny embedded world (but also
|
||
has a few limitations — like where you can mount file systems).
|
||
|
||
**NuttX interacts with devices via device drivers** – that is via software
|
||
that controls hardware and conforms to certain NuttX conventions
|
||
(see ``include/nuttx/fs/fs.h``). Device drivers are represented
|
||
by device nodes in the pseudo-file system.
|
||
By convention, these device nodes are created in the ``/dev`` directory.
|
||
|
||
Now that we have digressed a little to introduce the NuttX file system
|
||
and device nodes, we can return to our discussion of task resources.
|
||
|
||
|
||
``/dev/console`` and Standard Streams
|
||
-------------------------------------
|
||
|
||
There are three special cases of I/O: ``stdin``, ``stdout``, and ``stderr``.
|
||
These are type ``FILE*`` and correspond to file descriptors ``0``, ``1``,
|
||
and ``2`` respectively.
|
||
When the very first thread is created (called the IDLE thread),
|
||
the special device node ``/dev/console`` is opened. ``/dev/console`` provides
|
||
the ``stdin``, ``stdout``, and ``stderr`` for the initial task.
|
||
|
||
|
||
Inheritance of the Task Environment and I/O Redirection
|
||
=======================================================
|
||
|
||
When one task creates a new task, that new task inherits the task resources
|
||
of its parent. This includes all of the environment variables,
|
||
file descriptors, and sockets.
|
||
|
||
.. note::
|
||
|
||
Task resources inheritance can be limited by special options
|
||
in the NuttX configuration.
|
||
|
||
So, if nothing special is done, then every task will use ``/dev/console``
|
||
for the standard I/O. However, a task may close file descriptor
|
||
``0`` through ``2`` and open a new device for standard I/O.
|
||
Then any children tasks that are created will inherit that new re-directed
|
||
standard I/O as well.
|
||
|
||
This mechanism is used throughout NuttX.
|
||
For example in the THTTPD server to redirect socket I/O to standard I/O
|
||
for CGI tasks. In the Telnet server so that new tasks inherit the
|
||
Telnet session.
|
||
|
||
|
||
Tasks vs. Pthreads
|
||
==================
|
||
|
||
Systems like Linux also support POSIX pthreads.
|
||
In the Linux environment, the process is created with one thread running
|
||
in it. But by using interfaces like ``pthread_create()``, you can create
|
||
multiple threads that run and share the same process resources.
|
||
|
||
NuttX also supports POSIX pthreads and the NuttX pthreads also support
|
||
this behavior. That is, the NuttX POSIX pthreads also share the resources
|
||
of the parent task.
|
||
|
||
However, since NuttX does not support process address environments,
|
||
the difference is not so striking.
|
||
When a task creates a pthread, the newly create pthread will share
|
||
the environment variables, file descriptors, sockets, and streams
|
||
of the parent task.
|
||
|
||
.. note::
|
||
|
||
Task resources are reference counted and will persist
|
||
as long as a thread in the task group is still active.
|
||
|
||
See :ref:`tasks-vs-threads` for more details.
|
||
|
||
|
||
Process IDs / Task IDs / Pthread IDs
|
||
====================================
|
||
|
||
The term process ID is standard (usually abbreviated as pid) and used to
|
||
identify a task in NuttX. So, more technically, this number is a task ID
|
||
as was described above.
|
||
Pthreads are also described by a ``pthread_t`` ID.
|
||
In NuttX, the ``pthread_t`` ID is also the same task ID.
|