본문 바로가기
코스웨어/12년 내장형하드웨어

[Micro C/OS] 10.29 업무일지 -정철

by 알 수 없는 사용자 2012. 10. 29.
728x90
반응형

새로시작한 리얼타임 오퍼레이션 시스템!


오늘 수업은 약간의 리뷰성 수업이었습니다.

RTOS의 개념과 어떤 문제점을 가지며 어떤식으로 해결해 나가는지,

초기화내용 등등에 대하여 알아 보았습니다.

오늘 쌤이 달리는 바람에 내용이 좀 많습니다.


* 전경/배경 시스템


소형이면서 복잡하지 않은 일반적인 시스템. (슈퍼 루프)



* 태스크


:운영체제가 제어하는 프로그램의 기본단위.


각 태스크는 5가지의 상태를 가진다.


1. 수면상태 : 메모리에 올라가 있지만, 커널에 등록되지 않아 실행할 수 없는 상태

2. 준비상태 : 실행될 수 있지만, 우선순위가 더 높은 태스크가 현재 실행되는 상태

3. 실행상태 : 태스크가 CPU를 점유해서 실행되고 잇는 상태

4. 대기상태 : 특정 이벤트를 기다리는 상태

5. 인터럽트 서비스 루틴 상태 : 인터럽트가 발생하여 CPU가 인터럽트 서비스 루틴을 수행하고 있는 상태



각 태스크는 각자의 tcb(task control block)을 가지고 있다.

이정보가 CPU레지스터에 들어가면 '실행되고 있다' 라고 한다.


태스크의 tcb가 cpu에 들어가있는 상태에서 다음 스케쥴링에 의해 현재정보를 tcb에 저장하고,

다음 태스크의 정보가 cpu에 들어가게된다.

이러한 동작이 빠르게 반복되면 멀티태스킹이 가능해진다.



* 문맥전환(Context Switch)


현재 cpu레지스터의 상태를 저장영역에 저장하고, 새로운 태스크의 상태를 cpu레지스터에 복귀하고 새로운 태스크의 코드를 실행하는 과정을

문맥전환이라고 한다.

문맥전환은 시스템에 오버헤드를 부과한다.


- 오버헤드

: 프로그램 수행에 반드시 필요한 것 이외의 준비과정과 마무리과정 등을 오버헤드라고 한다.

이를 테면 태스크의 상태를 cpu에 복귀키거나 저장하는 일등이 오버헤드에 해당된다.


단지 문맥전환을 많이 한다고해서 커널의 성능이 높다고 할 수는 없다.

(물론 동등한 상황에서 문맥전환이 빠르다면 성능이 높겠지...)



* 커널과 스케쥴링


커널은 멀티태스킹 시스템의 한부분. 태스크 관리와 그 태스크들 사이의 통신을 책임진다.

각 태스크의 고유한 스택영역이 필요하며, RAM을 쉽게 소비하게 된다.


스케쥴러는

1. 태스크에 우선순위를 부여하고,

2. 문맥전환을 한다.


커널에는 비선점형과 선점형 두 종류가 있다.


- 비선점형 커널

비선점형의 경우 ISR이 발생하면

다음 프로세서를 실행할 준비를 하고,

현재 프로세서가 수행을 끝낸뒤에

다음 프로세서를 실행한다.


- 선점형 커널

선점형의 경우 ISR이 발생하면

그 즉시 다음 프로세서를 실행하고,

프로세서가 수행을 완료하면, 이전 프로세서를 다시 수행한다.



* 재진입 가능 함수


전역변수에 접근할 경우 비재진입 함수라고한다. (지역변수만 접근하는 함수는 재진입 함수이다.)


swap()함수를 예로 들어보겠다.

x와 y의 값을 바꿔주는 swap함수가 있다.

값을 임시로 보관하는 temp변수가 전역 변수라면,


첫번째 실행중 -> 인터럽트 -> 두번째 함수 수행 -> 두번째 함수 수행 완료 -> 첫번째 함수 재 수행

의 순서로 흘렀다고 가정했을 떄,


int temp;


swap(int x, int y)    //x = 1, y = 2

{

temp = x;


-----------------------여기서 인터럽트


swap(int x, int y)    //x = 3, y = 4    -------두번째 함수 수행

{

temp = x;

x = y;

y = temp;


return 0;

}                                                --------두번째 함수 수행 종료

-----------------------여기서 복귀

x = y;

y = temp


return 0;

}


swap()함수가 두번째로 수행 결과는 x = 4, y = 3으로 원하는 결과를 얻게 되지만

swap()함수의 첫번째 수행 결과는 x = 2, y = 3으로 원하는 결과를 얻지 못하게 된다.


전역변수를 쓰면 동기화시 문제가 발생하게 된다.



* 라운드 로빈 스케줄링


2개 이상의 태스크가 같은 우선순위를 가지고 있을 때,

퀸텀이라 불리는 미리정해진 시간간격 동안 하나의 태스크를 실행한 뒤,

다른 한 태스크를 선택해서 실행한다.



* 태스크 우선순위


1. 정적 우선순위 : 응용프로그램을 실행하는 동안, 태스크의 우선순위를 바꿀 수 없는 경우.(static)

2. 동적 우선순위 : 응용프로그램을 실행하는 동안 태스크의 우선순위를 바꿀 수 있는 경우.(dynamic)


-우선순위의 전도


우선순위 전도란 말그대로, 우선순위를 갖는 Task들간의 우선순위가 역전되었다는 뜻이다.

예를 들어 설명하면,  우선순위의 숫자가 작을수록 우선순위가 높다고 할때, 우선순위 0의

Task0가, 이미 우선순위 3의 Task3가 획득한 세마포어에 대해 Obtain을 시도하는 경우,

당연히, Obtain이 실패하여 Task가 Block상태에 빠졌을때, Task0는 Task3보다 우선순위가

높음에도 불구하고, Task3가 세마포어를 Release할때까지 Block상태가 유지되는데,  이런 경우를

우선순위 전도라고 한다.


우선순위가 전도된 상황

태스크 3이 먼저 수행된다. 태스크 3이 공유자원에 세마포어를 획득한다.

이 와중에 우선순위가 높은 태스크 1이 수행된다.

태스크 1은 공유자원에 접근해야 하는데, 태스크 3이 세마포어를 획득했기 때문에,

다시 태스크 3이 수행된다.

이 때 태스크 2가 수행되면, 태스크 1은 블럭상태이고 태스크 3은 우선순위가 낮기 때문에

태스크 2가 수행된다.

태스크 2의 수행이 끝나면 세마포어를 획득한 태스크 3이 수행되고, 이후 태스크 1이 수행된다.


이경우 2 -> 3 -> 1의 순서로 수행을 하게 된다. 이런경우 우선순위가 전도되었다 라고 한다.


우선순위 상속을 지원하는 커널

위와 같은 케이스 이지만, 다른점은

태스크 2가 수행되기전에 우선 태스크 3이 수행을 끝내고, 우선순위가 높은 태스크 1도 수행을 끝낸뒤

태스크 2가 수행되기 시작한다.

이 같은 경우 우선순위 상속을 지원한다고 한다.



* 상호 배제


각 태스크들은 서로 정보를 교환하기 위해 가장 간단하게 전역변수를 이용한다.

하지만, 비재진입 함수의 케이스에서 보면,

프로그램이 원하는 결과를 얻지 못하는 경우가 생긴다. (각 태스크의 경쟁과 공유데이터 내용의 손상 등의 이유로)

이것을 해결하려면, 태스크가 데이터를 독점하게 해줘야 한다.

공유자원의 족점적인 엑세스를 얻기위해 다음과 같은 방법이 있다.


1. 인터럽트 비활성화

2. Test-And-Set 수행

3. 스케줄링 비활성화

4. 세마포어 사용


자세한건 나중에... (책에 설명은 P.50부터 참조하기 바람)



* 교착 상태


멀티프로세싱의 문제점이다.

두 태스크가 각자 다른 태스크에서 쓰고 있는 자원을 무한정 기다리는 상태를 말한다.



위와 같은 상황이 되면 태스크 둘다 자원을 무한정 기다리는 상태가 된다.

이를 피할 수 있는 방법은


1. 프로세스를 징행하기 전에 필요한 모든 자원을 획득한다.

2. 순서대로 자원을 획득한다.

3. 역순으로 자원을 양도한다.



* 태스크 컨트롤 블록



태스크가 생성되면 태스크 컨트롤 블록을 할당한다.

조건부 컴파일되어 있는 부분은 MC/OS가 제공하는 기능이 필요 없을 경우,

그 기능을 포함하지 않고 RAM을 절약하기 위해서이다.


태스크 컨트롤 블록의 empty 리스트


초기화 과정에서 빈 링크드 리스트에 단일 링크드 리스트 형태로 삽입된다.


OS_TCBInit()함수를 통해서 빈 링크드 리스트에서 하나를 빼온다.



* OS_TCBInit()함수


OS_TCBInit()함수는 7개의 전달인자를 받는다.


prio : 태스크의 우선순위

ptos : 스택 프레임의 시작위치를 가리키는 포인터

pbos : 스택의 끝부분 위치를 가리키는 포인터

id : 태스크 식별자

stk_size : 스택의 전체 크기

pext : OSTCBExtPtr 필드에 두기 위한 포인터

opt : 선택사항을 알려주는 값


1. 태스크 등록준비


태스크 컨트롤 블록의 빈 리스트에서 하나를 빼온다.


OS_TCB    *ptcb;


    OS_ENTER_CRITICAL();
    ptcb = OSTCBFreeList;                                  /* Get a free TCB from the free TCB list    */
    if (ptcb != (OS_TCB *)0) {
        OSTCBFreeList        = ptcb->OSTCBNext;            /* Update pointer to free TCB list          */
        OS_EXIT_CRITICAL();

이 소스의 경우
빈 OS_TCB 풀로 부터 1개의 태스크 컨트롤 블록을 받아온다.
이후 태스크를 쓰기 위해 값을 세팅하는 과정을 거친다.

2. 준비 리스트 등록

ptcb->OSTCBY         = prio >> 3;
ptcb->OSTCBBitY      = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX         = prio & 0x07;
ptcb->OSTCBBitX      = OSMapTbl[ptcb->OSTCBX];

이후 준비리스트를 만든다.

준비리스트는 스케줄링 시 계산시간을 절약하리 위해 미리 계산해 둔다.


만약 우선순위를 30번으로 둔다고 가정한다면,

prio에 30이 들어가 있을 것이다.

prio >> 3은 상위 3비트를 추출하기 위한 코드이고

prio & 0x07은 하위 3비트를 추출하기 위한 코드이다


ptcb->OSTCBBitY 에는 OSMapTbl[]배열을 참고하여 0000 1000 의 값이 들어간다. 0x08

ptcb->OSTCBBitX 에는 OSMapTbl[]배열을 참고하여 0100 0000 의 값이 들어간다. 0x40


이 값을 P.89 번의 표를 참고하면 표의 30번 준비리스트에

해당 태스크가 등록 된다는 것을 알 수 있다.


728x90