In-Depth Analysis of FreeRTOS Kernel Source Code: From Task Scheduling to Interrupt Handling
Introduction
As a representative of embedded real-time operating systems, FreeRTOS has an elegant and efficient kernel implementation. This article will delve into the key implementations of the FreeRTOS kernel, including task scheduling, memory management, interrupt handling, and other core mechanisms.
Task Management Implementation
Task Control Block (TCB) Structure
typedef struct tskTaskControlBlock {
volatile StackType_t *pxTopOfStack; // Stack top pointer
ListItem_t xStateListItem; // State list item
ListItem_t xEventListItem; // Event list item
UBaseType_t uxPriority; // Task priority
StackType_t *pxStack; // Stack starting address
char pcTaskName[configMAX_TASK_NAME_LEN]; // Task name
#if ( portSTACK_GROWTH > 0 )
StackType_t *pxEndOfStack; // Stack end pointer
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; // Base priority (for priority inheritance)
#endif
} tskTCB;
typedef tskTCB TCB_t;
Task Creation Process
BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask)
{
TCB_t *pxNewTCB;
BaseType_t xReturn;
// Allocate TCB memory
pxNewTCB = (TCB_t *)pvPortMalloc(sizeof(TCB_t));
if (pxNewTCB != NULL) {
StackType_t *pxStack = NULL;
// Allocate task stack
pxStack = (StackType_t *)pvPortMalloc(usStackDepth * sizeof(StackType_t));
if (pxStack != NULL) {
// Initialize stack
memset(pxStack, 0xa5U, usStackDepth * sizeof(StackType_t));
// Initialize TCB
pxNewTCB->pxStack = pxStack;
// Initialize task stack frame
pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxStack + usStackDepth - 1,
pxTaskCode,
pvParameters);
// Set task attributes
prvInitialiseTCBVariables(pxNewTCB,
pcName,
uxPriority);
// Add task to ready list
prvAddTaskToReadyList(pxNewTCB);
xReturn = pdPASS;
}
}
return xReturn;
}
Scheduler Implementation
Scheduler Initialization
void vTaskStartScheduler(void)
{
// Create idle task
xIdleTaskHandle = xTaskCreateStatic(prvIdleTask,
"IDLE",
configMINIMAL_STACK_SIZE,
NULL,
portPRIVILEGE_BIT,
xIdleTaskStack,
&xIdleTaskTCB);
#if (configUSE_TIMERS == 1)
// Create timer service task
prvInitialiseTimerTaskAndQueue();
#endif
// Set system clock interrupt
xPortStartScheduler();
// Start the first task
prvStartFirstTask();
}
Core Implementation of Context Switching
void vTaskSwitchContext(void)
{
// Get the highest priority ready task
UBaseType_t uxTopPriority = uxTopReadyPriority;
// Find the highest priority ready list
while (listLIST_IS_EMPTY(&(pxReadyTasksLists[uxTopPriority]))) {
--uxTopPriority;
}
// Get the next task to run
List_t * const pxList = &(pxReadyTasksLists[uxTopPriority]);
ListItem_t * const pxListItem = listGET_OWNER_OF_NEXT_ENTRY(pxList);
TCB_t *pxTCB = listGET_LIST_ITEM_OWNER(pxListItem);
// Update current task pointer
pxCurrentTCB = pxTCB;
}
Memory Management Implementation
Heap Memory Manager
// Memory block structure
typedefstruct A_BLOCK_LINK {
struct A_BLOCK_LINK *pxNextFreeBlock;// Next free block
size_t xBlockSize; // Block size
} BlockLink_t;
// Memory allocation implementation
void *pvPortMalloc(size_t xWantedSize)
{
BlockLink_t *pxBlock;
void *pvReturn = NULL;
vTaskSuspendAll();
{
// Align size
if (xWantedSize > 0) {
xWantedSize += heapSTRUCT_SIZE;
if ((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0) {
xWantedSize += (portBYTE_ALIGNMENT -
(xWantedSize & portBYTE_ALIGNMENT_MASK));
}
}
// Find suitable memory block
if (xWantedSize <= xFreeBytesRemaining) {
pxBlock = pxFirstFreeBlock;
while ((pxBlock->xBlockSize < xWantedSize) &&
(pxBlock->pxNextFreeBlock != NULL)) {
pxBlock = pxBlock->pxNextFreeBlock;
}
if (pxBlock != NULL) {
// Allocate memory block
pvReturn = (void *)(((uint8_t *)pxBlock) + heapSTRUCT_SIZE);
// Update free list
prvRemoveBlockFromFreeList(pxBlock);
}
}
}
xTaskResumeAll();
return pvReturn;
}
Interrupt Management
Interrupt Nesting Count
// Interrupt nesting counter
static UBaseType_t uxInterruptNesting = 0;
// Enter interrupt
void vPortEnterCritical(void)
{
portDISABLE_INTERRUPTS();
uxInterruptNesting++;
if (uxInterruptNesting == 1) {
// First level interrupt
xSchedulerRunning = pdFALSE;
}
}
// Exit interrupt
void vPortExitCritical(void)
{
if (uxInterruptNesting > 0) {
uxInterruptNesting--;
if (uxInterruptNesting == 0) {
// Last level interrupt
xSchedulerRunning = pdTRUE;
}
}
if (uxInterruptNesting == 0) {
portENABLE_INTERRUPTS();
}
}
Deferred Interrupt Handling
typedef struct xPENDING_INTERRUPT {
PendedFunction_t pxFunction; // Function to be executed later
void *pvParameter1; // Parameter 1
uint32_t ulParameter2; // Parameter 2
} PendingInterrupt_t;
static PendingInterrupt_t xPendingInterrupts[configMAX_PENDING_INTERRUPTS];
// Register deferred interrupt
BaseType_t xTimerPendFunctionCallFromISR(PendedFunction_t xFunctionToPend,
void *pvParameter1,
uint32_t ulParameter2,
BaseType_t *pxHigherPriorityTaskWoken)
{
BaseType_t xReturn = pdFAIL;
if (uxPendedInterrupts < configMAX_PENDING_INTERRUPTS) {
// Save interrupt information
xPendingInterrupts[uxPendedInterrupts].pxFunction = xFunctionToPend;
xPendingInterrupts[uxPendedInterrupts].pvParameter1 = pvParameter1;
xPendingInterrupts[uxPendedInterrupts].ulParameter2 = ulParameter2;
uxPendedInterrupts++;
xReturn = pdPASS;
// Notify timer task
vTaskNotifyGiveFromISR(xTimerTaskHandle, pxHigherPriorityTaskWoken);
}
return xReturn;
}
Synchronization Mechanism Implementation
Core Implementation of Semaphores
typedef struct xSEMAPHORE {
QueueHandle_t xQueue; // Queue-based implementation
} Semaphore_t;
// Create semaphore
SemaphoreHandle_t xSemaphoreCreateBinary(void)
{
Semaphore_t *pxSemaphore;
// Create a queue of length 1
pxSemaphore = (Semaphore_t *)xQueueCreate(1, semSEMAPHORE_QUEUE_ITEM_LENGTH);
if (pxSemaphore != NULL) {
// Initialize as empty semaphore
pxSemaphore->xQueue = xQueueCreate(1, 0);
if (pxSemaphore->xQueue == NULL) {
vPortFree(pxSemaphore);
pxSemaphore = NULL;
}
}
return (SemaphoreHandle_t)pxSemaphore;
}
// Take semaphore
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
TickType_t xBlockTime)
{
Semaphore_t *pxSemaphore = (Semaphore_t *)xSemaphore;
BaseType_t xReturn;
// Receive data from queue
xReturn = xQueueReceive(pxSemaphore->xQueue,
NULL,
xBlockTime);
return xReturn;
}
Timer Services
Software Timer Implementation
typedef struct tmrTimerControl {
constchar *pcTimerName; // Timer name
ListItem_t xTimerListItem; // Timer list item
TickType_t xTimerPeriodInTicks; // Timer period
void *pvTimerID; // Timer ID
TimerCallbackFunction_t pxCallbackFunction; // Callback function
UBaseType_t uxAutoReload; // Auto-reload flag
} Timer_t;
// Create timer
TimerHandle_t xTimerCreate(const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction)
{
Timer_t *pxNewTimer;
// Allocate timer structure
pxNewTimer = (Timer_t *)pvPortMalloc(sizeof(Timer_t));
if (pxNewTimer != NULL) {
// Initialize timer attributes
pxNewTimer->pcTimerName = pcTimerName;
pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;
pxNewTimer->pvTimerID = pvTimerID;
pxNewTimer->pxCallbackFunction = pxCallbackFunction;
pxNewTimer->uxAutoReload = uxAutoReload;
vListInitialiseItem(&(pxNewTimer->xTimerListItem));
listSET_LIST_ITEM_OWNER(&(pxNewTimer->xTimerListItem),
(void *)pxNewTimer);
}
return (TimerHandle_t)pxNewTimer;
}
Conclusion
Key features of the FreeRTOS kernel implementation:
-
Concise and efficient code
- Minimized critical path
- Optimized memory access
- Reduced context switch overhead
Portability design
- Isolation of hardware-related code
- Unified portability interface
- Conditional compilation support
Strong configurability
- Rich configuration options
- Modular design
- Flexible feature trimming
Key point optimization
- Interrupt latency handling
- Priority inheritance
- Memory management strategies
It is recommended to study the following aspects in depth:
- Scheduling algorithm implementation
- Interrupt handling mechanisms
- Synchronization primitive implementation
- Memory management strategies
- Time management services
By studying the source code, deepen your understanding of core RTOS concepts.