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

FreeRTOS 들여다보기2 - Task creation

fish9903 2023. 8. 20. 09:40

사용하는 보드와 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를 생성하고 이를 어떻게 schedeling하는지 알아본다.

 

1. Task

Task란 단어 그대로 작업이란 뜻이다. 우리가 하루에 하는 일을 컴퓨터의 일로 바라보면 될듯하다.

즉, task는 단지 application에서 실행되는(실행가능한) 코드이다. (RTOS에선 process를 task로 본다.)

 

하나의 application은 여러개의 task로 나눌 수 있다. 

예를 들어, 미세먼지 농도 측정 application에는 데이터 수집(task1), 데이터 display(task2) 두개의 task가 존재할 수 있다.

 

 

1. Task creation, implementation

FreeRTOS에선 어떻게 task를 생성하고 구현하는지 보자.

 

다음은 freeRTOS에서 사용하는 task생성 함수이다.

xTaskCreate 함수

사용하는 파라미터를 순서대로 보면

  • pvTaskCode: 동작할 코드(address of task handler / function pointer)
  • pcName: task의 이름
  • usStackDepth: 이 task가 사용할 stack memory 크기
  • pvParameters: pvTaskCode(task handler)에 넘겨줄 데이터
  • uxPriority: 이 task의 priority
  • pxCreatedTask: 생성한 task의 memory address를 가리키는 포인터

 

Task 생성에 성공하면 pdPASS를, 실패했다면 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY라는 메모리 부족으로 생성 실패했다는 에러를 반환한다.

 

이 API를 사용하여 2개의 task를 생성한 코드는 아래와 같다.

main.c에 priority가 동일한 두 개의 task를 생성하고 scheduler를 호출하는 코드

각가의 task를 구현한 코드는 다음과 같다.

보통 task는 무한루프로 구현한다

 

2. 실행 결과와 분석(preemption scheduling)

이를 실행시켜 보면 다음과 같은 결과가 나온다.

Task1과 2의 출력이 섞여있다

이런 이상한? 결과가 나오는 이유는 현재 freeRTOS 설정파일을 보면 알 수 있다.

preemption setting

FreeRTOSConfig.h 파일을 보면 다양한 매크로들이 정의되어 있다. 

이중에서 configUSE_PREEMPTION이 1로 세팅되어 있음을 알 수 있다. 이는 scheduling을 preemption방식을 사용한다는 뜻이다.

Preemption scheduling을 사용하기 때문에 위와 같은 이상한? 결과가 나오는 것이다.

 

좀 더 자세히 살펴보면  이는 이상한 결과가 아닌, 정상적인 결과이다.

FreeRTOS는 tick 기반으로 scheduling을 한다.(tick에 대한 내용은 추후에..)

이 tick의 주기에 따라 task를 변경하는데, 주기에 대한 매크로도 FreeRTOSConfig.h 파일에서 찾을 수 있다.

Tick

configTICK_RATE_HZ = 1000 이라는 것은 1초에 1000번의 tick(interrupt)이 발생한다는 것이다. 

즉, 1ms마다 task가 변경된다. (정확히 말하면 scheduler가 호출된다.)

 

이를 바탕으로 위 출력 결과를 분석해보자.

각 task는 최대 1ms동안 실행될 수 있다. 1ms마다 scheduler가 호출되어 scheduling을 하는데, task1과 task2는 같은 priority를 가지고 있기 때문에 task1이 실행중이면 task2이, taks2가 실행중이면 task1이 실행되도록 scheduling을 한다.

 

Preemption scheduling을 하기때문에 현재 실행중인 task가 아직 끝나지 않은 상황(여기선 parameter를 전부 print하는 것)이라도 상관하지 않고, 밀어내고 다음 task를 실행시킨다.

 

따라서 1ms동안 처리하지 못한 task 내 작업들은 중단되고 다음 task로 넘어간다.

1ms마다 scheduler가 호출되어 task를 바꾼다.

출력결과를 보면 "Hello world from task1"이 출력되다가 갑자기 "Hello worldfHelloworld from task2" 라는 섞인 결과를 출력한다.

여기서 Hello worldf까지가 task1의 출력, Helloworld from task2은 task2의 출력이다.

이전까지 task1의 출력을 하다가 Hello worldf를 출력하는 도중에 1ms가 지나서 task2의 출력이 된 결과이다.

 

정리하면 이렇게 실행중인 task를 밀어내고 다른 task를 실행할 수 있는 이유는 preemption scheduling을 하기 때문이다.

 

3. 실행 결과와 분석(non preemption scheduling)

그렇다면 non preemption scheduling의 경우는 어떨까?

 

이를 확인하기 위해 FreeRTOSConfig.h 파일의 preemption 설정을 다음과 같이 변경하고 실행시킨다.

non preemption setting

 

Task1 결과만 출력되는 모습(non preemption)

이 경우 task1의 결과만 출력되었다.

서로 priority가 같고, preemption이 아니기 때문에 처음 실행한 task1이 계속 실행되는 것이다.

 

이 상황에서 task2도 실행하려면 다음과 같이 코드를 변경하면 된다.

task 구현코드에 taskYIELD() 추가

각 task가 끝나면 자원을 반납(CPU에서 나감)하도록 한다. (yield)

 

그러면 다음과 같은 결과가 나온다.

Task1과 2의 결과가 번갈아 출력된다

이러한 scheduling 방식을 cooperative scheduling이라고 한다. 이 방식에서는 tick(interrupt)를 사용하지 않고 scheduling을 한다.

 


사용한 함수의 코드 내부

 

xTaskCreate() 

xTaskCreate()함수의 일부

 stack에 task가 사용할공간을 확보하고, TCB(Task Control Block)을 stack에 배치한다.

 

taskYIELD()

context switching을 하는 매크로로 정의되어 있다.

 

TCB와 context switching 참고... https://fish9903.tistory.com/entry/FreeRTOS-%EB%93%A4%EC%97%AC%EB%8B%A4%EB%B3%B4%EA%B8%B03-Scheduler2Context-switching


다음은 이런 task를 실행시키고 관리하는 scheduler에 대해 알아본다.