Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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 kernal 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:

...

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:

...

Code Block
  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.

...

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).

...

Code Block
    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:

...

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. Below is the full text from the top-level TODO list coped on 2016-08-15:

...