태스크 컨트롤 블록은 태스크가 선점될 때 해당 태스크의 상태를 유지하기 위해 사용되는 데이터 구조체다. uC/OS-II는 태스크를 생성할 때 이 태스크 컨트롤 블록을 할당한다. 태스크가 다시 CPU 제어권을 가질 때 태스크 컨트롤 블록에 저장했던 데이터를 이용해서 마지막으로 실행했던 부분부터 코드를 정확히 재개할 수 있다.
typedef struct os_tcb {OS_STK *OSTTCBStkPtr; - (1)
#if OS_TASK_CREATE_EXT_EN > 0void *OSTCBExtPtr; - (2)
OS_STK *OSTCBStkBottom; - (3)
INT32U OSTCBStkSize; - (4)
INT16U OSTCBOpt; - (5)
INT16U OSTCBId; - (6)
#endifstruct os_tcb *OSTCBNext; - (7)
struct os_tcb *OSTCBPrev; - (7)
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) (OS_MBOX_EN > 0) (OS_SEM_EN > 0) (OS_MUTEX_EN > 0) OS_EVENT *OSTCBEventPtr; - (8)
#endif#if (OS_Q_EN > 0) && (OS_MAX_QS > 0) (OS_MBOX_EN > 0) void *OSTCBMsg; - (9)
#endif#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)#if OS_TASK_DEL_EN > 0OS_FLAG_NODE *OSTCBFlagNode; - (10)
#endifOS_FLAGS OSTCBFlagRdy; - (11)
#endifINT16U OSTCBDly; - (12)
INT8U OSTCBStat; - (13)
INT8U OSTCBPrio; - (14)
INT8U OSTCBX; - (15)
INT8U OSTCBY; - (15)
INT8U OSTCBBitX; - (15)INT8U OSTCBBitY; - (15)
#if OS_TASKBOOLEAN OSTCBDelReq; - (16)
#endif} OS_TCB;(1) .OSTCBStkPtr
현재 태스크가 마지막으로 사용한 스택의 위치를 가리키는 포인터이다. uC/OS-II에서 모든 태스크는 자신이 사용하는 스택을 임의의 크기로 가질 수 있다. .OSTCBStkPtr은 문맥전환 시 어셈블리 코드에서 직접 엑세스해야 하는 유일한 필드이다. 이를 위해 구조체의 첫번째 위치에 있다.
(2) .OSTCBExtPtr
사용자 정의 태스크 컨트롤 블록을 가리키는 포인터이다. 이 필드를 적절히 사용하면 uC/OS-II의 소스코드를 수정하지 않고 태스크 컨트롤 블록을 사용자 용도에 맞게 확장할 수 있다. OSTaskCreateExt() 함수만 .OSTCBExtPtr 필드를 사용한다. 따라서 이 필드를 사용하려면 OS_TASK_CREATE_EXT_EN 매크로를 1로 설정해야 한다. 이 필드를 사용하면 태스크의 이름이나 태스크가 CPU를 할당 받아 받아 실행한 시간, 문맥전환이 일어난 회수 등의 정보를 담는 자료구조를 사용자가 정의해서 사용할 수도 있을 것이다.
(3) .OSTCBStkBottom
태스크가 사용하는 스택영역의 끝을 가리키는 포인터. 스택이 낮은 주소 방향으로 커지는 시스템의 경우라면, 스택으로 할당받은 메모리의 가장 낮은 번지를 가리킨다. 반면에 스택에 데이터를 저장할 때 스택 포인터 값이 커지는 시스템에서는 메모리의 가장 높은 주소를 가리키게 된다.
OSTaskStkChk() 함수가 사용된 스택의 크기를 동적으로 조사하기 위해 이 필드를 사용한다. 이 필드를 통해 각 태스크에게 할당한 스택공간 중 사용하지 않는 공간이 얼마나 되는지 알아낼 수 있다. 스택 검사기능은 태스크를
OSTaskCreateExt() 함수로 생성할 경우에만 사용할 수 있다. 따라서 스택 검사기능을 사용하려면 OS_TASK_CREATE_EXT_EN 매크로도 1로 선언되어 있어야 한다.
(4) .OSTCBStkSize
스택의 크기를 바이트 단위가 아니라 항목개수 단위로 나타내는 필드. 즉 32bit 시스템에서 .OSTCBStkSize가 1,000일 경우 실제 스택의 크기는 4,000bytes가 되고, 16bit 시스템일 경우엔 2,000bytes 크기의 스택을 의미한다. .OSTCBStkSize 필드도
OSTaskStkChk() 함수가 사용하는 필드이다. 역시 OS_TASK_CREATE_EXT_EN 매크로가 1로 선언되어 있어야 한다.
(5) .OSTCBOpt
OSTaskCreateExt()를 써서 태스크를 생성할 경우, 옵션에 해당하는 값을 보관하는 필드. 따라서 OS_TASK_CREATE_EXT_EN 매크로가 1로 선언되어 있어야 한다. 현재 uC/OS-II에서는 3개의 옵션을 정의하고 있다.
* OS_TASK_OPT_STK_CHK생성하는 태스크에 대해 스택 검사 기능을 사용한다는 거을 OSTaskCreateExt()에게 알린다.
스택 점검은 응용프로그램에서 OSTaskStkChk()를 호출해서 수행한다. * OS_TASK_OPT_STK_CLR태스크를 생성할 때 스택 공간을 0으로 클리어하라는 것을 의미한다.(수행시간이 길어질 수도 있다. ) 스택 검사기능을 사용할 경우엔 꼭 이 옵션을 활성화해서 스택을 0으로 클리어해야 한다. 이 옵션을 지정하지 않고 태스크의 생성/삭제 과정을 반복한다면 스택 사용량에 대해 잘못된 계산 결과가 나올 것이다. 하지만 일단 생성된 태스크를 삭제하지 않고, 시작코드에서 모든 램 영역을 0으로 클리어 해준다면, 이 옵션을 생략해서 실행시간을 절약할 수 있다.
* OS_TASK_OPT_STK_SAVE_FP태스크가 부동 소수점 연산기능을 사용할 경우 사용하는 옵션이다. 하드웨어적으로 부동 소수점 연산을 지원하는 코프로세서를 사용한다면, 문맥전환이 발생할 때 코프로세서의 레지스터들도 문맥에 포함하도록 처리해야 한다.
(6) .OSTCBId
태스크의 ID를 나타낸다. 현재 사용하지 않는 필드이며 추후 확장을 위해 정의되어 있다.
(7) .OSTCBNext / .OSTCBPrev
커널 내부에서 생성된 태스크 컨트롤 블록을 이중 연결 리스트로 관리하기 위해 사용하는 필드. 이 연결 리스트는
OSTimeTick()에서 태스크의 .OSTCBDly 값을 갱신하기 위해 사용한다. 태스크가 생성되면 태스크 컨트롤 블록이 할당되어 연결 리스트에 삽입되고, 태스크가 삭제되면 연결 리스트에서도 삭제된다. 이중 연결 리스트를 사용함으로써 빠르게 삽입/삭제 연산을 수행할 수 있다.
(8) .OSTCBEventPtr
이벤트 컨트롤 블록을 가리키는 포인터.
(9) .OSTCBMsg
태스크로 보낸 메시지를 가리키는 포인터.
(10) .OSTCBFlagNode
이벤트 플래그 노드를 가리키는 포인터. 이 필드는 이벤트 플래그 그룹에서 대기 중인 태스크를 삭제할 때
OSTaskDel() 함수가 사용한다. OS_FLAG_EN 매크로를 1로 설정해야 OS_TCB멤버로서 존재한다.
(11) .OSTCBFlagRdy
태스크가 이벤트 플래그 그룹에서 대기 중인 태스크를 준비상태로 만든 이벤트 플래그를 포함한다. OS_FLAG_EN 매크로를 1로 설정해야 OS_TCB멤버로서 존재한다.
(12) .OSTCBDly
태스크를 일정 틱 동안 지연할 필요가 있거나 타임아웃과 함께 이벤트를 기다릴 때 사용하는 필드. 지연 또는 대기 시간 틱 값을 갖는다. 이 값이 0일 경우, 태스크는 지연되지 않는다. 또는 이벤트 발생을 기다리는 경우라면 타임아웃을 사용하지 않은 경우다.
(13) .OSTCBStat
태스크의 상태를 나타내는 필드. 이 값이 OS_STAT_READY이면, 태스크가 실행 준비상태라는 것을 의미한다.
(14) .OSTCBPrio
태스크의 우선순위를 나타내는 필드. 높은 우선순위의 태스크일수록 낮은 .OSTCBPrio 값을 갖는다.
(15) .OSTCBX / .OSTCBY / .OSTCBBitX / .OSTCBBitY
태스크가 실행될 수 있는 상태임을 표시하거나 이벤트를 기다리는 상태라는 것을 판단하는 연산을 빠르게 수행하기 위해 사용하는 필드(실행 시에 이 값을 다시 계산하지 않도록, 미리 계산해서 태스크 컨트롤 블록 안에 저장해둠). 이 값은 태스크를 생성할 때 계산해서 저장하고, 또 태스크의 우선순위를 변경할 때 다시 계산한다.
.OSTCBX = priority >> 3;
.OSTCBY = OSMapTbl[priority >> 3] ;
.OSTCBBitX = priority & 0x07;
.OSTCBBitY = OSMapTbl[priority & 0x07];
(16) .OSTCBDelReq
태스크가 삭제 요청을 받았는지 여부를 나타내는 필드. OS_TASK_DEL_EN 매크로를 1로 설정해야 OS_TCB 맴버로 존재한다.
응용프로그램에서 사용할 수 있는 최대 태스크의 개수(OS_MAX_TASKS)는 OS_CFG.H 파일에 정의한다. 즉 uC/OS-II에서 최대 몇 개의 태스크 컨트롤 블록을 할당하도록 되있느지 OS_MAX_TASKS 값으로 알 수 있다. 이 값을 응용프로그램에서 실제 필요한 태스크의 개수만큼만 정의해서 메모리를 절약할 수 있다.
모든 태스크 컨트롤 블록은 OSTCBTbl[] 배열로 할당된다. uC/OS-II에서는 시스템 내부적인 사용을 위해 OS_N_SYS_TASKS 만큼의 태스크 컨트롤 블록을 더 할당한다. 현재는 IDLE 태스크와 통계 태스크가(OS_CFG.H 파일에 정의된 OS_TASK_STAT_EN이 1로 설정된 경우) 시스템 태스크에 해당한다. uC/OS-II 초기화 과정에서 OSTCBTlb[] 배열 내의 모든 태스크 컨트롤 블록은 자유 리스트에 단일 링크드 리스트 형태로 삽입된다. 태스크 생성을 위해 태스크 컨트롤 블록 할당이 필요할 경우,
OSTCBFreeList로 유지되는 자유 리스트에서 태스크 컨트롤 블록 하나를 꺼내서 할당해 주고, 태스크가 삭제되면 해당 컨트롤 블록은 다시 자유 리스트에 삽입된다.
OS_TCB는 태스크 생성 시 OS_TCBInit() 함수가 초기화 한다. OS_TCBInit()은 OSTaskCreate() 또는 OSTaskCreateExt() 함수가 호출한다. OS_TCBInit() 함수는 다음 7개의 전달인자를 받는다.
prio 태스크 우선순위ptos OSTaskStkInit() 함수가 초기화한 스택 프레임의 시작 위치를 가리키는 포인터..OSTCBStkPtr 필드에 저장.pbos 스택의 끝부분 위치를 가리키는 포인터..OSTCBStkBottom 필드에 저장.id 태스크 식별자..OSTCBId 필드에 저장.stksize 스택의 전체 크기..OSTCBExtPtr 필드에 저장.pext OS_TCB의 .OSTCBExtPtr 필드에 두기 위한 포인터.opt 선택사항을 알려주는 값..OSTCBOpt 필드에 저장.INT8U OS_TCBInit(INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id,
INT32U stk_size, void *pext, INT16U opt)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_TCB *ptcb;
OS_ENTER_CRITICAL();
ptcb = OSTCBFreeList();
if (ptcb != (OS_TCB *) 0) {
- (1)OSTCBFreeList = ptcb -> OSTCBNext;
OS_EXIT_CRITICAL();
- (2)ptcb -> OSTCBStkPtr = ptos;
ptcb -> OSTCBPrio = (INT8U) prio;
ptcb -> OSTCBStat = OS_STAT_RDY;
ptcb -> OSTCBDly = 0;
#if OS_TASK_CREATE_EXT_EN > 0
- (3)ptcb -> OSTCBExtPtr = pext;
ptcb -> OSTCBStkSize = stk_size;
ptcb -> OSTCBStkBottom = pbos;
ptcb -> OSTCBOpt = opt;
ptcb -> OSTCBId = id;
#else
pext = pext;
stk_size = stk_size;
pbos = pbos;
opt = opt;
id = id;
#endif
#if OS_TASK_DEL_EN > 0
ptcb -> OSTCBDelReq = OS_NO_ERR;
- (4)#endif
ptcb -> OSTCBY = prio >> 3;
- (5)ptcb -> OSTCBBitY = OSMapTbl [ptcb -> OSTCBY];
ptcb -> OSTCBX = prio & 0x07;
ptcb -> OSTCBBitX = OSMapTbl [ptcb -> OSTCBX];
#if OS_EVENT_EN > 0
ptcb -> OSTCBEventPtr = (OS_EVENT *) 0;
-(6)#endif
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) &&
(OS_TASK_DEL_EN > 0)
ptcb -> OSTCBFlagNode = (OS_FLAG_NODE *) 0;
-(7)
#endif
#if OS_MBOX_EN (OS_Q_EN && (OS_MAX_QS >= 2))
ptcb -> OSTCBMsg = (void *) 0;
#endif
#if OS_VERSION >= 204
OSTCBInitHook(ptcb);
#endif
OSTaskCreateHook(ptcb);
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = ptcb;
-(8)
ptcb -> OSTCBNext = OSTCBList;
ptcb -> OSTCBPrev = (OS_TCB *) 0;
if (OSTCBList != (OS_TCB *)0) {
OSTCBList -> OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
OSRdyGrp = ptcb -> OSTCBBitX;
OSRdyTbl [ptcb -> OSTCBY] = ptcb -> OSTCBBitX;
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
-(9)
}
OS_EXIT_CRITICAL();
return (OS_NO_MORE_TCB);
}
(1) OS_TCBInit() 함수는 우선 자유 OS_TCB 풀로부터 1개의 태스크 컨트롤 블록을 받기 위해 시도한다.
(2) 자유 풀에 사용할 수 있는 태스크 컨트롤 블록이 있을 경우 이 블록을 초기화 한다. 일단 OS_TCB가 할당되면 OS_TCBInit() 함수를 호출한 태스크 생성자가 OS_TCB를 완전히 소유한 것이고, 다른 곳에서 태스크를 생성하려고 하더라도 데이터 구조체가 손상될 염려가 없기 때문에 인터럽트를 활성화할 수 있다는 점을 주목하자. 따라서 OS_TCBInit() 함수는 인터럽트를 활성화 한 상태에서 OS_TCB의 몇몇 필드를 초기화 할 수 있다.
(3) OSTaskCreateExt() 함수에 관련된 코드를 생성하도록 OS_CFG.H의 OS_TASK_CREATE_EXT_EN 매크로를 1로 설정했다면 OS_TCB 추가 필드로 함께 초기화 한다.
(4) OS_CFG.H 파일의 OS_TASK_DEL_EN 매크로 설정에 따라 OS_TCB의 .OSTCBDelReq 필드 존재 여부도 달라진다. 즉 태스크를 삭제할 일이 없다면 이 매크로를 비활성화 해서 .OSTCBDelReq 필드를 제외시켜 OS_TCB 저장영역을 절약할 수 있다.
(5) 스케쥴링 시 계산시간을 절약하기 위해 OS_TCBInit() 함수는 몇몇 필드를 미리 계산해 둔다. OS_TCB의 저장영역은 커지지만 계산 시간을 절약하기 위해 이 필드를 정의한다.
(6) 응용 프로그램이 세마포어, 뮤텍스, 메시지 메일박스, 메시지 큐를 사용하지 않는다면 OS_TCB에 .OSTCBEventPtr 필드를 둘 필요가 없다.
(7) 이벤트 플래그를 활성화 했다면 (OS_CFG.H의 OS_FLAGS_EN = 1) 이벤트 플래그 노드를 가리키는 포인터는 아무것도 가리키지 않도록 초기화된다. 태스크가 처음 생성되면 이벤트 플래그를 기다리지 않기 때문이다.
(8) OS_TCBInit() 함수는 새로 생성한 OS_TCB를 이중 링크드 리스트에 삽입할 때 인터럽트를 비활성화한다. 리스트는 OSTCBList에서 시작하며 새 태스크의 OS_TCB는 항상 리스트 앞에 삽입된다.
(9) 마지막으로 태스크는 실행 준비상태가 된다. 그리고 OS_TCBInit() 함수는 OS_TCB를 할당해서 초기화했다는 코드 값을 가지고 호출자(OSTaskCreate() 또는 OSTaskCreateExt())로 리턴한다.
출처> uC/OS-II 실시간 커널....