인프런 커뮤니티 질문&답변

research님의 프로필 이미지
research

작성한 질문수

FreeRTOS 프로그래밍

소스코드 분석-부트의 원리

taskr간 context switching 관련하여 질문이 있습니다.

해결된 질문

작성

·

366

2

안녕하세요 강사님.

 

컨텍트 스위칭 관련하여 궁금한점이 있어 질문드립니다.

아래와 같은 상황에서 우선 순위사 제일 높은 task를 컨텍트 스위칭 전에 cpu 자원을 사용하게 하고 싶습니다.

  1. task1 : 우선 순위 5 (suspend 상태)

  2. task2 : 우선 순위 4

  3. task3 : 우선 선위 4

우선 task2가 실행하던 도중 task1을 resume 하고 바로 portyield를 실행하게 되면 우선 순위가 동일한 task3번이 cpu를 점유하여 일을 처리하다가 컨텍트 스위칭이 발생하게 되면 task1이 cpu를 점유가게 될텐데

 

제가 궁금한건 task2에서 task1을 resume 한 후 컨택트 스위칭이 발생하기 전에 cpu를 task1이 점유하게 하는 방법이 없는지 궁금합니다.

 

감사합니다.

답변 1

0

홍영기님의 프로필 이미지
홍영기
지식공유자

안녕하세요. research님!

요청하신 질문 내용을 세심히 읽어보고, 제 생각을 샘플코드를 작성으로 표현해 보았습니다. 자세한 내용은 이 프로젝트 파일(다운로드)과 아래 그림을 함께 참고해주세요.

TaskMain's PRIO=5, Task1's PRIO=4, Task2's PRIO=4

Task1 이나 Task2 중 어떤 태스크가 임의의 시간에 휴면하고 있는 TaskMain 을 깨웁니다(90라인, 102라인 참고). TaskMain 은 깨어나서 콘솔 화면에 printf("[TaskMain:prio 5]I'm here\n"); 출력 후 정지합니다.

image

아래와 같이 예상된 결과가 나옵니다.

image

질문하신 내용을 다음과 같이 정리해드립니다.

첫째,

우선 순위사 제일 높은 task를 컨텍트 스위칭 전에 cpu 자원을 사용하게 하고 싶습니다

이것은 실현 가능하지 않습니다. 컨텍스트 전환은 태스크가 CPU 를 테이크하기 위한 필수 요소입니다. 그렇기 때문에 HPT(highest priority task) 가 컨텍스트 전환을 생략하고 CPU 를 사용하는 것은 가능하지 않습니다.

둘째,

portyield() 는 단순히 문맥전환을 실행하라는 기능으로만 작동하는 함수인데요. 흔히 사용하는 함수는 아니며, 어플리케이션에서 사용해도 되는 함수이기는 하지만 질문하신 목적으로는 효과를 보실 수가 없습니다. 그리고, 태스크의 운용 원리를 잘못 이해하고 계신 부분이 있는 것 같습니다. 75번 라인에서처럼 휴면하고 있는 HIGHEST PRIO TASK(TaskMain) 은 90라인이나 103라인에서의 vTaskResume() 함수의 효과에 의하여 즉각 다음과 같이 상태 전환이 연쇄적으로 일어납니다. BLOCK -> READY -> RUN. 즉 바로 실행된다는 뜻입니다. 다른 태스크(TASK3)가 TASK2 와 TASK1 사이를 비집고 들어올 수가 없다는 뜻이죠.

질문하신 문장을 교정해보면,

'우선 task2가 실행하던 도중 task1을 resume 하고 바로 portyield를 실행하게 되면 우선 순위가 동일한 task3번이 cpu를 점유하여 일을 처리하다가 컨텍트 스위칭이 발생하게 되면 task1이 cpu를 점유가게 될텐데'

이 문장 표현을 이렇게 바꾸어 봅니다

"우선 task2가 실행하던 도중 task1을 resume 하면 굳이 portyield() 실행 없이도 즉각 task1 이 task2 을 선점하여 실행을 시작한다"

 

홍영기님의 프로필 이미지
홍영기
지식공유자

혹시 제가 질문을 잘못 이해하고 답변드린 부분이 있다면 지적해주세요.

research님의 프로필 이미지
research
질문자

아.. 제가 잘못 생각하고 있었던거 같습니다.

GPIO를 이용하여 오실로스코프로 task간 전환 테스트를 진행하고 있었는데

HAL_Delay(); 함수를 이용하는 바람에 타이밍이 맞지 않아서 잘못 생각하고 있었습니다.

 

RTOS를 설정하면 HAL_Delay(1) 함수가 1ms로 동작하지 않고 1.5~ 2ms 정도로 동작을 하는 바람에 잘못 생각하고 있었습니다.

 

HAL_Delay()를 삭제하고 테스트를 해보니 강사님 말씀대로 task2에서 task1을 resume시키니12us 정도 후에 task1이 실행되는걸 확인하였습니다.

12us 는 강의에서 말씀해주신 task간 전환 비용으로 생각됩니다.

 

#define TASK_MAIN_PRIO   5
#define TASK_TASK1_PRIO  4
#define TASK_TASK2_PRIO  4
#define TASK_TASK3_PRIO  2

TaskHandle_t xHandleMain, xTask1Handle, xTask2Handle;

void TaskMain(void const *pvParameters)
{
  while(1)
  {
    for(uint8_t i=0; i<5; i++)
    {
      HAL_GPIO_TogglePin(GPIO5_BEAD_GPIO_Port, GPIO5_BEAD_Pin);
    }

    vTaskSuspend(xHandleMain);
  }
}

void vTask1(void const *pvParameters)
{
  while(1)
  {
    for(uint8_t i=0; i<5; i++)
    {
      HAL_GPIO_TogglePin(GPIO4_BEAD_GPIO_Port, GPIO4_BEAD_Pin);
    }
    vTaskResume(xHandleMain);
    for(uint8_t i=0; i<5; i++)
    {
      HAL_GPIO_TogglePin(GPIO2_BEAD_GPIO_Port, GPIO2_BEAD_Pin);
    }
  }
}

void vTask2(void const *pvParameters)
{
  while(1)
  {
    HAL_GPIO_TogglePin(GPIO3_BEAD_GPIO_Port, GPIO3_BEAD_Pin);
  }
}


extern "C" {

void vApplicationIdleHook(void)
{
  while(1)
  {
    HAL_GPIO_TogglePin(GPIO2_BEAD_GPIO_Port, GPIO2_BEAD_Pin);
  }
}

}


void userThread(void)
{
 xTaskCreate((TaskFunction_t)TaskMain,
             "TaskMain",
             128,
             NULL,
             TASK_MAIN_PRIO,
             &xHandleMain);

 xTaskCreate((TaskFunction_t)vTask1,
              "vTask1",
              128,
              NULL,
              TASK_TASK1_PRIO,
              &xTask1Handle);

 xTaskCreate((TaskFunction_t)vTask2,
               "vTask2",
               128,
               NULL,
               TASK_TASK2_PRIO,
               &xTask2Handle);

}
  1. 최초 TaskMain이 실행

    • GPIO5 5번 토굴

    • suspend 상태로 진입

  2. TaskMain이 suspend 상태로 진입하였기 때문에 Task1 , Taks2 2개중 1개가 실행되어야함 이때 xtaskCreate()함수에서 task1을 먼저 생성하였기 때문에 task1이 실행됨

  3. task1에서 TaskMain을 resume 하였기 때문에 GPIO4번을 5번 토굴 후 TaskMain 실행

    1. 전환 비용 12us 정도 소요

  4. TaskMain에서 GPIO5번 토굴 후 suspend

  5. Task1이 한번 실행되었기 때문에 다음 순서인 Task2가 실행

  6. Task2 1ms 실행

    1. #define configTICK_RATE_HZ ((TickType_t)1000)

  7. Task1이 resume 작업 후인 GPIO2번 토굴 부터 다시 진행

이렇게 실행이 됩니다.

 

답변 감사합니다.

research님의 프로필 이미지
research

작성한 질문수

질문하기