임베디드/임베디드 프로젝트

FreeRTOS 들여다보기5 - Task1(task state, delay)

fish9903 2023. 8. 27. 19:05

사용하는 보드와 freeRTOS 버전 정보, 진행 내용 등에 대한 정보는 github에 있습니다. (계속 업데이트 중..)

https://github.com/fish9903/FreeRTOS-STM32G4

 

GitHub - fish9903/FreeRTOS-STM32G4: FreeRTOS (Real Time Operating System) on STM32G474(ARM cortex M4 based mircocontrollers)

FreeRTOS (Real Time Operating System) on STM32G474(ARM cortex M4 based mircocontrollers) - GitHub - fish9903/FreeRTOS-STM32G4: FreeRTOS (Real Time Operating System) on STM32G474(ARM cortex M4 based...

github.com


이번엔 FreeRTOS의 Task에 대해 알아보자

 

1. Task state

Task state는 크게 두가지로 나눌 수 있다.

Running stateNot-Running state이다.

 

Running state는 현재 CPU를 점유하며 실행중인 상태를 의미하고, Not-Running state는 현재 CPU를 점유하지 않는 상태를 의미한다.

Not-Running state에는 Suspended, Ready, Blocked state가 속해있고, 그림으로 보면 다음과 같다.

 

Task간 전환

Task가 처음 생성되면 Ready state로 들어간다.

그리고 scheduler가 실행될 task를 ready state인 task 중에서 골라서 실행한다. (Ready list)

여기까진 단순한 과정이다.

이제 Suspended와 Blocked state에 대해 알아보자

 

2. Blocked State

Blocked state는 일시적 또는 영구적으로 CPU에서 실행되지 못하는 상태이다.

어떤 event가 발생해야 ready state로 가고, event가 있기 전까지 동작하지 않는다.

따라서 blocked state는 CPU를 점유하지 않는 상태이다. (중요)

Time out이 있다는 특징이 있다.

 

이런 특징을 가진 blocked state는 delay구현, synchronization을 위해 사용될 수 있다.

이번 글에서는 bocked state를 사용한 delay 방법을 알아본다.

 

Running state인 task를 blocked state로 전환하는 API는 vTaskDelay(), vTaskDelayUntil() 두 개가 있다.

이 API에 대해선 아래에서 다룬다.

 

3. Suspended State

Suspended state도 blocked state와 동일하게 CPU를 점유하지않는 상태이다.

이 state에 진입하는 방법이 특이한데, 다른 task 또는 자기자신이 task를 suspend시켰을 때 진입한다.

그리고 다른 실행중인 task가 suspend를 취소하기 전까지 ready state로 복귀하지 못한다. 이것이 blocked state와의 차이점이다. (Blocked state는 time out이 있어서 자동으로 나올 수 있음)

 

또 하나 blocked state와 차이점은 ready state는 suspended state로 바로 들어갈 수 있다는 것이다.

 

4. Delay

위에서 freeRTOS에서 task를 delay하는데 사용되는 API를 언급했었다.

이 API말고 STM사에서 제공하는 HAL관련 API인 HAL_Delay()라는 API도 있다.

이 둘의 차이점을 알아보자

 

결정적인 차이는 HAL_Delay() API는 task를 delay시키는 시간 동안 그 task가 계속해서 CPU를 점유하고 있고, vTaskDelay()와 vTaskDelayUntil()은 그렇지 않다는 것이다.

 

이를 코드와 실행결과로 살펴보자

 

4.1) HAL_Delay()를 사용한 delay 구현

3개의 task 생성
Delay 설정(10ms)
SystemView로 본 실행 결과

 

SystemView의 결과를 분석해보자

우선 타임라인 부분을 보면 3개의 task가 번갈아 실행되고 있는 것을 확인할 수 있다.

특징은 하나의 task가 while문이 끝날 때 까지(delay가 끝날 때 까지) CPU를 점유하고 있다는 것이다.

 

이 특징은 Contexts 부분을 보면 더 자세히 나와있다.

3개의 task가 CPU Load를 33%씩 잡아먹고있어서, CPU가 Idle인 경우가 거의 없다.

이는 time periodic한 목적을 이루는데 delay사용할 경우, 매우 안좋은 경우라고 할 수 있다.

 

왜냐하면 아무일도 하지않고 단지 delay를 기다리고 있을 뿐인데, CPU를 점유하고 있기 때문이다.

이는 CPU낭비이고, 전력 소모도 커진다는 단점이 있다.

 

4.2) vTaskDelay(), vTaskDelayUntil()을 사용한 delay 구현

이 문제를 해결하기 위해 이 API가 사용된다.

 

4.1)와 동일한 상황을 만들고 실행해보자

2ms를 기다린다

 

SystemView로 본 실행 결과

 

vTaskDelay()는 RTOS Tick기반으로 delay를 하기때문에 ms --> Tick으로 변환하는 매크로를 통해 2ms를 기다리는 코드를 작성하였다.

1초에 1000번의 tick이 발생하므로, 1ms에 1tick이 발생한다. (FreeRTOSConfig.h 참고)

 

SystemView 결과를 보면 4.1)과 확연히 다름을 느낄 수 있다.

우선 timeline을 보면 대부분 비어있는(Idle 동작) 것을 볼 수 있다.

 

Contexts 부분을 보면 3개의 task의 CPU Load가 매우 적어진 것을 확인할 수 있고, 빈 공간은 전부 Idle이 채우고 있다.

 

CPU가 task가 delay인 동안에 쉬고(idle)있다는 뜻이다.

이처럼 특정 시간마다 작동되는 periodic task를 구현할 때는 이런 방법을 사용해야 한다.

 

이런 결과가 나오는 이유는 vTaskDelay()는 task를 blocked state로 만들기 때문이다.

Tasks를 CPU를 점유하지 않는 blocked state로 만들고, 특정 event(RTOS Tick)가 발생하면 깨우는 방식을 사용한다.

 

vTaskDelayUntil()도 blocked state를 사용하여 delay를 구현한다.

vTaskDelay()와 다른점은 더 정확한 time delay를 제공한다는 것이다.

 

vTaskDelay()는 이 함수가 호출된 시점부터 지정한 시간(tick)동안 delay를 시키기 때문에 상대적인 시간 동안 delay를 시킨다.

하지만 vTaskDelayUntil() 함수는 task가 unblock되기 원하는 절대적인 시간동안 delay시킨다.

 

따라서 fixed frequency를 위해선 vTaskDelayUntil()함수를 사용해야 정확한 결과를 얻을 수 있다.

물론 특정 task가 특정 시간 동안 delay하는 동작 구현을 위해선 vTaskDelay()를 사용해도 된다.

 

vTaskDelay() vs. vTaskDelayUntil()

vTaskDelay(): vTaskDelay()함수가 호출될 때 부터 time계산 (상대적)

이 작업이 n 초 동안 delay하는 동작을 위해 사용 가능

 

vTaskDelayUntil(): 절대적인 시간으로 task를 delay한다.

이 작업이 n 초 마다 정확한 주기마다 이루어져야 할 때 사용 가능

 

위 코드에선 vTaskDelayUntile()로 구현을 해도 비슷한 결과가 나올 것이다.