FreeRTOS 간단한 커널 구현 8 - 시간 조각 할당

  1. 고려 사항과 답변 =======

0.1. 고려 사항 1

시간 조각 할당을 추가하는 이유는 무엇인가요?

현재 RTOS 커널은 선점 우선순위를 지원하고 있습니다. 즉, 높은 우선순위의 작업이 낮은 우선순위의 작업을 중단시키고 실행됩니다. 그러나 동일한 우선순위의 작업에 대해 시간 조각 할당을 지원하지 않으면, 한 번 실행된 작업만 계속 실행되며, 지연 차단이 해제될 때까지 다른 작업이 실행되지 않습니다. 따라서 동일한 우선순위의 작업이 순차적으로 실행될 수 있도록 시간 조각 할당 기능을 추가해야 합니다.

  1. 커널 프로그램 수정 ========

1.1. xTaskIncrementTick()

이 함수에서는 작업 지연 차단 시간이 만료되어 작업 스케줄링이 발생할 뿐만 아니라, 시간 조각 할당을 위한 작업 전환도 지원합니다. 다음은 변경된 내용입니다.

/*task.c*/
BaseType_t xTaskIncrementTick(void)
{
	// 수정되지 않은 코드 생략
	......
#if((configUSE_PREEMPTION == 1) && (configUSE_TIME_SLICING == 1))
	// 시간 조각 할당 지원
	if(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[pxCurrentTCB->uxPriority])) > 1)
	{
		xSwitchRequired = pdTRUE;
	}
#endif
	return xSwitchRequired;
}

/* FreeRTOSConfig.h */
// 시간 조각 할당 활성화
#define configUSE_TIME_SLICING                  1

1.2. 원리

시스템에 동일한 우선순위의 두 작업만 존재한다고 가정해보겠습니다. 시간 조각 할당을 통해 작업 전환되는 과정은 다음과 같습니다.

xTaskIncrementTick() -> vTaskSwitchContext() -> taskSELECT_HIGHEST_PRIORITY_TASK() -> listGET_OWNER_OF_NEXT_ENTRY()

타이머 인터럽트 서비스 함수가 호출되면, 특정 우선순위의 준비 목록에 항목이 하나 이상 있는지 확인합니다. 이는 해당 우선순위에서 여러 작업이 존재함을 의미합니다. 이 경우 작업 스케줄링이 발생합니다.

작업 스케줄링이 발생하면 vTaskSwitchContext()taskSELECT_HIGHEST_PRIORITY_TASK() 함수가 호출되어 현재 최우선순위의 작업을 찾습니다. 그러나 시스템에는 동일한 우선순위의 두 작업만 존재하므로 최우선순위는 변하지 않지만, 반환되는 작업은 다음 작업이 됩니다.

왜 그런가요?

핵심은 listGET_OWNER_OF_NEXT_ENTRY() 매크로 함수입니다. 이 함수는 매번 연결 리스트의 다음 항목의 pvOwner 값을 가져옵니다. 이는 이중 연결 리스트이기 때문에, 동일한 우선순위의 작업들 사이를 반복적으로 순회하게 됩니다. 작업 스케줄링이 발생할 때마다 작업이 전환되기 때문에, 시간 조각 할당이 가능해집니다.

  1. 실험 ====

3.1. 테스트

FreeRTOS 간단한 커널 구현 6 - 우선순위 "3.1. 테스트" 섹션을 참조하여, 두 작업의 우선순위를 동일하게 설정하고, 두 작업 모두 소프트웨어 지연을 사용하여 연속 실행하도록 테스트합니다. 다음은 예제 코드입니다.

/* main.c */
/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
/* USER CODE END Includes */

/* USER CODE BEGIN PV */
// 소프트웨어 지연
void delay(uint32_t count)
{
	for(;count!=0;count--);
}

TaskHandle_t Task1_Handle;
#define TASK1_STACK_SIZE                    128
StackType_t Task1Stack[TASK1_STACK_SIZE];
TCB_t Task1TCB;
UBaseType_t Task1Priority = 2;

TaskHandle_t Task2_Handle;
#define TASK2_STACK_SIZE                    128
StackType_t Task2Stack[TASK2_STACK_SIZE];
TCB_t Task2TCB;
UBaseType_t Task2Priority = 2;

// 작업 1 진입 함수
void Task1_Entry(void *parg)
{
	for(;;)
	{
		HAL_GPIO_TogglePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin);
		delay(10000000);
	}
}
// 작업 2 진입 함수
void Task2_Entry(void *parg)
{
	for(;;)
	{
		HAL_GPIO_TogglePin(ORANGE_LED_GPIO_Port, ORANGE_LED_Pin);
		delay(10000000);
	}
}
/* USER CODE END PV */

/* USER CODE BEGIN 2 */
// 작업 1 및 2 생성
Task1_Handle = xTaskCreateStatic((TaskFunction_t)Task1_Entry,
								 (char *)"Task1",
								 (uint32_t)TASK1_STACK_SIZE,
								 (void *)NULL,
								 (UBaseType_t)Task1Priority,
								 (StackType_t *)Task1Stack,
								 (TCB_t *)&Task1TCB);
														
Task2_Handle = xTaskCreateStatic((TaskFunction_t)Task2_Entry,
								 (char *)"Task2",
								 (uint32_t)TASK2_STACK_SIZE,
								 (void *) NULL,
								 (UBaseType_t)Task2Priority,
								 (StackType_t *)Task2Stack,
								 (TCB_t *)&Task2TCB );
// 작업 스케줄러 시작, 영구히 리턴하지 않음
vTaskStartScheduler();
/* USER CODE END 2 */

configUSE_TIME_SLICING을 0으로 설정하고 프로그램을 로드한 후, 논리 분석기를 사용하여 두 LED의 핀 전압을 캡처합니다. 결과는 다음과 같습니다.

동일한 우선순위의 두 작업이 연속적으로 실행되므로, 시간 조각 할당이 비활성화된 경우, 작업 Task2만 실행됩니다 (왜 Task2인지?)

configUSE_TIME_SLICING을 1로 설정하고 다시 프로그램을 로드한 후, 논리 분석기를 사용하여 두 LED의 핀 전압을 캡처합니다. 결과는 다음과 같습니다.

우선순위가 같고 연속적으로 실행되는 작업은 거의 동시에 실행되는 것을 볼 수 있습니다. 이는 각 시간 조각(타이머 간격)마다 작업 스케줄링이 발생하기 때문입니다.

3.2. 개선할 부분

현재 RTOS 간단한 커널에서 구현된 기능은 다음과 같습니다.

  1. 정적 방식으로 작업 생성
  2. 수동 작업 전환
  3. 임계 구간 보호
  4. 작업 지연 차단
  5. 작업 우선순위 지원
  6. 차단 목록
  7. 시간 조각 할당

향후 RTOS 간단한 커널에서 추가할 수 있는 기능은 다음과 같습니다.

  1. 작업 간 통신 메커니즘
  2. 세마포어
  3. 상호배제 락
  4. 메시지 큐
  5. ...
  6. 기타 기능
  7. 소프트웨어 타이머
  8. ...

태그: freertos 작업 스케줄링 시간 조각 할당 RTOS 커널 구현

8월 21일 06:18에 게시됨