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

FreeRTOS 들여다보기3 - Scheduler1

fish9903 2023. 8. 21. 16:27

사용하는 보드와 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


이전 포스트 내용(task creation)과 이어진다.

 

1. Scheduler

Scheduler는 특별한 것이 아닌, 다른 프로그램과 같이 코드로 이루어진 프로그램이다.

Scheduler가 하는 기본적인 일은 다음에 어떤 task가 CPU에서 돌아갈지 결정(선택)하는 것이다.

한개의 task만 존재한다면 scheduler가 필요없겠지만, 대부분의 경우 여러개의 task가 돌아가기 때문에 scheduler가 필요하다.

 

Scheduler는 알고리즘 기반으로 여러 task를 관리한다.

이전 포스트에서 본 priority based preemptive scheduling, cooperative scheduling이 대표적인 예시이다.

이 두 알고리즘과 scheduler가 동작하는 방식을 알아본다.

(대부분의 RTOS는 priority based preemptive scheduling을 사용한다.)

 

2. Scheduler

Scheduler는 특별한 것이 아닌, 다른 프로그램과 같이 코드로 이루어진 프로그램이다.

 

이전 포스트에서 task1, task2를 생성한 코드를 보자

main.c

task1과 2를 생성하고 'vTaskStartScheduler()'로 scheduler를 호출한다.

 

이 함수의 구현을 보면 scheduler가 무엇으로 이루어져 있고 어떻게 동작하는지 알 수 있다.

 

task.c 파일의 vTaskStartScheduler()함수 구현 일부분

함수 구현 부분를 보면 xPortStartScheduler()함수를 호출하고 있고, 이를 다시한번 따라가보면

 

port.c

PendSV와 SysTick 관련 설정과 timer interrupt를 시작하는 것을 볼 수 있다.

 

FreeRTOS의 scheduler인 vTaskStartScheduler() 함수는 xPortStartScheduler()를 호출하고, 여기서 SysTick과 PendSV관련 설정을 하는 방식이다.

PendSv와 SysTick은 scheduler가 task를 변경하는 작업을 위해 필요한 동작이다.

PendSv는 xPortPendSVHandler()에 구현되어 있고, SysTick은 xPortSysTickhandler()에 구현되어 있다.

코드 주석을 보면 어떤 일을 하는지 짐작할 수 있다.

 

xPortPendSVHandler() 구현 일부분

xPortPendSVHandler() 구현부분의 주석을 보면 context switching에 관련된 동작을 하는 것을 알 수 있다.

 

xPortSysTickHandler() 구현 부분

xPortSysTickHandler() 구현부분의 주석을 보면 RTOS tick(interrupt)를 발생시키는 코드임을 알 수 있다.

이전 포스트에서 FreeRTOSConfig.h 파일에 tick이 1초에 1000번 즉, 1ms마다 1번씩 발생(interrupt)하는 것을 봤으므로 이 함수는 1ms마다 1번씩 tick을 발생시킬 것이다.

그리고 잘보면 context swtiching을 발생시키는 것을 볼 수 있다. Context switching은 xPortPendSVHandler()에서 처리하므로 이 handler를 호출한다.

 

정리해보면 FreeRTOS scheduler인 vTaskStartScheduler()는 xPortStartScheduler()를 호출하여 task를 바꾸기 위한 동작(sys tick(interrupt), context switching)을 설정한다.

 

3. Segger SystemView로 보기

Segger SystemView로 application의 동작 과정을 자세히 살펴보자 

 

실행 코드

 

SEGGER SystemView로 taks1과 task2 그리고 scheduler를 찍어본 모습

 

이 결과는 task1, task2를 생성하고 preemption방식으로 scheduling한 결과를 순서대로 기록한 것이다.

 

각 파트를 보면서 관찰해보자

 

task1, task2의 생성

시간순으로 정렬된 결과를 순서대로 보면 'Task Create  task-1'과 'Task Create  task-2' 작업을 볼 수 있다.

작성한 코드 그대로 task1을 생성하고 task2를 생성한 결과이다. (Priority = 2)

그리고 이후에 또 하나의Task(Task 0x10000B90)를 생성하는데 이 task에 대해서는 아래에서 살펴본다.

 

다음은 타임라인 부분을 보자

Timeline

타임라인을 보면 Task1(파란색)Taks2(초록색)이 번갈아 1ms마다 실행되는 것을 볼 수 있다. (preemption)

이를 좀 더 확대해서 보면 SystTick과 scheduler를 확인할 수 있다.

 

SysTick과 scheduler

빨간색이 SysTick, 흰색이 scheduler에 해당한다.

위 결과로 보면 task2가 실행된지 1ms가 지나 SysTick이 호출되어서 중단되고 scheduler가 실행된다.

그리고 task1이 1ms 동안 실행되고 다시 SystTick이 호출되어서 중단되고 scheduler가 실행 --> task2 실행되는 모습이다.

SysTick은 이처럼 1ms마다 interrupt를 발생시켜 scheduler를 호출하고 이 것을 xPortSysTickHandler()에서 수행한다.

그리고 좀 더 정확히 따지면 여기서 나타난 scheduler는 xPortPendSVHandler()이다. (context switching을 하기 때문)

 

4. 또 하나의 Task

위에서 언급한 task에 대해 알아보자

 

task0도 생성되었다

코드를 보면 task1, task2를 생성하였고 이것이 실행된 결과임을 알 수 있었다.

하지만 이 결과에는 task(Task 0x10000B90)도 생성되었는데 이건 무슨 task이고 언제 생성된 것일까?

 

FreeRTOS의 scheduler인 vTaskStartScheduler() 구현 부분을 보자

Idletask 라는 task를 생성하는 코드를 찾을 수 있다

scheduler 내부에서 IdleTask라는 task를 생성하고 있다.

 

Scheduler를 실행하면 자동으로 idle task를 생성하는데 이 task가 무슨 일을 할까?

우선 주석을 보면, 이 task는 가장 낮은 priority(0)를 가짐을 확일 할 수 있다. 따라서 실행할 task가 이것 이외 아무것도 없다면 실행되는 task이다. 여기서 왜 idle task인지 유추할 수도 있다.

 

Idle task는 가장 낮은 priority를 가지고 있기때문에 freeRTOS에선 최소한 한개의 task가 동작중이다.

이 task가 하는 일은 다음과 같다.

 

idle task

코드를 보면 prvCheckTasksWaitingTermination()이라는 함수를 호출한다.

이 함수는 다른 task가 삭제되었는지 확인하는 함수로, 삭제되었다면 memory에서 free하는 역할을 한다.

 

따라서 idle task는 가장 낮은 priority로 계속 실행되면서 task가 삭제될 때마다 그 task를 memory에서 free하는 뒤처리를 하는 task이다.

 

(Idle task는 hook function을 받아 처리할 수도 있는데, 이것은 나중에..)

 

위 SEGGER SystemView로 본 결과에선 Idle task가 동작하는 것을 볼 수 없었다. task1과 task2가 항상 번갈아서 동작하고, 삭제되는 task가 없기 때문이다.


다음에는 PendSv, SysTick으로 어떻게 scheduling을 하는지 자세히 다뤄본다.