FreeRTOS Tasks
Bob continues his article series about the open-source FreeRTOS. At the heart of FreeRTOS is the task. In part 2, looks at what a task is, what options are available to configure a task’s Task Control Block (TCB) and what features might be missing in the FreeRTOS TCB.
Growing up, I gradually discovered that I was good at multi-tasking. I first noticed it while working summers in the blueberry fields in Michigan. I could pick blueberries and muse about a myriad of topics (walking and chewing gum at the same time came later). Then, while in high school, I worked in a machine shop and operated a punch press. My mind would take endless excursions from girls to relativity—all while I stamped out the metal components of a greeting card rack for American Greetings. As I grew older, I would find myself multi-tasking in many more situations. While developing software, the compile, link and load times allowed me to do other things while waiting to test and debug my code. I answered email, made phone calls or touched base with a colleague. With the advent of the cell phone, my multi-tasking increased.
Somewhere along the line, I began to hear that perhaps this was not such a good habit. Various studies [1][2] came along with mixed results. But summary statements like this got my attention: “People who frequently use many types of media at once, or heavy media multitaskers, performed significantly worse on simple memory tasks” [3]. As I have gotten older, and complex cognitive tasks have become harder, I have attempted to break myself of this life-long habit. Figure 1 is me except for the tie!
Although there is much to learn about the effects of multi-tasking on our brains, multi-tasking operating systems are well-documented. This month we will continue our series on FreeRTOS—concentrating on the task. What is a task? What is the difference between a thread and a task? What are the key elements of a FreeRTOS task? And finally, what is missing in a FreeRTOS task that is included in more complex RTOSes?
WHAT IS A TASK?
From the FreeRTOS Tutorial [4], a task is a structured set of software that execute independently of other tasks. Of course, “independently” is a relative term. On a single core processor, only one task can execute at a time. One goal of every OS is to allow the tasks to be as independent as possible. But other tasks can affect timing, resources and stability. In Figure 2 Data Acquisition, User Interface, Network Communications and Logging are “independent” tasks. For a more detailed introduction to tasks and multi-tasking, checkout Part 1 of this article series “FreeRTOS (Part 1)” (Circuit Cellar 365, December 2020 [5]).
So, what is the difference between a thread and a task? Tasks can go by different names: process, apps or threads. In “A Note About Terminology,” the FreeRTOS tutorial [4] says:
In FreeRTOS, each thread of execution is called a “task.” There is no consensus on terminology within the embedded community, but I prefer “task” to “thread,” as thread can have a more specific meaning in some fields of application.
— ADVERTISMENT—
—Advertise Here—
What this means is that documentation for FreeRTOS uses thread and task interchangeably. Therefore, a function that is “thread safe” can be safely used in multiple tasks—it is task safe. Some operating systems (Linux) use the words “process” and “thread” for tasks or threads of execution. And Linux uses the term task to encompass the totality of a process and thread. The creator of Linux, Linus Torvalds, says:
The way Linux thinks about this … is that there is no such thing as a “process” or a “thread”. There is only the totality of the [Context of Execution] (called a “task” by Linux) [6].
Linux allows separate execution threads to inherit some of the Execution Context from a parent. This includes things like the CPU state (registers), the Memory Management Unit state if applicable (page mappings), permissions and various states of resources. For example, a process (or task) in Linux can start another process with a completely independent Execution Context. Or, it can start a thread which inherits some of the parents Execution Context. For our purposes, FreeRTOS uses thread and task (or process) interchangeably and there is truly no difference.
EXECUTION CONTEXT
Each time the scheduler or kernel needs to stop one task and start another task, the Execution Context of the first needs to be stored and the Execution Context of the second restored. Obviously, different processors will have different elements of the Execution Context. For example, if the processor does not have memory management, then there is no need to store that information. For most operating systems and FreeRTOS, this Execution Context is stored in the Task Control Block (TCB) and a stack.
Listing 1
struct tskTaskControlBlock /* The old naming convention is
used to prevent breaking kernel
aware debuggers. */
{
volatile StackType_t *pxTopOfStack; /*< Points to the location of
the last item placed on the
asks stack. THIS MUST BE
THE FIRST MEMBER OF THE
TCB STRUCT. */
ListItem_t xStateListItem; /*< The list that the state
list item of a task is
reference from denotes
the state of that task
(Ready, Blocked,
Suspended).*/
ListItem_t xEventListItem; /*< Used to reference a
task from an event list */
UBaseType_ uxPriority; /*< The priority of the
task. 0 is the lowest
priority. */
StackType_t *pxStack; /*< Points to the start
of the stack. */
char pcTaskName[ configMAX_TASK_NAME_LEN ];
/*^ Descriptive name given
to the task when created.
Facilitates debugging only.*/
} tskTCB; /*lint !e971 Unqualified char
types are allowed for strings
and single characters only.*/
LISTING 1. FreeRTOS minimal task control block (TCB).
Since FreeRTOS is highly configurable, let’s look at the minimal TCB and then look at what options are provided for your tasks. Listing 1 above shows the C code of the minimal TCB for FreeRTOS. This provides a great summary of the basics that are needed for the context of a task including:
Start of the stack: Where the stack pointer is set when the task is initialized.
Top of the stack: This points to the last item put on the stack.
State of the task: Is the task Ready, Blocked or Suspended?
Event list: For the location of call-back functions for events
Priority: The numeric value of the priority of the task (higher number is a higher priority)
My initial take on this list was that it was just lacking one thing. Every RTOS that I ever created from scratch or used had a maximum stack size so that the RTOS could determine stack-overruns. But I realized that it is not absolutely necessary. Just design the code perfectly and then thoroughly test and you will never need the OS to check for stack-overruns.
OPTIONAL PARAMETERS
That leads nicely to the optional parameters in the TCB for FreeRTOS. Of course, the pointer to the end of the stack is included for checking stack-overruns. Here are the optional parameters in the TCB:
— ADVERTISMENT—
—Advertise Here—
End of stack: This pointer is used during context switches to check for stack-overruns. It is used in method 1 of FreeRTOS’s Run Time Stack Checking. Because checking for stack-overruns at the time of a context switch may be too late, FreeRTOS also incorporates a second method that fills the stack with a known pattern, which can be checked at any time for stack-overruns. If the pattern is not present beyond the end of the stack, a task’s stack-overrun is flagged. This enables a watchdog-type task to continuously monitor each task for stack-overruns.
Memory management parameters: Not all processors have memory management and many handle it in a different manner. This pointer allows complete flexibility based on the individual port of FreeRTOS.
Critical section nesting count: This is used if you have functions that enter a critical section of code that cannot be interrupted or that cannot allow a task switch. FreeRTOS keeps track of these for you so that, if you are in a critical section of code and you call a function that enters and exits the critical section, the RTOS will not exit the critical section until the counter reaches zero. When we used the smx RTOS many years ago, we needed to maintain this ourselves.
Enable trace: This enables debuggers to provide tracing.
Enable mutexes: Mutex stands for mutual exclusivity. Mutexes can ensure atomic access to any shared resource. See my article “Concurrency in Embedded Systems (Part 5)” (Circuit Cellar 271, February 2013 [7]) for more details about mutexes.
Enable task tagging: This feature allows the task to be identified by a tag.
Amount of local thread pointers: FreeRTOS allows for pointers to global variables to be stored in the TCB. Some C libraries have global variables associated with them. errno is the most infamous. In a multi-tasking system with a single library, this can create a problem as demonstrated in Figure 3. A low priority task fails an fopen and tries to read library global: errno. A higher priority task interrupts the low priority task after the fopen but before errno is read. This causes the low priority task to have the wrong value for errno. This optional feature allows common libraries to use pointers to these global variables to be stored in the TCB to maintain deterministic results.
(Click to enlarge)
Enable run time stats: Enabling this allows FreeRTOS to accumulate how much time the task has been in a running state.
Enable newlib library: The GNU Arm embedded tool chain includes a smaller run-time library called newlib (there is also a nano version of this). This library, if used incorrectly, has re-entrancy issues with certain calls. FreeRTOS provides support for it but disavows its reliability.
Enable task notifications: The FreeRTOS Tutorial has an entire chapter on Task Notifications. The author explains it better than I can:
“Task Notifications” allows tasks to interact with other tasks, and to synchronize with ISRs, without the need for a separate communication object [Like a queues-, a semaphores- or event-group].
Allow static or dynamic stack and/or TCB: FreeRTOS allows the designer the flexibility of having both the stack and the TCB to be dynamic or static. But if so, the kernel needs to know that and keep track of it.
Abort delay functionality: If a task has a delay that has not timed out at the time it was aborted, this feature allows the timer to end when the task was aborted. Perhaps you know that your design will never abort tasks or won’t care if they get delayed because of a pending wait? Not enabling this will slightly reduce your footprint.
Enable POSIX error numbering: As you may suspect, FreeRTOS is not POSIX (Portable Operating System Interface) compliant. However, a POSIX wrapper for FreeRTOS is coming. FreeRTOS+POSIX provides a subset of the POSIX threading API. This addition to the TCB allows compatibility with POSIX error numbering.
WHAT IS MISSING?
Now let’s take a look at what’s missing in a FreeRTOS TCB. It is an apples and oranges comparison between FreeRTOS and Linux. The TCB in FreeRTOS is 75 lines of code whereas in Linux it is more than 700 lines. We don’t want FreeRTOS to become Linux! But there are some things that are missing that may be of value going forward.
— ADVERTISMENT—
—Advertise Here—
Security: Security is always on our mind and Linux supports a number of features in its equivalent TCB. Randomization of the TCB structure to increase security from prying eyes is one example.
Multi-core support: There are a few ports (Espressif for the ESP32) of a symmetric multiprocessing (SMP) version of FreeRTOS. The Linux TCB includes several variables that support SMP. As our microprocessors get more complex, non-uniform memory access (NUMA) may be required in the TCB. This allows for true multi-processors as opposed to multi-core devices which have uniform memory.
Trace capability: Linux provides a lot of tracing capability in the TCB that could enhance FreeRTOS without a significant cost.
Parent ancestry: Linux provides a lot of data for keeping track of which tasks started which. This could be useful going forward as FreeRTOS matures.
Out of memory (OOM) support: Although dynamic memory is a no-no for many embedded systems, (see MISRA C Guidelines [8]), FreeRTOS allows dynamic memory. Therefore, I think a mechanism to support OOM is essential for those systems.
CONCLUSION
One advantage of FreeRTOS is that it is still small enough to actually get a handle on what it does. Next time, just as I looked at configuring Linux in a previous article, I will look at what configuration options (other than those covered this month) are available to you. But of course, only in thin slices.
RESOURCES
References:
[1] https://www.pnas.org/content/pnas/115/40/9889.full.pdf “Minds and brains of media multitaskers: Current findings and future directions” Statements like this in their conclusion “in general, heavier media multitaskers often exhibit poorer performance in a number of cognitive domains” make one sit up and take notice.
[2] Reference 1 adds: “many studies report[ing] no performance differences between groups”
[3] October 28th, 2018 article in the Stanford News https://news.stanford.edu/2018/10/25/decade-data-reveals-heavy-multitaskers-reduced-memory-psychologist-says
[4] https://www.freertos.org/fr-content-src/uploads/2018/07/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf This is a very good and helpful tutorial.
[5] Embedded in Thin Slices: Part 1 of the FreeRTOS series in Circuit Cellar December 2020 Issue 365
[6] Check out this email from the creator of Linux Linus Torvalds https://www.evanjones.ca/software/threading-linus-msg.html
[7] Embedded in Thin Slices: Concurrency in Embedded Systems (Part 5)” (Circuit Cellar 271, February 2013)
[8] See https://www.misra.org.uk/Publications/tabid/57/Default.aspx for a great set of documentation for developing safety critical embedded systems
FreeRTOS | www.freertos.org
PUBLISHED IN CIRCUIT CELLAR MAGAZINE • FEBRUARY 2021 #367 – Get a PDF of the issue
Sponsor this ArticleBob Japenga has been designing embedded systems since 1973. From 1988 - 2020, Bob led a small engineering firm specializing in creating a variety of real-time embedded systems. Bob has been awarded 11 patents in many areas of embedded systems and motion control. Now retired, he enjoys building electronic projects with his grandchildren. You can reach him at
Bob@ListeningToGod.org