본문 바로가기
임베디드 관련/FreeRTOS

[FreeRTOS] Task의 우선순위와 선점

by minhyeok.lee 2024. 5. 18.
반응형

FreeRTOS에서 Task의 우선순위와 선점, Task간 흐름제어


1. 우선순위가 다를 때

우선순위가 다른 두 Task를 만든다.

우선순위의 숫자가 클 수록 더 우선순위가 높다.

xTaskCreate((TaskFunction_t)Task1, "Task1", 128, NULL, 10, &xHandle1);
xTaskCreate((TaskFunction_t)Task2, "Task2", 128, NULL, 9, &xHandle2 );

 

알파벳 ‘a’를 1초마다 한 번씩 출력하는 우선순위 10의 Task1을 만든다.

알파벳 ‘b’를 1초마다 한 번씩 출력하는 우선순위 9의 Task2를 만든다.

Task1과 Task2의 vTaskDelay()를 주석처리 해보면서 두 Task가 어떻게 작동하는지 확인한다.

 


 

1. Task1, 2 모두 vTaskDelay()를 주석처리 하지 않았을 때 결과

void Task1( void* pvParameters ) {
    while(true) {
        Serial.print("a");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void Task2( void* pvParameters ) {
    while(true) {
        Serial.print("b");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

1. Task1의 우선순위가 Task2보다 높기 때문에 먼저 a를 출력하고 1초간 Blocked 된다.

2. 이제 Task2가 실행될 수 있으며 b를 출력하고 1초간 blocked 된다.

3. 아무것도 실행되지 않으므로 Idle task가 있다면 실행된다. (없다면 Idle State-유휴상태로 들어간다.)

4. Blocked state에서 벗어난 Task1이 a를 출력하고 1초간 blocked되고 Task2가 b를 출력하고 blocked되는 과정이 반복된다.

 


 

2. Task 1의 vTaskDelay()를 주석했을 때 결과

void Task1( void* pvParameters ) {
    while(true) {
        Serial.print("a");
        // vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void Task2( void* pvParameters ) {
    while(true) {
        Serial.print("b");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

1. 우선순위가 높은 Task1이 시작되며 Blocked 될 일이 없기 때문에 Task2는 영원히 실행되지 않는다.

2. 이떄 Task2는 기아상태 Starvation(기아현상)이 된다.

 


 

3. Task 2의 vTaskDelay()를 주석했을 때 결과

void Task1( void* pvParameters ) {
    while(true) {
        Serial.print("a");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void Task2( void* pvParameters ) {
    while(true) {
        Serial.print("b");
        // vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

1. 우선순위가 높은 Task1이 시작되고, vTaskDelay()를 만난 뒤 Blocked 되면 Task2가 시작된다.

2. Task2는 빠른 속도로 b를 출력한다.

3. 1초 이후 Blocked상태에서 빠져나온 Task1이 Task2를 선점하여 a를 1회 출력한 뒤 다시 Blocked 된다.

4. Task2는 다시 빠른 속도로 b를 출력하는 과정이 반복된다.

 


 

4. Task 1, 2 모두 vTaskDelay()를 주석했을 때 결과

void Task1( void* pvParameters ) {
    while(true) {
        Serial.print("a");
        // vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void Task2( void* pvParameters ) {
    while(true) {
        Serial.print("b");
        // vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

1. 우선순위가 높은 Task1이 시작되며 Blocked 될 일이 없기 때문에 Task2은 영원히 실행되지 않는다.

2. 이떄 Task2는 기아상태 Starvation(기아현상)이 된다.


2. 우선순위가 같을 때

우선순위가 같은 두 Task를 만든다.

xTaskCreate((TaskFunction_t)Task1, "Task1", 128, NULL, 10, &xHandle1);
xTaskCreate((TaskFunction_t)Task2, "Task2", 128, NULL, 10, &xHandle2 );

 

알파벳 ‘a’를 1초마다 한 번씩 출력하는 우선순위 10의 Task1을 만든다.

알파벳 ‘b’를 1초마다 한 번씩 출력하는 우선순위 10의 Task2를 만든다.

Task1과 Task2의 vTaskDelay()를 주석처리 해보면서 두 Task가 어떻게 작동하는지 확인한다.


 

 

1. Task1, 2 모두 vTaskDelay()를 주석처리 하지 않았을 때 결과

void Task1( void* pvParameters ) {
    while(true) {
        Serial.print("a");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void Task2( void* pvParameters ) {
    while(true) {
        Serial.print("b");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

1. Task1의 우선순위와 Task2의 우선순위가 같기 때문에 랜덤하게 a 혹은 b를 출력하고 해당 Task는 1초간 Blocked 된다.

2. 실행되지 않은 Task가 실행될 수 있다.

    - "1."에서 a가 출력되었다면 b를 출력하고 1초간 blocked 된다.

    - "1."에서 b가 출력되었다면 a를 출력하고 1초간 blocked 된다.

3. 이후 아무것도 실행되지 않으므로 Idle task가 있다면 실행된다. (없다면 Idle State-유휴상태로 들어간다.)

4. "1."에서 실행되어 Blocked 되었던 Task가 Blocked state에서 벗어나고 1.부터 순서를 다시 반복하게 된다.

 


 

2. Task 1의 vTaskDelay()를 주석했을 때 결과

void Task1( void* pvParameters ) {
    while(true) {
        Serial.print("a");
        // vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void Task2( void* pvParameters ) {
    while(true) {
        Serial.print("b");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

1. Task1의 우선순위와 Task2의 우선순위가 같기 때문에 결과는 아래와 같다.

    -  처음 a가 출력되었다면 vTaskDelay가 없기때문에 a가 계속 출력되다가 다음 Task상태에 있는 Task2가 b를 출력하고 Blocked 된다.

    -  처음 b가 출력되었다면 해당 Task는 1초간 Blocked 된다

2. Task1이 계속 실행되어 a는 계속해서 출력된다.

3. Task1은 vTaskDelay가 없기떄문에 Idle State로 들어가지 않고 계속하여 a를 출력한다.

4. Blocked 되었던 Task2가 Blocked state에서 벗어나고 b를 한 번 출력하고 1초간 Blocked 되고 Task1은 계속 a를 출력한다.

 

 


 

3. Task 2의 vTaskDelay()를 주석했을 때 결과

void Task1( void* pvParameters ) {
    while(true) {
        Serial.print("a");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void Task2( void* pvParameters ) {
    while(true) {
        Serial.print("b");
        // vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

1. Task1의 우선순위와 Task2의 우선순위가 같기 때문에 결과는 아래와 같다.

    -  처음 b가 출력되었다면 vTaskDelay가 없기때문에 b가 계속 출력되다가 다음 Task상태에 있는 Task2가 a를 출력하고 Blocked 된다.

    -  처음 a가 출력되었다면 해당 Task는 1초간 Blocked 된다

2. Task2가 계속 실행되어 b를 계속해서 출력된다.

3. Task2는 vTaskDelay가 없기떄문에 Idle State로 들어가지 않고 계속하여 b를 출력한다.

4. Blocked 되었던 Task1이 Blocked state에서 벗어나고 a를 한 번 출력하고 1초간 Blocked 되고 Task2은 계속 b를 출력한다.

 

 

 


 

4. Task 1, 2 모두 vTaskDelay()를 주석했을 때 결과

void Task1( void* pvParameters ) {
    while(true) {
        Serial.print("a");
        // vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void Task2( void* pvParameters ) {
    while(true) {
        Serial.print("b");
        // vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

1. Task1의 우선순위와 Task2의 우선순위가 같기 때문에 결과는 a와 b가 번갈아가며 계속하여 출력된다.

2. a가 5번 먼저 출력되면 b가 5번 출력되는 형태가 아니라 무작위로 a와 b가 출력된다.

3. 따라서 몇 번씩 돌리겠다와 같은 Task 실행횟수 혹은 Task의 흐름제어가 필요하다면 우선순위를 사용해야 한다.

 


3. 결론

1. FreeRTOS에서 멀티테스킹이 작동하는 방식은 Priority(우선순위)와 Preemption(선점)이다.

2. Task를 생성하고 우선순위를 변경하며 관리해야한다.

3. 실행흐름을 올바르게 구축하지 못하여 Task의 priority 설정이 잘못된다면 Task의 Starvation(기아현상)을 유발하여 Task가 개발자의 의도와는 다르게 동작하지 않을 수 있다.

4. 우선순위를 같게 한다면 Starvation은 막을 수 있지만 특정 Task를 특정 횟수(몇 번)만큼 수행하거나 Task 사이의 동작순서와 같은 흐름제어가 필요하다면 우선순위를 사용한다.

5. 단순 우선순위 설정만으로 해결되지 않는다면 아래와 같이 Task의 우선순위를 함수 수행 중 변경하여 흐름제어를 해야한다.

vTaskSuspend(xHandle1);
vTaskPrioritySet(xHandle1, 7);
vTaskResume(xHandle1);
반응형

댓글