nuttx/Documentation/os/scheduling/processes_vs_tasks.rst
Vinicius May f6ecf80ebb Documentation: brand new layout for NuttX documentation.
The documentation grew one page at a time, so the tree follows the
history of who wrote what and not the shape of NuttX. Scheduling is
spread over three places, a driver page can sit above the subsystem
that owns it, and the front page lists everything at the same level.
That is a lot to face when all you want to know is where the scheduler
lives.

This change files every page under the code it describes. It is a move,
not a rewrite: outside the ten pages named below, every page keeps the
text that is already in master, and no page's text is deleted.

What it does:

* Groups the table of contents into nine chapters.
* Moves the OS subsystems under os/: scheduling, memory, drivers,
  filesystem, networking, IPC, interrupts, libs, time.
* Renames the platform pages to the names the source tree uses, and
  derives their tags from the tree instead of by hand.
* Splits guides/ by subject.
* Adds Documentation/redirects.py, with a rule for every page that left
  its old path, so old URLs keep working. The redirect page also carries
  a link's #anchor across to the new page.

Ten pages have text that is new or rewritten. Nine of them are the
landing page of a chapter, which has to exist for the new structure:

    index                  the front page
    os/index               OS Design
    os/scheduling/index    Scheduling
    os/interrupts/index    Interrupts
    os/ipc/index           IPC
    os/time/index          Time and timers
    about/index            About
    developing/index       Developing NuttX
    ReleaseNotes/index     Release notes

The tenth is os/libs/libbuiltin, the only page here with technical
content: libs/libbuiltin/ had no page at all. Five SVG diagrams come
with these pages, hand-written XML with no editor metadata.

Nothing outside Documentation/ is touched.

How it was checked:

* Sphinx builds with -W: no warnings, and no document left outside a
  toctree.
* A script, offered in the PR, proves the narrow claim this rests on.
  For every page outside the ten named above it erases what a move
  touches -- link target, path, tag line, toctree block, table border --
  from the whole old text and the whole new text, and requires the two
  to be byte for byte identical. It also requires every sentence of a
  deleted page to turn up somewhere, and every page that left its old
  path to have a redirect, from a URL that existed, to where its content
  went. It exits non-zero and names the page if any of that is not true,
  and it tests added pages too, so forgetting to declare one cannot make
  it pass.
* An independent audit checked 133 factual claims on these ten pages
  against the tree, one shell command per claim: 130 confirmed, 1
  refuted and fixed here, 2 not checkable.
* tools/checkpatch.sh is clean over the range.

The diff is large because moving a page changes every link that points
to it. Most of it is pure renames, and board pages that gained one tag
line.

Assisted-by: Claude:claude-opus-5
2026-10-08 01:40:54 +08:00

263 lines
8.4 KiB
ReStructuredText

==============================
Linux Processes vs NuttX Tasks
==============================
You may be used to running programs that are stored in files on Linux or Windows.
If you transition to using NuttX tasks on an MCU with limited resources, you will
encounter some behavioral differences. This Wiki page will summarize a few of
those differences.
NuttX Build Types
=================
NuttX can be built in several different ways:
* **Kernel Build** The kernel build, selected with ``CONFIG_BUILD_KERNEL``, uses
the MCU's Memory Management Unit (MMU) to implement processes very similar to
Linux processes. There is no interesting discussion here; NuttX behaves very
much like Linux.
* **Flat Build** Most resource-limited MCUs have no MMU and the code is built as
a blob that runs in an unprotected, flat address space out of on-chip FLASH
memory. This build mode is selected with ``CONFIG_BUILD_FLAT`` and is, by far, the
most common way that people build NuttX. This is the interesting case to which
this Wiki page is directed.
* **Protected Build** Another build option is the protected build. This is essentially
the same as the flat build, but uses the MCU's Memory Protection Unit (MPU) to
separate unproctect user address ranges from protected system address ranges.
The comments of this Wiki page also apply in this case.
Initialization of Global Variables
==================================
Linux Behavior
--------------
If you are used to writing programs for Linux, then one thing you will notice is that
global variables are initialized only once when the system powers up. For example.
Consider this tiny program:
.. code-block:: C
bool test = true;
int main(int argc, char **argv)
{
printf("test: %i\n", test);
test = false;
printf("test: %i\n", test);
return 0;
}
If you build and run this program under Linux, you will always see this output::
test: 1
test: 0
In this case, the global variables are re-initialized each time that you load the
file into memory and run it.
NuttX Flat-Build Behavior
-------------------------
But if you build this program into on-chip FLASH and start it as a task (via, say,
``task_start()``) you will see this the first time that you run the program::
test: 1
test: 0
But after that, you will always see::
test: 0
test: 0
The test variable was initialized to true (1) only once at power up, but reset to
false (0) each time that the program runs.
If you want the same behavior when the program is built into the common FLASH blob,
then you will have modify the code so that global variables are explicitly reset
each time the program runs like:
.. code-block:: C
bool test;
int main(int argc, char **argv)
{
test = true;
printf("test: %i\n", test);
test = false;
printf("test: %i\n", test);
return 0;
}
NuttX Load-able Programs
------------------------
If you load programs from an file into RAM and execute them, as Linux does, then
NuttX will again behave like Linux. Because the flat build NuttX works the same way:
When you execute a NuttX ELF or NxFLAT module in a file, the file is copied into RAM
and the global variables are initialized before the program runs.
But code that is built into FLASH works differently. There is only one set of global
variables: All of the global variables for the blob that is the monolithic FLASH image.
They are all initialized once at power-up reset.
This is one of the things that makes porting Linux applications into the FLASH blob
more complex. You have to manually initialize each global variable in the ``main()``
each time your start the task.
Global Variables and Multiple Task Copies
=========================================
It is better to avoid the use of global variables in the flat build context whenever
possible because that usage adds another limitation: No more that one copy of the
program can run at any given time. That is because the global variables are shared
by each instance (unlike, again, running a program from a file where there is a private
copy of each global variable).
One way to support multiple copies of an in-FLASH program is to move all global variables
into a structure. If the amount of memory need for global variables is small, then each
``main()`` could simply allocate a copy of that structure on the stack. In the simple
example above, this might be:
.. code-block:: C
struct my_globals_s
{
bool test;
};
int main(int argc, char **argv)
{
struct my_globals_s my_globals = { true };
printf("test: %i\n", my_globals.test);
my_globals.test = false;
printf("test: %i\n", my_globals.test);
return EXIT_SUCCESS;
}
A pointer to the structure containing the allocated global variables would then have
to passed as a parameter to every internal function that needs access to the global
variables. So you would change a internal function like:
.. code-block:: C
static void print_value(void)
{
printf("test: %i\n", test);
}
to:
.. code-block:: C
static void print_value(FAR struct my_globals_s *globals)
{
printf("test: %i\n", globals->test);
}
Then pass a reference to the allocated global data structure each time that the
function is called like:
.. code-block:: C
print_value(&my_globals);
If the size of the global variable structure is large, then allocating the instance
on the stack might not be such a good idea. In that case, it might be better to
allocate the global variable structure using ``malloc()``. But don't forget to ``free()``
the allocated variable structure before exiting! (See the following Memory Clean-Up
discussion).
.. code-block:: C
struct my_globals_s
{
bool test;
};
int main(int argc, char **argv)
{
FAR struct my_globals_s *my_globals;
my_globals = (FAR struct my_globals_s *)malloc(sizeof(struct my_globals_s));
if (my_globals = NULL)
{
fprintf(stderr, "ERROR: Failed to allocate state structure\n");
return EXIT_FAILURE;
}
my_globals=>test = true;
printf("test: %i\n", my_globals->test);
my_globals=>test = false;
printf("test: %i\n", my_globals->test);
free(my_globals);
return EXIT_SUCCESS;
}
Memory Clean-Up
===============
Linux Process Exit
------------------
Another, unrelated thing that makes porting Linux programs into the FLASH blob
is the memory clean-up. When a Linux process exits, its entire address environment
is destroyed including all of allocated memory. This tiny program will not leak
memory if implemented as a Linux process:
.. code-block:: C
int main(int argc, char **argv)
{
char *buffer = malloc(1024);
... do stuff with buffer ...
return 0;
}
That same program, if ported into the FLASH blob will now have memory leaks because
there is no automatic clean-up of allocated memory when the task exits. Instead, you
must explicitly clean up all allocated memory by freeing it:
.. code-block:: C
int main(int argc, char **argv)
{
char *buffer = malloc(1024);
... do stuff with buffer ...
free(buffer);
return 0;
}
The memory clean-up with the Linux process exits is a consequent of the teardown of
the process address environment when the process terminates. Each process contains
its own heap; when the process address environment is torndown, that process heap
is returned to the OS page allocator. So the memory clean-up basically comes for free.
NuttX Task Exit
---------------
But when you run a task in the monolithic, on-chip FLASH blob, you share the same
heap with all other tasks. There is no magic clean-up that can find and free your
tasks's allocations within the common heap (see "Ways to Free Memory on Task Exit").
NuttX Process Exit
------------------
NOTE that when you run processes on NuttX (with ``CONFIG_BUILD_KERNEL``), NuttX also
behaves the same way as Linux: The address environment is destroyed with the task
exits and all of the memory is reclaimed. But all other cases will leak memory.
Ways to Free Memory on Task Exit
--------------------------------
There are ways that you could associate allocated memory with a task so that it could
cleaned up when the task exits. That approach has been rejected, however, because (1)
it could not be done reliably, and (2) it would add a memory allocation overhead that
would not be acceptable in context where memory is constrained.
Related issue can be found on `Github <https://github.com/apache/nuttx/issues/3345>`_.