RealViewPB에서 Task Control Block (TCB) 설계 및 제어 개념
RealViewPB에서 태스크를 관리하기 위해 **Task Control Block (TCB)**를 사용하며, 이를 기반으로 태스크의 상태, 레지스터 컨텍스트, 스택 정보 등을 관리해야 한다.
1. Task Control Block (TCB)란?
TCB는 각 태스크를 관리하는 자료구조이다.
운영체제가 여러 개의 태스크를 관리하고, **태스크 간 전환(Context Switch)**이 가능하도록 하기 위해 사용된다..
🔹 TCB의 주요 구성 요소:
- 컨텍스트(Context)
- 실행 중인 태스크의 현재 상태를 저장 (CPU 레지스터 값)
- spsr, r0-r12, pc, sp 등이 포함됨
- 태스크 정보
- 태스크 ID, 태스크 이름, 우선순위 정보 저장
- 스택 정보
- 개별 태스크의 스택 포인터 (sp)
- 태스크의 스택 베이스 주소
작업 순서는 다음과 같다.
1) TCB 정의
2) 인스턴스 선언 추가
3) API 선언 및 정의
4) 사용
우선 한 테스크에는 레지스터들과 메모리가 존재한다.
레지스터는 KernelTaskContext_t에 정의되어있고, 관리하기 위한 sp와 메모리 주소는 KernelTcb_t에 정의되어있다.
#ifndef KERNEL_TASK_H_
#define KERNEL_TASK_H_
#include "MemoryMap.h"
#define NOT_ENOUGH_TASK_NUM 0xFFFFFFFF
#define USR_TASK_STACK_SIZE 0x100000
#define MAX_TASK_NUM (TASK_STACK_SIZE / USR_TASK_STACK_SIZE)
typedef struct KernelTaskContext_t
{
uint32_t spsr;
uint32_t r0_r12[13];
uint32_t pc;
} KernelTaskContext_t;
typedef struct KernelTcb_t
{
uint32_t sp;
uint8_t* stack_base;
} KernelTcb_t;
typedef void (*KernelTaskFunc_t)(void);
void Kernel_task_init(void);
uint32_t Kernel_task_create(KernelTaskFunc_t startFunc);
#endif
이때, tcb의 stack_base는 base address를 의미하기에 해당 Task의 가장 낮은 주소를 지칭하게 된다.
sp는 현재 stack pointer를 의미함으로 base address + USR_TASK_STACK_SIZE -4; 가 된다.
이렇게 tcb를 초기화 한 이후, tcb의 레지스터를 초기화해야한다.
KernelTaskContext_t* ctx = (KernelTaskContext_t*)tcb.sp; 해당 코드는 tcb의 최상단 메모리를 KernelTaskContext_t
즉, 레지스터 형식으로인식하게 한다.
이를 통해 레지스터 형식으로 수정및 접근이 가능하다.
#include <stdint.h>
#include <stdbool.h>
#include "ARMv7AR.h"
#include "task.h"
static KernelTcb_t sTask_list[MAX_TASK_NUM];
static uint32_t sAllocated_tcb_index;
void Kernel_task_init(void)
{
sAllocated_tcb_index = 0;
for(uint32_t i = 0; i < MAX_TASK_NUM ; i++)
{
sTask_list[i].stack_base = (uint8_t*)(TASK_STACK_START + (i * USR_TASK_STACK_SIZE));
sTask_list[i].sp = (uint32_t)sTask_list[i].stack_base + USR_TASK_STACK_SIZE - 4;
sTask_list[i].sp -= sizeof(KernelTaskContext_t);
KernelTaskContext_t* ctx = (KernelTaskContext_t*)sTask_list[i].sp;
ctx->pc = 0;
ctx->spsr = ARM_MOD_BIT_SYS;
}
}
uint32_t Kernel_task_create(KernelTaskFunc_t startFunc)
{
KernelTcb_t* new_tcb = &sTask_list[sAllocated_tcb_index++];
if(sAllocated_tcb_index > MAX_TASK_NUM)
{
return NOT_ENOUGH_TASK_NUM;
}
KernelTaskContext_t* ctx = (KernelTaskContext_t*)new_tcb->sp;
ctx -> pc = (uint32_t)startFunc;
return (sAllocated_tcb_index - 1);
}
현재는 스케쥴러와 컨텍스트 스위칭을 구현하지 못한 상태이기 때문에 더미 태스크로 구현하면 아래와 같다.
#include <stdint.h>
#include <stdbool.h>
#include "HalUart.h"
#include "HalInterrupt.h"
#include "HalTimer.h"
#include "task.h"
#include "stdio.h"
#include "stdlib.h"
static void Hw_init(void);
static void Kernel_init(void);
static void Printf_test(void);
static void Timer_test(void);
void User_task0(void);
void User_task1(void);
void User_task2(void);
void main(void)
{
Hw_init();
uint32_t i = 100;
while(i--)
{
Hal_uart_put_char('N');
}
Hal_uart_put_char('\n');
putstr("Hello World!\n");
Printf_test();
User_task0();
User_task1();
User_task2();
}
static void Hw_init(void)
{
Hal_interrupt_init();
Hal_timer_init();
Hal_uart_init();
Kernel_init();
}
static void Printf_test(void)
{
char* str = "printf pointer test";
char* nullptr = 0;
uint32_t i = 5;
uint32_t* sysctrl0 = (uint32_t*)0x10001000;
debug_printf("%s\n", "Hello printf");
debug_printf("output string pointer: %s\n", str);
debug_printf("%s is null pointer, %u number\n", nullptr, 10);
debug_printf("%u = 5\n", i);
debug_printf("dec=%u hex=%x\n", 0xff, 0xff);
debug_printf("SYSCTRL0 = %x\n",*sysctrl0);
}
static void Timer_test(void)
{
while(true)
{
debug_printf("currunt count : %u \n",Hal_timer_get_1ms_counter());
delay(1000);
}
}
static void Kernel_init(void)
{
uint32_t taskId;
Kernel_task_init();
taskId = Kernel_task_create(User_task0);
if(NOT_ENOUGH_TASK_NUM==taskId)
{
putstr("Task0 creation fail\n");
}
taskId = Kernel_task_create(User_task1);
if(NOT_ENOUGH_TASK_NUM==taskId)
{
putstr("Task1 creation fail\n");
}
taskId = Kernel_task_create(User_task2);
if(NOT_ENOUGH_TASK_NUM==taskId)
{
putstr("Task2 creation fail\n");
}
}
void User_task0(void)
{
debug_printf("User Task#0\n");
}
void User_task1(void)
{
debug_printf("User Task#1\n");
}
void User_task2(void)
{
debug_printf("User Task#2\n");
}
결과는 아래와 같다.
'SW 개발 공부 > OS 개발 프로젝트' 카테고리의 다른 글
[나빌로스] 10.컨텍스트 스위칭 (0) | 2025.04.02 |
---|---|
[나빌로스] 7.타이머 (0) | 2025.04.02 |
[나빌로스] 5-1. UART printf 구현하기 (0) | 2025.04.01 |
[나빌로스] 5. UART (0) | 2025.03.29 |