
개요:
Idle Task는 FreeRTOS가 자동으로 생성하는 특수한 Task로, 다른 모든 Task가 Blocked 또는 Suspended 상태일 때 실행됩니다.
주요 특징:
Idle Task의 주요 역할:
삭제된 Task의 메모리 정리
시스템이 완전히 멈추지 않도록 보장
저전력 모드 진입
Idle Task 동작 흐름:
모든 Task가 Blocked/Suspended
↓
Idle Task 실행
↓
삭제 대기 중인 Task 메모리 해제
↓
Idle Hook 함수 호출 (설정된 경우)
↓
저전력 모드 진입 (설정된 경우)
↓
다른 Task가 Ready 되면 즉시 양보
FreeRTOS 내부 Idle Task 함수 (개념):
/* FreeRTOS 내부 Idle Task 구현 (단순화된 버전) */
static void prvIdleTask(void *pvParameters) {
/* Idle Task는 무한 루프로 실행됨 */
for(;;) {
/* 1. 삭제 대기 중인 Task의 메모리 정리 */
prvCheckTasksWaitingTermination();
/* 2. Idle Hook 함수 호출 (저전력 모드 전환/시스템 상태 모니터링/백그라운드 정리 작업 용도) */
#if (configUSE_IDLE_HOOK == 1)
{
extern void vApplicationIdleHook(void);
vApplicationIdleHook();
}
#endif
/* 3. 다른 Task에게 양보할 기회 제공 */
#if (configIDLE_SHOULD_YIELD == 1)
{
/* 같은 우선순위의 다른 Task가 있으면 양보 */
taskYIELD();
}
#endif
/* 4. 저전력 모드 진입 (Tickless Idle) */
#if (configUSE_TICKLESS_IDLE != 0)
{
portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime);
}
#endif
}
}
FreeRTOSConfig.h 설정:
/* Idle Hook 함수 사용 여부 */
#define configUSE_IDLE_HOOK 1
/* Idle Task가 다른 Task에게 양보할지 여부
* 1: 같은 우선순위(0)의 다른 Task가 있으면 양보
* 0: 선점될 때까지 계속 실행 */
#define configIDLE_SHOULD_YIELD 1
/* Tickless Idle 모드 사용 여부 (저전력) */
#define configUSE_TICKLESS_IDLE 0
/* Idle Task 스택 크기 설정 */
#define configMINIMAL_STACK_SIZE 128
Idle Task 동작 확인 예제:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
volatile uint32_t idleCounter = 0;
void vApplicationIdleHook(void) {
/* Idle Task가 실행될 때마다 카운터 증가
* 이 함수는 Idle Task의 매 루프마다 호출됨 */
idleCounter++;
}
void vTask1(void *pvParameters) {
while(1) {
printf("[Task1] 실행\n");
vTaskDelay(pdMS_TO_TICKS(1000)); /* 1초 대기 */
}
}
void vTask2(void *pvParameters) {
while(1) {
printf("[Task2] 실행\n");
vTaskDelay(pdMS_TO_TICKS(1500)); /* 1.5초 대기 */
}
}
void vMonitorTask(void *pvParameters) {
uint32_t lastCounter = 0;
while(1) {
uint32_t currentCounter = idleCounter;
uint32_t idleExecutions = currentCounter - lastCounter;
printf("\n[모니터] Idle Task 실행 횟수: %lu\n", idleExecutions);
printf(" (높을수록 시스템이 유휴 상태)\n");
lastCounter = currentCounter;
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
int main(void) {
printf("=== Idle Task 동작 확인 ===\n\n");
xTaskCreate(vTask1, "Task1", 256, NULL, 2, NULL);
xTaskCreate(vTask2, "Task2", 256, NULL, 2, NULL);
xTaskCreate(vMonitorTask, "Monitor", 512, NULL, 1, NULL);
vTaskStartScheduler();
while(1);
}
앞서 사용한 개발자 정의 Hook 함수와는 달리 VApplicationIdleHook()는 정의(Define)만 해두면 호출(Call)은 OS가 알아서 실행하는 콜백(Callback) 메커니즘을 이용합니다.
함수 원형:
void vApplicationIdleHook(void);
특징:
주의사항:
void vApplicationIdleHook(void) {
/* 주의사항 및 금지 항목 */
/* vTaskDelay(pdMS_TO_TICKS(100)); */
/* xQueueReceive(queue, &data, portMAX_DELAY); */
/* while(1) {}; */
}
패턴 1: 백그라운드 작업
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
/* 체크섬 계산 상태 */
typedef struct {
uint8_t *data;
uint32_t length;
uint32_t currentIndex;
uint32_t checksum;
bool complete;
} ChecksumTask;
ChecksumTask checksumState = {0};
/* 대용량 데이터의 체크섬을 백그라운드에서 계산 */
void vApplicationIdleHook(void) {
if(!checksumState.complete && checksumState.data != NULL) {
/* 한 번에 소량만 처리 (Idle Hook은 빨리 리턴해야 함) */
for(int i = 0; i < 10 && checksumState.currentIndex < checksumState.length; i++) {
checksumState.checksum += checksumState.data[checksumState.currentIndex];
checksumState.currentIndex++;
}
/* 완료 확인 */
if(checksumState.currentIndex >= checksumState.length) {
checksumState.complete = true;
printf("[Idle Hook] 체크섬 계산 완료: 0x%08lX\n", checksumState.checksum);
}
}
}
void start_checksum_calculation(uint8_t *data, uint32_t length) {
checksumState.data = data;
checksumState.length = length;
checksumState.currentIndex = 0;
checksumState.checksum = 0;
checksumState.complete = false;
printf("[메인] 백그라운드 체크섬 계산 시작 (%lu bytes)\n", length);
}
void vMainTask(void *pvParameters) {
uint8_t testData[1000];
/* 테스트 데이터 초기화 */
for(int i = 0; i < 1000; i++) {
testData[i] = i & 0xFF;
}
/* 백그라운드 체크섬 계산 시작 */
start_checksum_calculation(testData, 1000);
while(1) {
printf("[메인 Task] 다른 작업 수행 중...\n");
if(checksumState.complete) {
printf("[메인 Task] 체크섬 결과 확인: 0x%08lX\n", checksumState.checksum);
/* 새로운 계산 시작 */
checksumState.complete = false;
start_checksum_calculation(testData, 1000);
}
vTaskDelay(pdMS_TO_TICKS(2000)); //여기서 vApplicationIdleHook()가 2초(Delay time)동안 실행됨
}
}
패턴 2: 워치독 갱신
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
volatile uint32_t watchdogCounter = 0;
/* Idle Hook에서 워치독 갱신 */
void vApplicationIdleHook(void) {
/* 하드웨어 워치독 타이머 리셋 */
watchdogCounter++;
/* 시스템 정상성 체크 후 워치독 갱신(WatchDog Feeding), :
* WATCHDOG_RESET_REGISTER = WATCHDOG_RESET_VALUE; */
}
void vMonitorTask(void *pvParameters) {
uint32_t lastCounter = 0;
while(1) {
uint32_t currentCounter = watchdogCounter;
uint32_t resetCount = currentCounter - lastCounter;
printf("[모니터] 워치독 리셋 횟수: %lu\n", resetCount);
if(resetCount < 100) {
printf("경고: 시스템 과부하 가능성\n");
}
lastCounter = currentCounter;
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
패턴 3: 유휴 시간(Idle Time) 측정
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
volatile uint32_t idleTimeCounter = 0;
volatile uint32_t totalTimeCounter = 0;
void vApplicationIdleHook(void) {
/* Idle Task 실행 시간 카운트 */
idleTimeCounter++;
}
/* Tick Hook에서 전체 시간 카운트 */
void vApplicationTickHook(void) {
totalTimeCounter++;
}
void vCpuUsageTask(void *pvParameters) {
uint32_t lastIdle = 0;
uint32_t lastTotal = 0;
while(1) {
uint32_t currentIdle = idleTimeCounter;
uint32_t currentTotal = totalTimeCounter;
uint32_t idleDelta = currentIdle - lastIdle;
uint32_t totalDelta = currentTotal - lastTotal;
if(totalDelta > 0) {
uint32_t cpuUsage = 100 - ((idleDelta * 100) / totalDelta);
printf("[CPU 사용률] %lu%%\n", cpuUsage);
}
lastIdle = currentIdle;
lastTotal = currentTotal;
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
FreeRTOSConfig.h:
/* Idle Hook 함수 활성화 */
#define configUSE_IDLE_HOOK 1
/* Tick Hook도 함께 사용하는 경우 */
#define configUSE_TICK_HOOK 1
Tick Hook은 시간이 흐흘 때마다 무조건 실행되는 함수입니다.
전체 예제:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
/* 통계 변수 */
volatile uint32_t idleExecutions = 0;
volatile uint32_t tickCount = 0;
void vApplicationIdleHook(void) {
idleExecutions++;
/* LED 토글 (실제 하드웨어 동작을 보고 시스템 정상성을 체크하는 경우) */
/* toggle_status_led(); */
}
void vApplicationTickHook(void) {
tickCount++;
}
/* 주 동작부, vWorkerTask가 잠들면 Idle Task가 돌면서 idleExecutions를 증가시키고 LED 작동*/
void vWorkerTask(void *pvParameters) {
int taskId = (int)pvParameters;
while(1) {
printf("[Worker %d] 작업 수행\n", taskId);
/* 작업 부하 시뮬레이션 */
for(volatile int i = 0; i < 100000; i++);
vTaskDelay(pdMS_TO_TICKS(500 + taskId * 200));
}
}
/*Idle Task가 실행되는 동안의 수치(LED_toggle Count)를 모아 시스템 상태치 반환*/
void vStatisticsTask(void *pvParameters) {
uint32_t lastIdle = 0;
uint32_t lastTick = 0;
while(1) {
uint32_t currentIdle = idleExecutions;
uint32_t currentTick = tickCount;
uint32_t idleDelta = currentIdle - lastIdle;
uint32_t tickDelta = currentTick - lastTick;
printf("\n=== 시스템 통계 ===\n");
printf("Tick 증가: %lu\n", tickDelta);
printf("Idle 실행: %lu회\n", idleDelta);
if(tickDelta > 0) {
uint32_t idleRatio = (idleDelta * 100) / tickDelta;
printf("유휴 비율: %lu%%\n", idleRatio);
printf("활성 비율: %lu%%\n", 100 - idleRatio);
}
lastIdle = currentIdle;
lastTick = currentTick;
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
int main(void) {
printf("=== Idle Hook 활용 시스템 ===\n\n");
/* 작업 부하가 다른 여러 Task 생성 */
xTaskCreate(vWorkerTask, "Worker1", 256, (void*)1, 2, NULL);
xTaskCreate(vWorkerTask, "Worker2", 256, (void*)2, 2, NULL);
xTaskCreate(vWorkerTask, "Worker3", 256, (void*)3, 2, NULL);
xTaskCreate(vStatisticsTask, "Stats", 512, NULL, 1, NULL);
vTaskStartScheduler();
while(1);
}
개념:
CPU 사용률 = (1 - Idle 시간 / 전체 시간) × 100%
간단한 측정 방법:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
typedef struct {
uint32_t idleCount;
uint32_t totalCount;
uint32_t cpuUsage;
} CpuStats;
volatile CpuStats cpuStats = {0};
void vApplicationIdleHook(void) {
cpuStats.idleCount++;
}
void vApplicationTickHook(void) {
cpuStats.totalCount++;
}
void calculate_cpu_usage(void) {
static uint32_t lastIdle = 0;
static uint32_t lastTotal = 0;
uint32_t currentIdle = cpuStats.idleCount;
uint32_t currentTotal = cpuStats.totalCount;
uint32_t idleDelta = currentIdle - lastIdle;
uint32_t totalDelta = currentTotal - lastTotal;
if(totalDelta > 0) {
/* CPU 사용률 계산 */
cpuStats.cpuUsage = 100 - ((idleDelta * 100) / totalDelta);
}
lastIdle = currentIdle;
lastTotal = currentTotal;
}
void vCpuMonitorTask(void *pvParameters) {
while(1) {
calculate_cpu_usage();
printf("[CPU 모니터] 사용률: %lu%%\n", cpuStats.cpuUsage);
if(cpuStats.cpuUsage > 80) {
printf(" 경고: 높은 CPU 사용률!\n");
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
FreeRTOS 런타임 통계 활성화:
/* FreeRTOSConfig.h */
#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
/* 런타임 카운터 설정 */
extern volatile uint32_t ulHighFrequencyTimerTicks;
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() \
(ulHighFrequencyTimerTicks = 0)
#define portGET_RUN_TIME_COUNTER_VALUE() \
ulHighFrequencyTimerTicks
런타임 통계 구현:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include <string.h>
/* 고주파 타이머 */
volatile uint32_t ulHighFrequencyTimerTicks = 0;
/* Tick Hook에서 고주파 카운터 증가 */
void vApplicationTickHook(void) {
ulHighFrequencyTimerTicks += 10; /* Tick보다 10배 빠른 카운터 */
}
void print_task_statistics(void) {
TaskStatus_t *pxTaskStatusArray;
volatile UBaseType_t uxArraySize, x;
uint32_t ulTotalRunTime, ulStatsAsPercentage;
/* Task 개수 확인 */
uxArraySize = uxTaskGetNumberOfTasks();
/* Task 정보를 저장할 배열 할당 */
pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
if(pxTaskStatusArray != NULL) {
/* Task 상태 정보 가져오기 */
uxArraySize = uxTaskGetSystemState(pxTaskStatusArray,
uxArraySize,
&ulTotalRunTime);
printf("\n=== Task 런타임 통계 ===\n");
printf("%-15s %10s %10s\n", "Task 이름", "실행 시간", "CPU 사용률");
printf("----------------------------------------\n");
/* 전체 실행 시간이 0이면 나누기 오류 방지 */
if(ulTotalRunTime > 0) {
for(x = 0; x < uxArraySize; x++) {
/* CPU 사용률 계산 (백분율) */
ulStatsAsPercentage = (pxTaskStatusArray[x].ulRunTimeCounter * 100)
/ ulTotalRunTime;
printf("%-15s %10lu %9lu%%\n",
pxTaskStatusArray[x].pcTaskName,
pxTaskStatusArray[x].ulRunTimeCounter,
ulStatsAsPercentage);
}
}
printf("총 실행 시간: %lu\n", ulTotalRunTime);
vPortFree(pxTaskStatusArray);
}
}
void vHighLoadTask(void *pvParameters) {
while(1) {
/* CPU 집약적인 작업 시뮬레이션 */
for(volatile int i = 0; i < 500000; i++);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void vMediumLoadTask(void *pvParameters) {
while(1) {
for(volatile int i = 0; i < 200000; i++);
vTaskDelay(pdMS_TO_TICKS(200));
}
}
void vLowLoadTask(void *pvParameters) {
while(1) {
for(volatile int i = 0; i < 50000; i++);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void vStatisticsTask(void *pvParameters) {
while(1) {
print_task_statistics();
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
int main(void) {
printf("=== Task별 CPU 사용률 측정 ===\n");
xTaskCreate(vHighLoadTask, "HighLoad", 256, NULL, 3, NULL);
xTaskCreate(vMediumLoadTask, "MediumLoad", 256, NULL, 2, NULL);
xTaskCreate(vLowLoadTask, "LowLoad", 256, NULL, 1, NULL);
xTaskCreate(vStatisticsTask, "Stats", 512, NULL, 1, NULL);
vTaskStartScheduler();
while(1);
}
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
void print_stack_usage(void) {
TaskStatus_t *pxTaskStatusArray;
UBaseType_t uxArraySize, x;
uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
if(pxTaskStatusArray != NULL) {
uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, NULL);
printf("\n=== Task 스택 사용량 ===\n");
printf("%-15s %15s %15s\n", "Task 이름", "여유 스택(words)", "상태");
printf("--------------------------------------------------\n");
for(x = 0; x < uxArraySize; x++) {
UBaseType_t stackHighWaterMark =
uxTaskGetStackHighWaterMark(pxTaskStatusArray[x].xHandle);
const char *status;
if(stackHighWaterMark < 50) {
status = "위험";
} else if(stackHighWaterMark < 100) {
status = "경고";
} else {
status = "정상";
}
printf("%-15s %15u %15s\n",
pxTaskStatusArray[x].pcTaskName,
stackHighWaterMark,
status);
}
vPortFree(pxTaskStatusArray);
}
}
void vStackMonitorTask(void *pvParameters) {
while(1) {
print_stack_usage();
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include <string.h>
typedef struct {
uint32_t totalHeap;
uint32_t freeHeap;
uint32_t minEverFreeHeap;
uint32_t usedHeap;
uint32_t taskCount;
uint32_t cpuUsage;
} SystemStatus;
SystemStatus sysStatus = {0};
void update_system_status(void) {
/* 힙 메모리 상태 */
sysStatus.totalHeap = configTOTAL_HEAP_SIZE;
sysStatus.freeHeap = xPortGetFreeHeapSize();
sysStatus.minEverFreeHeap = xPortGetMinimumEverFreeHeapSize();
sysStatus.usedHeap = sysStatus.totalHeap - sysStatus.freeHeap;
/* Task 개수 */
sysStatus.taskCount = uxTaskGetNumberOfTasks();
}
void print_system_status(void) {
update_system_status();
printf("\n");
printf("╔══════════════════════════════════════════════╗\n");
printf("║ 시스템 상태 모니터링 ║\n");
printf("╠══════════════════════════════════════════════╣\n");
printf("║ 메모리 상태: ║\n");
printf("║ 전체 힙: %8lu bytes ║\n", sysStatus.totalHeap);
printf("║ 사용 중: %8lu bytes ║\n", sysStatus.usedHeap);
printf("║ 여유 힙: %8lu bytes ║\n", sysStatus.freeHeap);
printf("║ 최소 여유: %8lu bytes ║\n", sysStatus.minEverFreeHeap);
uint32_t heapUsagePercent = (sysStatus.usedHeap * 100) / sysStatus.totalHeap;
printf("║ 사용률: %8lu%% ║\n", heapUsagePercent);
printf("╠══════════════════════════════════════════════╣\n");
printf("║ Task 정보: ║\n");
printf("║ 총 Task 수: %8lu ║\n", sysStatus.taskCount);
printf("╚══════════════════════════════════════════════╝\n");
}
void print_detailed_task_info(void) {
TaskStatus_t *pxTaskStatusArray;
UBaseType_t uxArraySize, x;
uint32_t ulTotalRunTime;
uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
if(pxTaskStatusArray != NULL) {
uxArraySize = uxTaskGetSystemState(pxTaskStatusArray,
uxArraySize,
&ulTotalRunTime);
printf("\n상세 Task 정보:\n");
printf("%-15s %8s %10s %12s %10s\n",
"이름", "우선순위", "상태", "여유스택", "CPU%%");
printf("----------------------------------------------------------------\n");
for(x = 0; x < uxArraySize; x++) {
const char *state;
switch(pxTaskStatusArray[x].eCurrentState) {
case eRunning: state = "Running"; break;
case eReady: state = "Ready"; break;
case eBlocked: state = "Blocked"; break;
case eSuspended: state = "Suspended"; break;
case eDeleted: state = "Deleted"; break;
default: state = "Unknown"; break;
}
UBaseType_t stackFree =
uxTaskGetStackHighWaterMark(pxTaskStatusArray[x].xHandle);
uint32_t cpuPercent = 0;
if(ulTotalRunTime > 0) {
cpuPercent = (pxTaskStatusArray[x].ulRunTimeCounter * 100)
/ ulTotalRunTime;
}
printf("%-15s %8lu %10s %12u %9lu%%\n",
pxTaskStatusArray[x].pcTaskName,
pxTaskStatusArray[x].uxCurrentPriority,
state,
stackFree,
cpuPercent);
}
vPortFree(pxTaskStatusArray);
}
}
void vSystemMonitorTask(void *pvParameters) {
while(1) {
print_system_status();
print_detailed_task_info();
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
typedef struct {
const char *name;
uint32_t iterations;
TickType_t startTime;
TickType_t endTime;
uint32_t duration;
} BenchmarkResult;
void run_benchmark(BenchmarkResult *result, void (*testFunc)(void), uint32_t iterations) {
result->iterations = iterations;
result->startTime = xTaskGetTickCount();
for(uint32_t i = 0; i < iterations; i++) {
testFunc();
}
result->endTime = xTaskGetTickCount();
result->duration = result->endTime - result->startTime;
}
/* 콜백 함수들 */
void test_integer_math(void) {
volatile int result = 0;
for(int i = 0; i < 1000; i++) {
result += i * 2;
}
}
void test_memory_access(void) {
volatile uint8_t buffer[100];
for(int i = 0; i < 100; i++) {
buffer[i] = i;
}
}
void test_context_switch(void) {
taskYIELD();
}
/* 벤치마크 테스트 실행 결과 확인 */
void print_benchmark_results(BenchmarkResult *results, int count) {
printf("\n=== 성능 벤치마크 결과 ===\n");
printf("%-20s %12s %15s\n", "테스트", "반복 횟수", "소요 시간(ms)");
printf("--------------------------------------------------\n");
for(int i = 0; i < count; i++) {
printf("%-20s %12lu %15lu\n",
results[i].name,
results[i].iterations,
results[i].duration);
}
}
void vBenchmarkTask(void *pvParameters) {
BenchmarkResult results[3];
printf("\n벤치마크 시작...\n");
results[0].name = "정수 연산";
run_benchmark(&results[0], test_integer_math, 100);
results[1].name = "메모리 접근";
run_benchmark(&results[1], test_memory_access, 1000);
results[2].name = "컨텍스트 스위칭";
run_benchmark(&results[2], test_context_switch, 100);
print_benchmark_results(results, 3);
printf("\n벤치마크 완료\n");
vTaskDelete(NULL);
}
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
typedef enum {
ERROR_NONE = 0,
ERROR_STACK_OVERFLOW,
ERROR_HEAP_EXHAUSTED,
ERROR_TASK_STARVATION,
ERROR_CPU_OVERLOAD
} ErrorCode;
typedef struct {
ErrorCode code;
const char *taskName;
TickType_t timestamp;
const char *description;
} SystemError;
#define MAX_ERROR_LOG 10
SystemError errorLog[MAX_ERROR_LOG];
int errorCount = 0;
void log_error(ErrorCode code, const char *taskName, const char *description) {
if(errorCount < MAX_ERROR_LOG) {
errorLog[errorCount].code = code;
errorLog[errorCount].taskName = taskName;
errorLog[errorCount].timestamp = xTaskGetTickCount();
errorLog[errorCount].description = description;
errorCount++;
printf("\n[오류 발생!] %s - %s\n", taskName, description);
}
}
void check_system_health(void) {
/* 힙 메모리 체크 */
size_t freeHeap = xPortGetFreeHeapSize();
if(freeHeap < 1024) {
log_error(ERROR_HEAP_EXHAUSTED, "System", "힙 메모리 부족");
}
/* CPU 사용률 체크 */
static uint32_t lastIdle = 0;
static uint32_t lastTotal = 0;
extern volatile uint32_t idleExecutions;
extern volatile uint32_t tickCount;
uint32_t idleDelta = idleExecutions - lastIdle;
uint32_t totalDelta = tickCount - lastTotal;
if(totalDelta > 0) {
uint32_t cpuUsage = 100 - ((idleDelta * 100) / totalDelta);
if(cpuUsage > 90) {
log_error(ERROR_CPU_OVERLOAD, "System", "CPU 과부하");
}
}
lastIdle = idleExecutions;
lastTotal = tickCount;
/* Task 스택 체크 */
TaskStatus_t *pxTaskStatusArray;
UBaseType_t uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
if(pxTaskStatusArray != NULL) {
uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, NULL);
for(UBaseType_t x = 0; x < uxArraySize; x++) {
UBaseType_t stackFree =
uxTaskGetStackHighWaterMark(pxTaskStatusArray[x].xHandle);
if(stackFree < 50) {
log_error(ERROR_STACK_OVERFLOW,
pxTaskStatusArray[x].pcTaskName,
"스택 부족");
}
}
vPortFree(pxTaskStatusArray);
}
}
void print_error_log(void) {
if(errorCount == 0) {
printf("\n기록된 오류 없음\n");
return;
}
printf("\n=== 오류 로그 ===\n");
printf("%-10s %-15s %-30s %s\n", "시간(ms)", "Task", "설명", "코드");
printf("----------------------------------------------------------------\n");
for(int i = 0; i < errorCount; i++) {
printf("%-10lu %-15s %-30s %d\n",
errorLog[i].timestamp,
errorLog[i].taskName,
errorLog[i].description,
errorLog[i].code);
}
}
void vHealthCheckTask(void *pvParameters) {
while(1) {
check_system_health();
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
void vErrorReportTask(void *pvParameters) {
while(1) {
print_error_log();
vTaskDelay(pdMS_TO_TICKS(10000));
}
}
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include <stdio.h>
#include <string.h>
volatile uint32_t ulHighFrequencyTimerTicks = 0;
volatile uint32_t idleExecutions = 0;
volatile uint32_t tickCount = 0;
void vApplicationIdleHook(void) {
idleExecutions++;
}
void vApplicationTickHook(void) {
tickCount++;
ulHighFrequencyTimerTicks += 10;
}
typedef struct {
char name[16];
uint32_t executionCount;
uint32_t maxExecutionTime;
uint32_t minExecutionTime;
uint32_t avgExecutionTime;
TickType_t lastExecutionTime;
} TaskMetrics;
TaskMetrics taskMetrics[5];
int metricsCount = 0;
void update_task_metrics(const char *name, uint32_t executionTime) {
for(int i = 0; i < metricsCount; i++) {
if(strcmp(taskMetrics[i].name, name) == 0) {
taskMetrics[i].executionCount++;
taskMetrics[i].lastExecutionTime = executionTime;
if(executionTime > taskMetrics[i].maxExecutionTime) {
taskMetrics[i].maxExecutionTime = executionTime;
}
if(executionTime < taskMetrics[i].minExecutionTime ||
taskMetrics[i].minExecutionTime == 0) {
taskMetrics[i].minExecutionTime = executionTime;
}
taskMetrics[i].avgExecutionTime =
(taskMetrics[i].avgExecutionTime * (taskMetrics[i].executionCount - 1) +
executionTime) / taskMetrics[i].executionCount;
return;
}
}
if(metricsCount < 5) {
strcpy(taskMetrics[metricsCount].name, name);
taskMetrics[metricsCount].executionCount = 1;
taskMetrics[metricsCount].maxExecutionTime = executionTime;
taskMetrics[metricsCount].minExecutionTime = executionTime;
taskMetrics[metricsCount].avgExecutionTime = executionTime;
taskMetrics[metricsCount].lastExecutionTime = executionTime;
metricsCount++;
}
}
void print_task_metrics(void) {
printf("\n=== Task 실행 메트릭 ===\n");
printf("%-12s %8s %8s %8s %8s\n",
"Task", "실행횟수", "최소(ms)", "평균(ms)", "최대(ms)");
printf("--------------------------------------------------------\n");
for(int i = 0; i < metricsCount; i++) {
printf("%-12s %8lu %8lu %8lu %8lu\n",
taskMetrics[i].name,
taskMetrics[i].executionCount,
taskMetrics[i].minExecutionTime,
taskMetrics[i].avgExecutionTime,
taskMetrics[i].maxExecutionTime);
}
}
void vMonitoredTask(void *pvParameters) {
const char *name = (const char*)pvParameters;
int workload = (name[4] - '0') * 50000;
while(1) {
TickType_t startTime = xTaskGetTickCount();
for(volatile int i = 0; i < workload; i++);
TickType_t endTime = xTaskGetTickCount();
uint32_t executionTime = endTime - startTime;
update_task_metrics(name, executionTime);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void vDashboardTask(void *pvParameters) {
while(1) {
printf("\033[2J\033[H");
printf("╔═══════════════════════════════════════════════════╗\n");
printf("║ FreeRTOS 시스템 모니터링 대시보드 ║\n");
printf("╠═══════════════════════════════════════════════════╣\n");
size_t freeHeap = xPortGetFreeHeapSize();
size_t minHeap = xPortGetMinimumEverFreeHeapSize();
uint32_t taskCount = uxTaskGetNumberOfTasks();
printf("║ 메모리: %lu / %lu bytes (최소: %lu) ║\n",
configTOTAL_HEAP_SIZE - freeHeap,
(uint32_t)configTOTAL_HEAP_SIZE,
minHeap);
printf("║ Task 수: %lu ║\n", taskCount);
static uint32_t lastIdle = 0;
static uint32_t lastTotal = 0;
uint32_t idleDelta = idleExecutions - lastIdle;
uint32_t totalDelta = tickCount - lastTotal;
if(totalDelta > 0) {
uint32_t cpuUsage = 100 - ((idleDelta * 100) / totalDelta);
printf("║ CPU 사용률: %lu%% ║\n", cpuUsage);
printf("║ CPU: [");
for(int i = 0; i < 30; i++) {
if(i < (cpuUsage * 30 / 100)) {
printf("█");
} else {
printf(" ");
}
}
printf("] ║\n");
}
lastIdle = idleExecutions;
lastTotal = tickCount;
printf("╚═══════════════════════════════════════════════════╝\n");
print_task_metrics();
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
int main(void) {
xTaskCreate(vMonitoredTask, "Task1", 256, (void*)"Task1", 2, NULL);
xTaskCreate(vMonitoredTask, "Task2", 256, (void*)"Task2", 2, NULL);
xTaskCreate(vMonitoredTask, "Task3", 256, (void*)"Task3", 2, NULL);
xTaskCreate(vDashboardTask, "Dashboard", 512, NULL, 1, NULL);
vTaskStartScheduler();
while(1);
}
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include <string.h>
#define MAX_PROFILING_POINTS 20
typedef struct {
const char *name;
TickType_t startTime;
TickType_t totalTime;
uint32_t callCount;
} ProfilingPoint;
ProfilingPoint profilingPoints[MAX_PROFILING_POINTS];
int profilingCount = 0;
void profiling_start(const char *name) {
for(int i = 0; i < profilingCount; i++) {
if(strcmp(profilingPoints[i].name, name) == 0) {
profilingPoints[i].startTime = xTaskGetTickCount();
return;
}
}
if(profilingCount < MAX_PROFILING_POINTS) {
profilingPoints[profilingCount].name = name;
profilingPoints[profilingCount].startTime = xTaskGetTickCount();
profilingPoints[profilingCount].totalTime = 0;
profilingPoints[profilingCount].callCount = 0;
profilingCount++;
}
}
void profiling_end(const char *name) {
TickType_t endTime = xTaskGetTickCount();
for(int i = 0; i < profilingCount; i++) {
if(strcmp(profilingPoints[i].name, name) == 0) {
TickType_t elapsed = endTime - profilingPoints[i].startTime;
profilingPoints[i].totalTime += elapsed;
profilingPoints[i].callCount++;
return;
}
}
}
void print_profiling_results(void) {
printf("\n=== 성능 프로파일링 결과 ===\n");
printf("%-20s %12s %15s %15s\n",
"함수/구간", "호출 횟수", "총 시간(ms)", "평균 시간(ms)");
printf("----------------------------------------------------------------\n");
for(int i = 0; i < profilingCount; i++) {
uint32_t avgTime = 0;
if(profilingPoints[i].callCount > 0) {
avgTime = profilingPoints[i].totalTime / profilingPoints[i].callCount;
}
printf("%-20s %12lu %15lu %15lu\n",
profilingPoints[i].name,
profilingPoints[i].callCount,
profilingPoints[i].totalTime,
avgTime);
}
}
void heavy_computation(void) {
profiling_start("heavy_computation");
volatile uint32_t result = 0;
for(int i = 0; i < 1000000; i++) {
result += i;
}
profiling_end("heavy_computation");
}
void data_processing(void) {
profiling_start("data_processing");
uint8_t buffer[256];
for(int i = 0; i < 256; i++) {
buffer[i] = i;
}
profiling_end("data_processing");
}
void vWorkerTask(void *pvParameters) {
while(1) {
profiling_start("worker_iteration");
heavy_computation();
data_processing();
profiling_end("worker_iteration");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void vProfilerTask(void *pvParameters) {
vTaskDelay(pdMS_TO_TICKS(5000));
while(1) {
print_profiling_results();
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
int main(void) {
printf("=== 성능 프로파일러 ===\n\n");
xTaskCreate(vWorkerTask, "Worker", 512, NULL, 2, NULL);
xTaskCreate(vProfilerTask, "Profiler", 512, NULL, 1, NULL);
vTaskStartScheduler();
while(1);
}
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include <stdio.h>
#include <string.h>
typedef enum {
EVENT_TASK_CREATED,
EVENT_TASK_DELETED,
EVENT_TASK_SWITCHED_IN,
EVENT_TASK_SWITCHED_OUT,
EVENT_ERROR,
EVENT_WARNING,
EVENT_INFO
} EventType;
typedef struct {
EventType type;
TickType_t timestamp;
char taskName[16];
char message[64];
} SystemEvent;
QueueHandle_t eventQueue;
void log_event(EventType type, const char *taskName, const char *message) {
SystemEvent event;
event.type = type;
event.timestamp = xTaskGetTickCount();
strncpy(event.taskName, taskName ? taskName : "System", 15);
strncpy(event.message, message, 63);
xQueueSend(eventQueue, &event, 0);
}
const char* get_event_type_string(EventType type) {
switch(type) {
case EVENT_TASK_CREATED: return "TASK_CREATE";
case EVENT_TASK_DELETED: return "TASK_DELETE";
case EVENT_TASK_SWITCHED_IN: return "SWITCH_IN";
case EVENT_TASK_SWITCHED_OUT: return "SWITCH_OUT";
case EVENT_ERROR: return "ERROR";
case EVENT_WARNING: return "WARNING";
case EVENT_INFO: return "INFO";
default: return "UNKNOWN";
}
}
void vEventLoggerTask(void *pvParameters) {
SystemEvent event;
while(1) {
if(xQueueReceive(eventQueue, &event, portMAX_DELAY) == pdTRUE) {
printf("[%8lu] %-12s %-15s %s\n",
event.timestamp,
get_event_type_string(event.type),
event.taskName,
event.message);
}
}
}
void vTestTask(void *pvParameters) {
int taskId = (int)pvParameters;
char msg[64];
snprintf(msg, sizeof(msg), "Task%d started", taskId);
log_event(EVENT_INFO, pcTaskGetName(NULL), msg);
for(int i = 0; i < 5; i++) {
snprintf(msg, sizeof(msg), "Working iteration %d", i);
log_event(EVENT_INFO, pcTaskGetName(NULL), msg);
vTaskDelay(pdMS_TO_TICKS(1000));
}
snprintf(msg, sizeof(msg), "Task%d completed", taskId);
log_event(EVENT_INFO, pcTaskGetName(NULL), msg);
vTaskDelete(NULL);
}
void vSpawnerTask(void *pvParameters) {
for(int i = 1; i <= 3; i++) {
char taskName[16];
snprintf(taskName, sizeof(taskName), "Test%d", i);
TaskHandle_t handle;
BaseType_t result = xTaskCreate(vTestTask, taskName, 256, (void*)i, 2, &handle);
if(result == pdPASS) {
log_event(EVENT_TASK_CREATED, taskName, "Task created successfully");
} else {
log_event(EVENT_ERROR, taskName, "Failed to create task");
}
vTaskDelay(pdMS_TO_TICKS(2000));
}
vTaskDelete(NULL);
}
int main(void) {
printf("=== 시스템 이벤트 로거 ===\n");
printf("[시간(ms)] [이벤트 타입] [Task 이름] [메시지]\n");
printf("----------------------------------------------------------------\n");
eventQueue = xQueueCreate(50, sizeof(SystemEvent));
xTaskCreate(vEventLoggerTask, "Logger", 512, NULL, 1, NULL);
xTaskCreate(vSpawnerTask, "Spawner", 512, NULL, 3, NULL);
vTaskStartScheduler();
while(1);
}
해야 할 것:
하지 말아야 할 것:
/* 통계 수집은 저우선순위 Task에서 수행 */
void vLowPriorityMonitorTask(void *pvParameters) {
while(1) {
collect_statistics();
vTaskDelay(pdMS_TO_TICKS(5000)); /* 자주 수행하지 않음 */
}
}
/* 조건부 로깅으로 오버헤드 감소 */
#define LOGGING_ENABLED 1
void conditional_log(const char *message) {
#if LOGGING_ENABLED
log_event(EVENT_INFO, pcTaskGetName(NULL), message);
#endif
}
/* 동적 할당 대신 정적 버퍼 사용 */
#define MAX_TASKS 10
static TaskStatus_t taskStatusArray[MAX_TASKS];
void collect_task_stats(void) {
UBaseType_t taskCount = uxTaskGetSystemState(taskStatusArray,
MAX_TASKS,
NULL);
/* 할당 없이 통계 수집 */
}
Idle Task
vApplicationIdleHook()
CPU 사용률 측정
시스템 모니터링
| 개념 | 설명 |
|---|---|
| Idle Task | 다른 Task가 없을 때 실행되는 우선순위 0 Task |
| Idle Hook | Idle Task에서 호출되는 사용자 정의 함수 |
| 런타임 통계 | Task별 CPU 사용 시간 측정 기능 |
| High Water Mark | Task 스택의 최대 여유 공간 |
| CPU 사용률 | (1 - Idle 시간 / 전체 시간) × 100% |
Task별 CPU 사용률, 메모리 사용량, 실행 횟수를 실시간으로 표시하는 모니터링 시스템 구현
요구사항:
시스템 부하를 감지하고 자동으로 Task 우선순위를 조정하는 시스템
요구사항:
주기적으로 시스템 상태를 점검하고 문제를 보고하는 진단 도구
요구사항:
/* 잘못된 코드 */
void vApplicationIdleHook(void) {
vTaskDelay(pdMS_TO_TICKS(100)); /* 절대 금지! */
}
/* 올바른 코드 */
void vApplicationIdleHook(void) {
/* 빠른 작업만 수행 */
update_watchdog();
idle_counter++;
}
/* 통계 수집 빈도 조절 */
void vMonitorTask(void *pvParameters) {
while(1) {
collect_statistics();
vTaskDelay(pdMS_TO_TICKS(5000)); /* 5초마다만 수집 */
}
}
/* 동적 할당 대신 정적 버퍼 사용 */
static TaskStatus_t taskBuffer[10];
void get_task_info(void) {
uxTaskGetSystemState(taskBuffer, 10, NULL);
/* 할당/해제 없이 사용 */
}