Week 2 Day 1: Task 생성과 관리

학습 목표

  • xTaskCreate()와 xTaskCreateStatic()의 차이를 이해한다
  • Task 삭제 방법과 주의사항을 파악한다
  • Task 일시정지/재개 기능을 활용할 수 있다
  • Task 우선순위를 동적으로 변경할 수 있다
  • Task Stack 사용량을 모니터링하고 최적화할 수 있다

1. Task 생성 방식 비교

1.1 동적 생성 vs 정적 생성

생성 방식함수메모리 할당사용 시기장점단점
동적 생성xTaskCreate()Heap에서 자동 할당일반적인 경우사용이 간편함Heap 메모리 필요
정적 생성xTaskCreateStatic()사용자가 미리 할당안전 중요 시스템메모리 위치 제어 가능코드가 복잡함

동적 생성은 편리하지만 Heap 단편화 위험이 있습니다. 안전 중요 시스템에서는 정적 생성을 권장합니다.

1.2 xTaskCreate() - 동적 생성

함수 원형:

BaseType_t xTaskCreate(
    TaskFunction_t pvTaskCode,      // Task 함수 포인터
    const char * const pcName,      // Task 이름 (디버깅용)
    uint16_t usStackDepth,          // Stack 크기 (words 단위)
    void *pvParameters,             // Task에 전달할 파라미터
    UBaseType_t uxPriority,         // 우선순위
    TaskHandle_t *pvCreatedTask     // Task 핸들 저장 (NULL 가능)
);

C 버전:

#include "FreeRTOS.h"
#include "task.h"

void vTaskExample(void *pvParameters) {
    int count = 0;
    
    while(1) {
        printf("Task running: %d\n", count++);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

int main(void) {
    TaskHandle_t xTaskHandle = NULL;
    BaseType_t xReturned;
    
    // Task 동적 생성
    xReturned = xTaskCreate(
        vTaskExample,           // Task 함수
        "Example",              // Task 이름
        256,                    // Stack: 256 words = 1024 bytes
        NULL,                   // 파라미터 없음
        2,                      // 우선순위 2
        &xTaskHandle            // 핸들 저장
    );
    
    if(xReturned == pdPASS) {
        printf("Task 생성 성공\n");
    } else {
        printf("Task 생성 실패 (메모리 부족)\n");
        return 1;
    }
    
    vTaskStartScheduler();
    
    while(1);
}

C++ 버전:

#include "FreeRTOS.h"
#include "task.h"
#include <iostream>

class Task {
protected:
    TaskHandle_t taskHandle;
    const char* taskName;
    uint16_t stackSize;
    UBaseType_t priority;
    
    virtual void run() = 0;
    
    static void taskEntry(void* pvParameters) {
        Task* task = static_cast<Task*>(pvParameters);
        task->run();
    }
    
public:
    Task(const char* name, uint16_t stack, UBaseType_t prio)
        : taskName(name), stackSize(stack), priority(prio), taskHandle(nullptr) {}
    
    bool create() {
        BaseType_t xReturned = xTaskCreate(
            taskEntry,
            taskName,
            stackSize,
            this,
            priority,
            &taskHandle
        );
        
        if(xReturned == pdPASS) {
            std::cout << "Task 생성 성공: " << taskName << std::endl;
            return true;
        } else {
            std::cout << "Task 생성 실패: " << taskName << std::endl;
            return false;
        }
    }
    
    TaskHandle_t getHandle() const { return taskHandle; }
    
    virtual ~Task() {}
};

class ExampleTask : public Task {
private:
    int count;
    
public:
    ExampleTask() : Task("Example", 256, 2), count(0) {}
    
    void run() override {
        while(1) {
            std::cout << "Task running: " << count++ << std::endl;
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
};

int main(void) {
    ExampleTask task;
    
    if(task.create()) {
        vTaskStartScheduler();
    } else {
        return 1;
    }
    
    while(1);
}

1.3 xTaskCreateStatic() - 정적 생성

함수 원형:

TaskHandle_t xTaskCreateStatic(
    TaskFunction_t pxTaskCode,
    const char * const pcName,
    uint32_t ulStackDepth,
    void * pvParameters,
    UBaseType_t uxPriority,
    StackType_t * const puxStackBuffer,     // 미리 할당된 Stack 버퍼
    StaticTask_t * const pxTaskBuffer       // 미리 할당된 TCB 버퍼
);

정적 생성은 메모리를 사용자가 직접 관리하므로 Heap 단편화 없이 안전하게 Task를 생성할 수 있습니다.

C 버전:

#include "FreeRTOS.h"
#include "task.h"

// FreeRTOSConfig.h에 추가 필요
// #define configSUPPORT_STATIC_ALLOCATION 1

void vStaticTask(void *pvParameters) {
    int count = 0;
    
    while(1) {
        printf("Static Task: %d\n", count++);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

int main(void) {
    // Task를 위한 Stack 버퍼 (정적 할당)
    static StackType_t xTaskStack[256];
    
    // Task Control Block 버퍼 (정적 할당)
    static StaticTask_t xTaskBuffer;
    
    TaskHandle_t xHandle;
    
    // 정적 Task 생성
    xHandle = xTaskCreateStatic(
        vStaticTask,            // Task 함수
        "Static",               // Task 이름
        256,                    // Stack 크기
        NULL,                   // 파라미터
        2,                      // 우선순위
        xTaskStack,             // Stack 버퍼
        &xTaskBuffer            // TCB 버퍼
    );
    
    if(xHandle == NULL) {
        printf("정적 Task 생성 실패\n");
        return 1;
    }
    
    printf("정적 Task 생성 성공\n");
    
    vTaskStartScheduler();
    
    while(1);
}

C++ 버전:

#include "FreeRTOS.h"
#include "task.h"
#include <iostream>

class StaticTask : public Task {
private:
    StackType_t taskStack[256];
    StaticTask_t taskBuffer;
    int count;
    
public:
    StaticTask() : Task("Static", 256, 2), count(0) {}
    
    bool createStatic() {
        taskHandle = xTaskCreateStatic(
            taskEntry,
            taskName,
            stackSize,
            this,
            priority,
            taskStack,
            &taskBuffer
        );
        
        if(taskHandle == nullptr) {
            std::cout << "정적 Task 생성 실패" << std::endl;
            return false;
        }
        
        std::cout << "정적 Task 생성 성공" << std::endl;
        return true;
    }
    
    void run() override {
        while(1) {
            std::cout << "Static Task: " << count++ << std::endl;
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
};

int main(void) {
    StaticTask task;
    
    if(task.createStatic()) {
        vTaskStartScheduler();
    } else {
        return 1;
    }
    
    while(1);
}

1.4 동적 vs 정적 메모리 사용량 비교

C 버전:

#include "FreeRTOS.h"
#include "task.h"

void check_heap_status(void) {
    size_t freeHeap = xPortGetFreeHeapSize();
    size_t minFreeHeap = xPortGetMinimumEverFreeHeapSize();
    
    printf("현재 Free Heap: %zu bytes\n", freeHeap);
    printf("최소 Free Heap: %zu bytes\n", minFreeHeap);
}

void vDynamicTask(void *pvParameters) {
    while(1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void vStaticTask(void *pvParameters) {
    while(1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

int main(void) {
    printf("=== 초기 상태 ===\n");
    check_heap_status();
    
    // 동적 Task 생성
    printf("\n=== 동적 Task 생성 ===\n");
    xTaskCreate(vDynamicTask, "Dynamic", 256, NULL, 2, NULL);
    check_heap_status();
    
    // 정적 Task 생성
    printf("\n=== 정적 Task 생성 ===\n");
    static StackType_t xStack[256];
    static StaticTask_t xTCB;
    xTaskCreateStatic(vStaticTask, "Static", 256, NULL, 2, xStack, &xTCB);
    check_heap_status();  // Heap 사용량 변화 없음
    
    vTaskStartScheduler();
    while(1);
}

출력 예시:

=== 초기 상태 ===
현재 Free Heap: 15360 bytes
최소 Free Heap: 15360 bytes

=== 동적 Task 생성 ===
현재 Free Heap: 14208 bytes  (1152 bytes 감소)
최소 Free Heap: 14208 bytes

=== 정적 Task 생성 ===
현재 Free Heap: 14208 bytes  (변화 없음)
최소 Free Heap: 14208 bytes

2. Task 삭제

2.1 vTaskDelete() 사용법

함수 원형:

void vTaskDelete(TaskHandle_t xTaskToDelete);

파라미터:

  • NULL: 자기 자신 삭제
  • TaskHandle: 다른 Task 삭제

Task를 삭제하면 해당 Task의 Stack과 TCB 메모리가 Idle Task에 의해 해제됩니다.

C 버전:

#include "FreeRTOS.h"
#include "task.h"

// 자기 자신을 삭제하는 Task
void vSelfDeleteTask(void *pvParameters) {
    int count = 0;
    
    while(1) {
        printf("Self Delete Task: %d\n", count++);
        vTaskDelay(pdMS_TO_TICKS(1000));
        
        if(count >= 5) {
            printf("Task 자기 자신 삭제\n");
            vTaskDelete(NULL);  // NULL = 자기 자신
        }
    }
    
    // 이 코드는 실행되지 않음
    printf("이 메시지는 출력되지 않음\n");
}

// 다른 Task를 삭제하는 Task
TaskHandle_t xWorkerHandle = NULL;

void vWorkerTask(void *pvParameters) {
    while(1) {
        printf("Worker Task 실행 중...\n");
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

void vControlTask(void *pvParameters) {
    int count = 0;
    
    while(1) {
        printf("Control Task: %d\n", count++);
        vTaskDelay(pdMS_TO_TICKS(1000));
        
        if(count == 10 && xWorkerHandle != NULL) {
            printf("Worker Task 삭제\n");
            vTaskDelete(xWorkerHandle);
            xWorkerHandle = NULL;
        }
    }
}

int main(void) {
    // 자기 삭제 예제
    xTaskCreate(vSelfDeleteTask, "SelfDelete", 128, NULL, 2, NULL);
    
    // 다른 Task 삭제 예제
    xTaskCreate(vWorkerTask, "Worker", 128, NULL, 2, &xWorkerHandle);
    xTaskCreate(vControlTask, "Control", 128, NULL, 3, NULL);
    
    vTaskStartScheduler();
    while(1);
}

C++ 버전:

#include "FreeRTOS.h"
#include "task.h"
#include <iostream>

class SelfDeleteTask : public Task {
private:
    int count;
    
public:
    SelfDeleteTask() : Task("SelfDelete", 128, 2), count(0) {}
    
    void run() override {
        while(1) {
            std::cout << "Self Delete Task: " << count++ << std::endl;
            vTaskDelay(pdMS_TO_TICKS(1000));
            
            if(count >= 5) {
                std::cout << "Task 자기 자신 삭제" << std::endl;
                vTaskDelete(taskHandle);
                return;  // 안전하게 종료
            }
        }
    }
};

class WorkerTask : public Task {
public:
    WorkerTask() : Task("Worker", 128, 2) {}
    
    void run() override {
        while(1) {
            std::cout << "Worker Task 실행 중..." << std::endl;
            vTaskDelay(pdMS_TO_TICKS(500));
        }
    }
};

class ControlTask : public Task {
private:
    WorkerTask& worker;
    int count;
    
public:
    ControlTask(WorkerTask& w) : Task("Control", 128, 3), worker(w), count(0) {}
    
    void run() override {
        while(1) {
            std::cout << "Control Task: " << count++ << std::endl;
            vTaskDelay(pdMS_TO_TICKS(1000));
            
            if(count == 10) {
                std::cout << "Worker Task 삭제" << std::endl;
                vTaskDelete(worker.getHandle());
            }
        }
    }
};

int main(void) {
    SelfDeleteTask selfTask;
    WorkerTask workerTask;
    ControlTask controlTask(workerTask);
    
    selfTask.create();
    workerTask.create();
    controlTask.create();
    
    vTaskStartScheduler();
    while(1);
}

2.2 Task 삭제 시 주의사항

C 버전:

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

SemaphoreHandle_t xMutex;

// 잘못된 예: Mutex를 잡고 있는 상태에서 삭제
void vBadTask(void *pvParameters) {
    while(1) {
        if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
            printf("Mutex 획득\n");
            
            // 위험! Mutex를 반환하지 않고 삭제
            vTaskDelete(NULL);
            
            // 실행되지 않음
            xSemaphoreGive(xMutex);
        }
    }
}

// 올바른 예: 리소스 정리 후 삭제
void vGoodTask(void *pvParameters) {
    int *dynamicData = (int*)pvPortMalloc(sizeof(int) * 10);
    
    while(1) {
        if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
            // 작업 수행
            
            // Mutex 반환
            xSemaphoreGive(xMutex);
        }
        
        // 종료 조건 확인
        if(should_terminate) {
            // 리소스 정리
            vPortFree(dynamicData);
            
            // Task 삭제
            vTaskDelete(NULL);
        }
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

주의사항 요약:
1. Mutex/Semaphore를 잡고 있는 상태에서 삭제 금지
2. 동적 할당한 메모리는 삭제 전에 해제
3. 다른 Task가 참조하는 핸들은 삭제 후 NULL로 설정
4. 삭제된 Task의 메모리는 Idle Task에서 회수됨


3. Task 일시정지와 재개

3.1 vTaskSuspend() / vTaskResume()

함수 원형:

void vTaskSuspend(TaskHandle_t xTaskToSuspend);
void vTaskResume(TaskHandle_t xTaskToResume);
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume);

Suspend된 Task는 스케줄러에서 제외되어 CPU 시간을 전혀 소비하지 않습니다.

C 버전:

#include "FreeRTOS.h"
#include "task.h"

TaskHandle_t xWorkerHandle;

void vWorkerTask(void *pvParameters) {
    int count = 0;
    
    while(1) {
        printf("Worker Task 실행: %d\n", count++);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

void vControlTask(void *pvParameters) {
    while(1) {
        printf("Worker Task 일시정지\n");
        vTaskSuspend(xWorkerHandle);
        
        vTaskDelay(pdMS_TO_TICKS(3000));
        
        printf("Worker Task 재개\n");
        vTaskResume(xWorkerHandle);
        
        vTaskDelay(pdMS_TO_TICKS(3000));
    }
}

int main(void) {
    xTaskCreate(vWorkerTask, "Worker", 128, NULL, 2, &xWorkerHandle);
    xTaskCreate(vControlTask, "Control", 128, NULL, 3, NULL);
    
    vTaskStartScheduler();
    while(1);
}

C++ 버전:

#include "FreeRTOS.h"
#include "task.h"
#include <iostream>

class Task {
protected:
    TaskHandle_t taskHandle;
    const char* taskName;
    uint16_t stackSize;
    UBaseType_t priority;
    
    virtual void run() = 0;
    
    static void taskEntry(void* pvParameters) {
        Task* task = static_cast<Task*>(pvParameters);
        task->run();
    }
    
public:
    Task(const char* name, uint16_t stack, UBaseType_t prio)
        : taskName(name), stackSize(stack), priority(prio), taskHandle(nullptr) {}
    
    void create() {
        xTaskCreate(taskEntry, taskName, stackSize, this, priority, &taskHandle);
    }
    
    void suspend() {
        if(taskHandle != nullptr) {
            vTaskSuspend(taskHandle);
        }
    }
    
    void resume() {
        if(taskHandle != nullptr) {
            vTaskResume(taskHandle);
        }
    }
    
    TaskHandle_t getHandle() const { return taskHandle; }
    
    virtual ~Task() {}
};

class WorkerTask : public Task {
private:
    int count;
    
public:
    WorkerTask() : Task("Worker", 128, 2), count(0) {}
    
    void run() override {
        while(1) {
            std::cout << "Worker Task 실행: " << count++ << std::endl;
            vTaskDelay(pdMS_TO_TICKS(500));
        }
    }
};

class ControlTask : public Task {
private:
    WorkerTask& worker;
    
public:
    ControlTask(WorkerTask& w) : Task("Control", 128, 3), worker(w) {}
    
    void run() override {
        while(1) {
            std::cout << "Worker Task 일시정지" << std::endl;
            worker.suspend();
            
            vTaskDelay(pdMS_TO_TICKS(3000));
            
            std::cout << "Worker Task 재개" << std::endl;
            worker.resume();
            
            vTaskDelay(pdMS_TO_TICKS(3000));
        }
    }
};

int main(void) {
    WorkerTask workerTask;
    ControlTask controlTask(workerTask);
    
    workerTask.create();
    controlTask.create();
    
    vTaskStartScheduler();
    while(1);
}

3.2 자기 자신 일시정지

C 버전:

void vSelfSuspendTask(void *pvParameters) {
    int count = 0;
    
    while(1) {
        printf("Task 실행: %d\n", count++);
        vTaskDelay(pdMS_TO_TICKS(1000));
        
        if(count >= 5) {
            printf("자기 자신 일시정지\n");
            vTaskSuspend(NULL);  // NULL = 자기 자신
            
            // 다른 Task가 vTaskResume()을 호출할 때까지 대기
            printf("재개됨!\n");
        }
    }
}

실행 결과:

Task 실행: 0
Task 실행: 1
Task 실행: 2
Task 실행: 3
Task 실행: 4
자기 자신 일시정지
(다른 Task가 Resume 호출 시)
재개됨!
Task 실행: 5

4. Task 우선순위 동적 변경

4.1 vTaskPrioritySet() / uxTaskPriorityGet()

함수 원형:

void vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority);
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask);

C 버전:

#include "FreeRTOS.h"
#include "task.h"

TaskHandle_t xTask1Handle;
TaskHandle_t xTask2Handle;

void vTask1(void *pvParameters) {
    while(1) {
        printf("[Task1] 우선순위: %d\n", uxTaskPriorityGet(NULL));
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void vTask2(void *pvParameters) {
    while(1) {
        printf("[Task2] 우선순위: %d\n", uxTaskPriorityGet(NULL));
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void vControlTask(void *pvParameters) {
    int count = 0;
    
    while(1) {
        count++;
        
        if(count == 5) {
            printf(">>> Task1 우선순위 상승 (1 → 4)\n");
            vTaskPrioritySet(xTask1Handle, 4);
        }
        
        if(count == 10) {
            printf(">>> Task1 우선순위 복구 (4 → 1)\n");
            vTaskPrioritySet(xTask1Handle, 1);
        }
        
        if(count == 15) {
            printf(">>> Task2 우선순위 상승 (2 → 5)\n");
            vTaskPrioritySet(xTask2Handle, 5);
        }
        
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

int main(void) {
    xTaskCreate(vTask1, "Task1", 128, NULL, 1, &xTask1Handle);
    xTaskCreate(vTask2, "Task2", 128, NULL, 2, &xTask2Handle);
    xTaskCreate(vControlTask, "Control", 128, NULL, 3, NULL);
    
    vTaskStartScheduler();
    while(1);
}

C++ 버전:

#include "FreeRTOS.h"
#include "task.h"
#include <iostream>

class Task {
protected:
    TaskHandle_t taskHandle;
    const char* taskName;
    uint16_t stackSize;
    UBaseType_t priority;
    
    virtual void run() = 0;
    
    static void taskEntry(void* pvParameters) {
        Task* task = static_cast<Task*>(pvParameters);
        task->run();
    }
    
public:
    Task(const char* name, uint16_t stack, UBaseType_t prio)
        : taskName(name), stackSize(stack), priority(prio), taskHandle(nullptr) {}
    
    void create() {
        xTaskCreate(taskEntry, taskName, stackSize, this, priority, &taskHandle);
    }
    
    void setPriority(UBaseType_t newPriority) {
        if(taskHandle != nullptr) {
            vTaskPrioritySet(taskHandle, newPriority);
            priority = newPriority;
        }
    }
    
    UBaseType_t getPriority() const {
        if(taskHandle != nullptr) {
            return uxTaskPriorityGet(taskHandle);
        }
        return 0;
    }
    
    TaskHandle_t getHandle() const { return taskHandle; }
    
    virtual ~Task() {}
};

class WorkTask : public Task {
private:
    int taskId;
    
public:
    WorkTask(int id, const char* name, UBaseType_t prio) 
        : Task(name, 128, prio), taskId(id) {}
    
    void run() override {
        while(1) {
            std::cout << "[Task" << taskId << "] 우선순위: " 
                      << getPriority() << std::endl;
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
};

class ControlTask : public Task {
private:
    WorkTask& task1;
    WorkTask& task2;
    int count;
    
public:
    ControlTask(WorkTask& t1, WorkTask& t2) 
        : Task("Control", 128, 3), task1(t1), task2(t2), count(0) {}
    
    void run() override {
        while(1) {
            count++;
            
            if(count == 5) {
                std::cout << ">>> Task1 우선순위 상승 (1 → 4)" << std::endl;
                task1.setPriority(4);
            }
            
            if(count == 10) {
                std::cout << ">>> Task1 우선순위 복구 (4 → 1)" << std::endl;
                task1.setPriority(1);
            }
            
            if(count == 15) {
                std::cout << ">>> Task2 우선순위 상승 (2 → 5)" << std::endl;
                task2.setPriority(5);
            }
            
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
};

int main(void) {
    WorkTask task1(1, "Task1", 1);
    WorkTask task2(2, "Task2", 2);
    ControlTask controlTask(task1, task2);
    
    task1.create();
    task2.create();
    controlTask.create();
    
    vTaskStartScheduler();
    while(1);
}

4.2 우선순위 변경 시나리오

긴급 모드 전환 예제:

C 버전:

typedef enum {
    MODE_NORMAL,
    MODE_EMERGENCY
} SystemMode;

SystemMode currentMode = MODE_NORMAL;
TaskHandle_t xSensorHandle;

void vSensorTask(void *pvParameters) {
    while(1) {
        read_sensor();
        
        UBaseType_t priority = uxTaskPriorityGet(NULL);
        printf("센서 Task 우선순위: %d\n", priority);
        
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void vEmergencyDetector(void *pvParameters) {
    while(1) {
        if(emergency_detected()) {
            currentMode = MODE_EMERGENCY;
            
            // 센서 Task를 최고 우선순위로
            vTaskPrioritySet(xSensorHandle, configMAX_PRIORITIES - 1);
            printf("긴급 모드: 센서 우선순위 상승\n");
        } else if(currentMode == MODE_EMERGENCY) {
            currentMode = MODE_NORMAL;
            
            // 정상 우선순위로 복구
            vTaskPrioritySet(xSensorHandle, 2);
            printf("정상 모드: 센서 우선순위 복구\n");
        }
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

5. Task Stack 모니터링

5.1 uxTaskGetStackHighWaterMark()

함수 원형:

UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask);

High Water Mark는 Stack에서 사용되지 않은 최소 공간을 의미합니다. 값이 작을수록 Stack 오버플로 위험이 높습니다.

C 버전:

#include "FreeRTOS.h"
#include "task.h"

void vMonitoredTask(void *pvParameters) {
    char largeBuffer[512];  // 큰 Stack 사용
    
    while(1) {
        // 작업 수행
        for(int i = 0; i < 512; i++) {
            largeBuffer[i] = i % 256;
        }
        
        // Stack 사용량 확인
        UBaseType_t highWaterMark = uxTaskGetStackHighWaterMark(NULL);
        printf("Stack High Water Mark: %d words = %d bytes\n", 
               highWaterMark, highWaterMark * sizeof(StackType_t));
        
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

int main(void) {
    // Stack 크기: 256 words = 1024 bytes
    xTaskCreate(vMonitoredTask, "Monitor", 256, NULL, 2, NULL);
    
    vTaskStartScheduler();
    while(1);
}

출력 예시:

Stack High Water Mark: 120 words = 480 bytes
(사용된 Stack: 1024 - 480 = 544 bytes)

C++ 버전:

#include "FreeRTOS.h"
#include "task.h"
#include <iostream>

class MonitoredTask : public Task {
private:
    char largeBuffer[512];
    
public:
    MonitoredTask() : Task("Monitor", 256, 2) {}
    
    void run() override {
        while(1) {
            // 작업 수행
            for(int i = 0; i < 512; i++) {
                largeBuffer[i] = i % 256;
            }
            
            // Stack 사용량 확인
            UBaseType_t highWaterMark = uxTaskGetStackHighWaterMark(taskHandle);
            std::cout << "Stack High Water Mark: " << highWaterMark 
                      << " words = " << highWaterMark * sizeof(StackType_t) 
                      << " bytes" << std::endl;
            
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
};

int main(void) {
    MonitoredTask task;
    task.create();
    
    vTaskStartScheduler();
    while(1);
}

5.2 vTaskList() - 전체 Task 상태 확인

C 버전:

// FreeRTOSConfig.h에 추가 필요
// #define configUSE_TRACE_FACILITY 1
// #define configUSE_STATS_FORMATTING_FUNCTIONS 1

#include "FreeRTOS.h"
#include "task.h"
#include <string.h>

void vMonitorTask(void *pvParameters) {
    char taskListBuffer[512];
    
    while(1) {
        printf("\n=== Task 상태 ===\n");
        vTaskList(taskListBuffer);
        printf("%s\n", taskListBuffer);
        
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

int main(void) {
    xTaskCreate(vTask1, "Task1", 128, NULL, 1, NULL);
    xTaskCreate(vTask2, "Task2", 256, NULL, 2, NULL);
    xTaskCreate(vTask3, "Task3", 512, NULL, 3, NULL);
    xTaskCreate(vMonitorTask, "Monitor", 256, NULL, 4, NULL);
    
    vTaskStartScheduler();
    while(1);
}

출력 예시:

=== Task 상태 ===
Name          State   Priority   Stack    Num
Task1         R       1          100      1
Task2         B       2          200      2
Task3         B       3          450      3
Monitor       R       4          180      4
IDLE          R       0          50       5

State: R=Running, B=Blocked, S=Suspended, D=Deleted

5.3 Stack Overflow 감지

C 버전:

// FreeRTOSConfig.h에 추가
// #define configCHECK_FOR_STACK_OVERFLOW 2

void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
    // Stack Overflow 발생 시 호출됨
    printf("!!! Stack Overflow 감지 !!!\n");
    printf("Task: %s\n", pcTaskName);
    
    // 긴급 처리
    // - 시스템 리셋
    // - 에러 로그 저장
    // - LED 경고 표시
    
    while(1) {
        // 무한 루프로 시스템 정지
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_13);
        HAL_Delay(100);
    }
}

C++ 버전:

class StackMonitor {
public:
    static void reportOverflow(TaskHandle_t xTask, const char* taskName) {
        std::cout << "!!! Stack Overflow 감지 !!!" << std::endl;
        std::cout << "Task: " << taskName << std::endl;
        
        // 긴급 처리
        emergencyHandler();
    }
    
private:
    static void emergencyHandler() {
        while(1) {
            HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_13);
            HAL_Delay(100);
        }
    }
};

extern "C" void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
    StackMonitor::reportOverflow(xTask, pcTaskName);
}

6. Task 정보 조회

6.1 Task 상태 조회

C 버전:

#include "FreeRTOS.h"
#include "task.h"

void print_task_info(TaskHandle_t xTask) {
    if(xTask == NULL) return;
    
    // 우선순위
    UBaseType_t priority = uxTaskPriorityGet(xTask);
    
    // Stack 여유 공간
    UBaseType_t stackMark = uxTaskGetStackHighWaterMark(xTask);
    
    // Task 이름
    const char* name = pcTaskGetName(xTask);
    
    // Task 상태
    eTaskState state = eTaskGetState(xTask);
    const char* stateStr[] = {"Running", "Ready", "Blocked", "Suspended", "Deleted"};
    
    printf("Task: %s\n", name);
    printf("  우선순위: %d\n", priority);
    printf("  상태: %s\n", stateStr[state]);
    printf("  Stack 여유: %d words\n", stackMark);
}

int main(void) {
    TaskHandle_t xHandle;
    
    xTaskCreate(vMyTask, "MyTask", 256, NULL, 2, &xHandle);
    
    vTaskDelay(pdMS_TO_TICKS(100));
    
    print_task_info(xHandle);
    
    vTaskStartScheduler();
    while(1);
}

C++ 버전:

class TaskInfo {
public:
    static void print(const Task& task) {
        TaskHandle_t handle = task.getHandle();
        if(handle == nullptr) return;
        
        UBaseType_t priority = uxTaskPriorityGet(handle);
        UBaseType_t stackMark = uxTaskGetStackHighWaterMark(handle);
        const char* name = pcTaskGetName(handle);
        eTaskState state = eTaskGetState(handle);
        
        const char* stateStr[] = {"Running", "Ready", "Blocked", "Suspended", "Deleted"};
        
        std::cout << "Task: " << name << std::endl;
        std::cout << "  우선순위: " << priority << std::endl;
        std::cout << "  상태: " << stateStr[state] << std::endl;
        std::cout << "  Stack 여유: " << stackMark << " words" << std::endl;
    }
};

C vs C++ 비교 정리

Task 관리 방식 비교

측면C 방식C++ 방식
Task 생성함수 포인터 직접 전달클래스 메서드로 캡슐화
핸들 관리전역 변수 또는 매개변수멤버 변수로 관리
우선순위 변경vTaskPrioritySet() 직접 호출setPriority() 메서드
상태 조회uxTaskPriorityGet() 직접 호출getPriority() 메서드
삭제vTaskDelete() 직접 호출소멸자에서 처리 가능

실전 선택 가이드

C 방식을 선택할 때:

  • FreeRTOS 공식 문서 예제 참고 시
  • 메모리가 매우 제한적인 경우
  • 간단한 Task 구조

C++ 방식을 선택할 때:

  • 복잡한 Task 계층 구조
  • 리소스 관리 자동화 필요
  • 코드 재사용성 중요

7. 실습 문제

문제 1: Task 생명주기 관리

동적으로 Task를 생성하고 일정 시간 후 자동 삭제하는 시스템을 작성하시오.

요구사항:

  • Task는 생성 후 10초간 실행
  • 10초 후 자동으로 자신을 삭제
  • 삭제 전에 리소스 정리

C 함수 프로토타입:

void vTimedTask(void *pvParameters);

C++ 클래스:

class TimedTask : public Task {
private:
    int lifetime;  // 초 단위
public:
    TimedTask(int seconds);
    void run() override;
};

문제 2: 우선순위 기반 작업 스케줄링

3개의 Task를 생성하고, 시스템 부하에 따라 우선순위를 동적으로 조정하시오.

요구사항:

  • Low, Medium, High 3개의 우선순위 Task
  • CPU 사용률 80% 이상이면 Low Task 일시정지
  • CPU 사용률 50% 이하면 모든 Task 정상 동작

문제 3: Stack 사용량 모니터링

모든 Task의 Stack 사용량을 주기적으로 확인하고, 임계값 이하면 경고를 출력하시오.

요구사항:

  • 5초마다 모든 Task의 Stack High Water Mark 확인
  • 여유 공간이 50 words 이하면 경고
  • vTaskList()를 사용하여 전체 상태 출력

8. 학습 정리

오늘 배운 내용

  • xTaskCreate()는 동적 할당, xTaskCreateStatic()은 정적 할당
  • vTaskDelete()로 Task를 삭제할 수 있으나 리소스 정리 필수
  • vTaskSuspend()/vTaskResume()으로 Task를 일시정지/재개
  • vTaskPrioritySet()으로 우선순위를 동적으로 변경 가능
  • uxTaskGetStackHighWaterMark()로 Stack 사용량 모니터링
  • vTaskList()로 전체 Task 상태 확인 가능
  • Stack Overflow Hook으로 오버플로 감지

핵심 개념

1. Task 생성 방식

  • 동적: Heap 사용, 편리함
  • 정적: 메모리 제어, 안전성

2. Task 삭제 주의사항

  • Mutex/Semaphore 반환 후 삭제
  • 동적 메모리 해제 후 삭제
  • 핸들 NULL로 설정

3. Stack 관리

  • High Water Mark로 사용량 확인
  • Stack Overflow Hook 구현 필수
  • 충분한 여유 공간 확보 (최소 20%)

profile
당신의 코딩 메이트

0개의 댓글