Versions Compared

Key

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

...

Code Block
  bool test = true;
Code Block
  int main(int argc, char **argv)
  {
     printf("test: %i\n", test);
    test = false;
    printf("test: %i\n", test);
    return 0;
  }

...

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

  struct my_globals_s
  {
    bool test;
  }
Code Block
  int main(int argc, char **argv)
  {
    struct my_globals_s my_globals = { true };
Code Block
    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:

...

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

  struct my_globals_s
  {
    bool test;
  }
Code Block
  int main(int argc, char **argv)
  {
    FAR struct my_globals_s *my_globals;
Code Block
    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;
      }
Code Block
    my_globals=>test = true;
    printf("test: %i\n", my_globals->test);
    my_globals=>test = false;
    printf("test: %i\n", my_globals->test);
Code Block
    free(my_globals);
    return EXIT_SUCCESS;
  }

Memory Clean-Up

Linux Process 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:

Code Block

  Title:       FREE MEMORY ON TASK EXIT
  Description: Add an option to free all memory allocated by a task when the
               task exits. This is probably not be worth the overhead for a
               deeply embedded system.
    
               There would be complexities with this implementation as well
               because often one task allocates memory and then passes the
               memory to another:  The task that "owns" the memory may not
               be the same as the task that allocated the memory.
    
               Update.  From the NuttX forum:
               ...there is a good reason why task A should never delete task B.
               That is because you will strand memory resources. Another feature
               lacking in most flat address space RTOSs is automatic memory
               clean-up when a task exits.
    
               That behavior just comes for free in a process-based OS like Linux:
               Each process has its own heap and when you tear down the process
               environment, you naturally destroy the heap too.
    
               But RTOSs have only a single, shared heap. I have spent some time
               thinking about how you could clean up memory required by a task
               when a task exits. It is not so simple. It is not as simple as
               just keeping memory allocated by a thread in a list then freeing
               the list of allocations when the task exists.
    
               It is not that simple because you don't know how the memory is
               being used. For example, if task A allocates memory that is used
               by task B, then when task A exits, you would not want to free that
               memory needed by task B. In a process-based system, you would
               have to explicitly map shared memory (with reference counting) in
               order to share memory. So the life of shared memory in that
               environment is easily managed.
    
               I have thought that the way that this could be solved in NuttX
               would be: (1) add links and reference counts to all memory allocated
               by a thread. This would increase the memory allocation overhead!
               (2) Keep the list head in the TCB, and (3) extend mmap() and munmap()
               to include the shared memory operations (which would only manage
               the reference counting and the life of the allocation).
    
               Then what about pthreads? Memory should not be freed until the last
               pthread in the group exists. That could be done with an additional
               reference count on the whole allocated memory list (just as streams
               and file descriptors are now shared and persist until the last
               pthread exits).
    
               I think that would work but to me is very unattractive and
               inconsistent with the NuttX "small footprint" objective. ...
    
               Other issues:
  1. Memory free time would go up because you would have to remove
Code Block
             - Memory free time would go up because you would have to remove
                 the memory from that list in free().
  1. There are special cases inside the RTOS itself. For example,
Code Block

              - There are special cases inside the RTOS itself.  For example,
                if task A creates task B, then initial memory allocations for
                 task B are created by task A.  Some special allocators would
                 be required to keep this memory on the correct list (or on
                 no list at all).
    
               Updated 2016-06-25:
               For processors with an MMU (Memory Management Unit), NuttX can be
               built in a kernel mode.  In that case, each process will have a
               local copy of its heap (filled with sbrk()) and when the process
               exits, its local heap will be destroyed and the underlying page
               memory is recovered.
    
               So in this case, NuttX work just link Linux or or *nix systems:
               All memory allocated by processes or threads in processes will
               be recovered when the process exists.
    
               But not for the flat memory build.  In that case, the issues
               above do apply.  There is no safe way to recover the memory in
               that case (and even if there were, the additional overhead would
               not be acceptable on most platforms).
    
               This does not prohibit anyone from creating a wrapper for malloc()
               and an atexit() callback that frees memory on task exit.  People
               are free and, in fact, encouraged, to do that.  However, since
               it is inherently unsafe, I would never incorporate anything
               like that into NuttX.
    
  Status:      Open.  No changes are planned.
  Priority:    Medium/Low, a good feature to prevent memory leaks but would
               have negative impact on memory usage and code size.