초심 프로젝트의 세번째 주제로 커널을 주제로 새롭게 알게된 것을 정리했다. 교재는 다음과 같다.
주교제 | MicroC/OS-II 실시간 커널 2판 |
커널 구조
MicroC/OS 의 가장 핵심적인 커널에 대해서 설명하고 있다.
준비 리스트
태스크를 생성하면, 가장 먼저 준비 리스트(Ready List) 에 등록이 된다. MicroC/OS 는 우선 순위 방식의 스케줄링 정책을 사용하기 때문에 우선 순위가 높은 태스크가 생성되면 곧바로 문맥전환(context switch)이 일어난다. 여기서 염두해야 할 것은 같은 우선순위를 가지는 태스크를 허용하지 않는 다는 점이다. 다시 말해 라운드로빈 스케줄링을 지원하지 않는다. 그리고 동시에 64 개의 태스크를 생성하고 실행할 수 있다.
MicroC/OS 에서는 준비 리스트를 사용하여, 현재 리스트에 어떤 우선순위를 가지고 있는 태스크가 있는지 알아낼 수 있다.
지금부터 하는 설명은 잘 이해가 안될 수 있다. 하지만 책과 내가 적은 설명을 곰곰히 생각해본다면, 이해할 수 있을 것이다.
MicroC/OS 는 준비 리스트에 있는 태스크들의 우선순위를 나타내기 위해서 OSRdyTbl[] 라고 하는 테이블을 가지고 있다.
이것은 각각 가로(8bit) X 세로(8bit) 로 이루어진 총 64bit(8byte)의 테이블이다. 각 테이블의 항목마다 숫자가 적혀있다. 여기서 숫자는 우선순위를 의미한다. MicroC/OS 는 우선 순위가 낮을 수록 높다.
OSRdyGrp |= OSMapTbl[prio >> 3]; // prio 는 태스크 생성 시의 우선순위, 오른쪽으로 3비트 쉬프트 OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07]; // OR 연산을 하는 이유는 같은 X 축 또는 Y 축에 태스크가 이미 있을 수 있기 때문
태스크가 생성되면, 앞서 설명한 OSRdyTbl 이라는 테이블과 OSRdyGrp 라고 하는 8bit 로 구성된 변수에 우선순위를 나타내기 위한 정보를 저장한다. (P.89)
예를 들어 우선 순위가 10(00001010) 인 태스크를 생성했다고 하자.
OSRdyGrp |= OSMapTbl[00000001]; // Y 축의 값 : 1 OSRdyTbl[00000001] |= OSMapTbl[00000010]; // X 축의 값 : 2
OSRdyGrp 의 0 번 비트부터 7 번 비트의 각각은 OSRdyTbl 테이블의 Y 축의 번호와 매핑된다. 이말은 LSB 로 갈 수록 우선순위가 높아진다는 것을 의미한다. (2,1) 이라면 10 이라는 우선순위를 갖는 태스크가 준비 리스트에 등록이 되는 것이다. 태스크 우선순위는 총 8bit 중에서 0-2 bit 는 X 축 인덱스, 3-5 bit 는 Y 축 인덱스를 나타낸다.
그렇다면 이제는 준비 리스트에서 가장 높은 우선순위를 가지는 태스크를 찾아보도록 하자!
찾기위해서는 X 축과 Y 축의 값을 찾아야 한다.
y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; prio = (y << 3) + x;
우선 결론 부터 얘기하자면, 위의 공식으로 구할 수 있다. 여기서는 ROM 에 저장되어 있는 OSUnMapTbl[] 이라고 하는 테이블이 필요하다.
INT8U const OSUnMapTbl[] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */ };
이 테이블에 나열된 숫자들이 어떤 의미를 가지고 있는지 궁금할 것이다. 16 X 16 배열이다. ROM 에 저장되어 있기 때문에 수정되지 않는다.
가장 높은 우선순위를 찾는 태스크를 찾는 것이 목표이기 때문에 OSRdyGrp 의 LSB 가 가장 중요하다.
예를 들어, OSRdyGrp 의 값이 '???????1' 이라면 LSB 가 bit 0 이기 때문에 가장 높은 우선순위를 가지는 태스크는 0 과7 사이의 우선순위를 가지고 있다고 할 수 있다. OSRdyGrp 의 값이 홀수의 경우에는
1(00000001) 3(00000011) 5(00000101) 7(00000111) 9(00010001) ... ...
이런식으로 FSB 에 어떤 값이 오더라도, LSB 가 bit 0 이기 때문에 다른 bit 는 무시된다. 이 때 가장 높은 우선순위를 가지는 태스크는 Y 축의 0 번 인덱스를 가진다. 그러므로 OSUnMapTbl 테이블에서 1,3,5,7,9,11,13,15… 번째 항목은 0 이 들어간다.
이번에는 '??????10' 이라고 하자. LSB 가 bit 1 이기 때문에 앞의 FSB 는 무시된다. 이 때 Y 축의 1번 인덱스를 가진다. OSRdyGrp 의 값이
2(00000010) 6(00000110) 10(00001010) 14(00001110) 18(00010010) ... ...
이렇듯 4 씩 증가하는 것을 알 수 있다. 그러므로 OSUnMapTbl 테이블에서 2,6,10,14,18… 번째 항목은 1 이 들어간다.
마지막으로 '10000000' 라고 하자. LSB 가 bit 7 이므로 Y 축의 7 번 인덱스를 가진다. OSRdyGrp 의 값이
128(10000000)
이렇듯 128 씩 증가하고 있다. OSUnMapTbl 테이블에서 128 번째 항목은 7 이 들어간다. 이런식으로 진행해 나가면, 앞에서 봤던 OSUnMapTbl 테이블을 완성할 수 있다.
이제 어떻게 최상위의 우선순위를 가지는 태스크를 찾아낼 수 있는지 알아보자
가장 먼저 OSRdyGrp 값을 읽어들여야 한다. 예를 들어 값이 01101000 이라고 하자. 이 값을 보고 LSB 가 1000 이므로 Y 축의 인덱스가 3 임을 알 수 있고, 또한 OSUnMapTbl 테이블에서 0x68(01101000) 번째 항목을 보면 3 이 매핑되는 것을 알 수 있다.
이번에는 X 축의 값을 알아볼 차례다. Y 축의 인덱스 값이 3 임을 알았기 때문에 세번째 인덱스에 해당하는 31,30,29…25,24 중에 가장 높은 우선순위를 가지는 태스크가 있을 것이다. 이 때 OSRdyTbl[3] 의 우선순위들(31-25) 저장된 값을 읽었을 때, 11100100(0xE4) 라는 값이 나왔다면, 현재 31,30,29,26 의 우선순위를 가지는 태스크가 준비 리스트에 등록이 되었음을 알 수 있다. 여기서 가장 높은 우선순위는 26 이므로 X 축의 인텍스는 2 라는 것을 알 수 있다. 또한 OSUnMapTbl 테이블에서 11100100(0xE4) 번째 항목을 보면 2 가 매핑 되어있다.
이제 X, Y 인덱스 값을 구했다. 이제 최종 우선순위를 알기위해 (3,2) 를 OSRdyTbl[] 테이블에 대입해보면, 우선순위가 26 임을 알 수 있다.
또한
3 = OSUnMapTbl[0x68]; 2 = OSUnMapTbl[0xE4]; 26 = (3 << 3) + 2;
이런 식으로도 구할 수 있다.