uC/OS-ii 기반의 멀티프로세서를 지원하는 Kernel 개발 ④
상태바
uC/OS-ii 기반의 멀티프로세서를 지원하는 Kernel 개발 ④
  • 황은정
  • 승인 2013.04.04 00:00
  • 댓글 0
이 기사를 공유합니다

 uC/OS-ii 기반의 멀티프로세서를 지원하는 Kernel 개발 ④ 이미 PC의 프로세서들은 듀얼 코어 이상의 멀티코어가 대중화 된지 오래이다. 심지어 스마트폰을 비롯한 임베디드 시장에서도 점차 듀얼 코어 이상의 멀티 프로세서 기술들이 도입되고 있다. 이를 통해 머지않아(혹은 이미) 멀티 코어 시스템 플랫롬이 시장의 주류가 될 것이라는 것은 쉽게 예상 할 수가 있다. 따라서, 본 창의과제를 통하여 멀티프로세서 환경에 대한 이해와 발생하는 이슈에 대해 알아보고, 이러한 시스템에서 동작하기 위해서 필요한 태스크 스케쥴링, 동기화 등을 제공하는 OS를 개발하기 위하여 선정하였다.  글 M.P.(정준호/한양대, 김운철/과기대, 이동욱/광운대)자료제공 임베디드소프트웨어산업협의회
   (1) 키보드초기화 일반적인 경우에는 키보드는 이미 BIOS에 의해 활성화된 상태이다. 따라서 키보드 활성화를 하는 단계를 굳이 수행하지 않아도 키보드에서 킷값을 읽는데 문제가 없다. 만약을 대비해 키보드를 활성했다. 일단 부팅을 하고나면 바로 키보드컨트롤러를 활성화 시켜 준다.
  다음의 블록 다이어그램처럼 처음에 0x64(쓰기모드)를 이용하여 0xAE(키보드디바이스활성화) 데이터를 전송한다. 이렇게 전송을 하면 키보드 활성화를 시켜주고 0xFA를 통해 키보드를 동작시켜 초기화를 할 수 있다.
 어셈블리어를 이용하여 포트 I/O 레지스터를 읽고 쓰기를 한다. 어셈블리어 함수를 만들어서 In과 Out을 한다. In 명령어는 포트 I/O어드레스를 지정하는데 DX레지스터를 사용하며 포트에서 값을 읽어 AX레지스터에 저장 한다. Out 명령어는 반대로 포트 I/O어드레스에 데이터를 출력하는 역할을 한다. 기능은 반대지만 포트 I/O어드레스에는 DX레지스터를 사용하며 값을 보내는 레지스터로는 AX레지스터를 사용한다.   키보드 초기화 kInPortByte: pushrdx ;함수에서 임시로 사용하는 레지스터를 스택에 저장    ;함수의 마지막 부분에서 스택에 사입된 값을 꺼내 복원
 movrdx,rdi ;RDX레지스터 에파라메터1를 저장 movrax,0 ;RAX레지스터를 초기화 inal,dx ;DX레지스터에 저장된 포트 어드레스에서 한 바이트를 읽어    ;AL레지스터에 저장
 poprdx  ;함수에서 사용이 끝난 레지스터를 복원 ret  ;함수를 호출한 다음 코드의 위치로.
kOutPortByte:    pushrdx       ;함수에서 임시로 사용하는 레지스터를 스택에 저장    pushrax       ;함수의 마지막 부분에서 스택에 삽입된 값을 꺼내 복원
    movrdx,rdi   ;RDX레지스터에 파라미터1(포트번호)를 저장    movrax,rsi   ;RAX레지스터에 파라미터2(데이터)를 저장    outdx,al     ;DX레지스터에 저장된 포트 어드레스에AL레지스터에 저장된                    ;한 바이트를 씀
    poprax        ;함수에서 사용이 끝난 레지스터를 복원      (2) 키보드 컨트롤러에서 키 값읽기 키보드는 키가 눌리거나 떨어질 때마다 키 별로 할당된 값을 키보드컨트롤러로 전달하며, 이 값을 스캔코드라고 한다. 키보드 컨트롤러의 출력 버퍼에는 키보드 또는 마우스에서 수신된 데이터가 저장된다. 따라서 상태레지스터를 읽어서 출력 버퍼에 데이터가 있는지 확인 후, 데이터가 있다면 출력 버퍼를 읽어서 저장하면 된다.    키보드 컨트롤러에서 키 값읽기 1 if(kInPortByte(0x64)& 0x01)   //키 데이터가 있다면,{        returnkInPortByte(0x60);  //그 값을 리턴 한다.}return0;     //값이 없다면0으로     다음과 같이 어셈블러를 이용하여 해당 상태레지스터를 보고 그 데이터를 리턴 받아서 출력하도록 한다.   키보드 컨트롤러에서 키 값읽기 2 while(1){ key_value=KeyScancode(); if(key_value!=0){     //키가 눌러 졌으면 키의ASCII코드 값을 화면에 출력  kPrintStirng(0,15,key_value); }}     다음과 같이 간단히 폴링 상태로 키보드에서 입력을 받아 key 데이터 즉 아스키 값을 출력을 할 수 있다. 이렇게 만들어진 키 데이터는 가장 기본적인 방식으로 while 루프 안에서 폴링 방식으로 체크를 한다. 이런 방식은 키가 지속적으로 걸리면 다른 테스크에 무리를 준다. 따라서 중간 발표 때에는 가장 간단하게 테스크 하나를 통해서 지속적으로 Key 값을 받아서 출력을 하였다. 하지만 이런 방식은 너무 비효율적 이므로 Keyboard 인터럽트를 통해서 키값을 받도록 한다.
 (3) Scan Code  Keyboard는 키가 눌리거나 떨어질 때마다 해당 키에 대한 고유 코드를 전송하며, 키 이벤트에 대한 고유코드를 Scan Code라고 한다. 




그림 8. Keyboard초기화표 8과 같이 Keyboard의 모든 키는 각자의 고유 코드를 가지고 있으며, Keyboard는 키가 눌리거나 떨어질 때마다 그 상태에 해당하는 키 값을 Keyboard Controller로 전송한다. Scan Code는 키가 눌렸을 때(Down)와 떨어졌을 때(Up)의 값이 다르며, 일반적으로 떨어졌을 때의 키 값은 눌러졌을 때의 값에 최상위 Bit(Bit 7)을 1로 설정한 값과 같다. 최상위 Bit를 1로 설정하는 것은 0x80을 더하는 것과 같으므로, 눌러졌을 때의 키 값에 0x80을 더하는 방식으로 처리하면 떨어졌을 때의 키 값을 가지고 있지 않아도 된다. 표 8에서 음영으로 표시한 부분은 확장 키(Extended Key)의 Scan Code를 표시한 것이다. 확장 키는 AT PC 시절에 사용되었던 Keyboard 이후에 추가된 키를 의미한다. 확장 키는 일반적으로 QWERTY 키 영역과 숫자 패드 사이에 있다. 확장 키는 일반 키와 달리 2개 이상의 Code로 구성되므로 처리할 때 주의해야 한다. 




 확장 키는 다른 키와 달리 0xE0이나 0xE1로 시작한다는 공통점이 있으므로, 0xE0이나 0xE1에 해당하는 Scan Code가 수신되었을 때 이를 판단해서 처리하면 된다. Scan Code에서 ASCII Code로의 변환을 용이하게 하기 위해 Scan Code의 값을 Table Index로 사용하여 ASCII 값을 구하는 변환 테이블을 만들어 사용하였다. 변환과정에서 주의해야 할 사항은 조합 키의 상태에 따라 영향을 받는 지의 여부와 확장 키를 처리하는 것이다.     ASCII Code 변환 알고리즘 if( 마지막 키가 Pause여서 나머지 Scan Code 2개를 무시해야 하는가? ){ Scan Code를 무시 종료}     if( Scan Code가 Pause의 첫 번째 코드(0xE1)인가? ){ 나머지 Scan Code 2개를 무시하도록 설정하고 Pause 코드를 반환 종료}else if( Scan Code가 확장 키의 첫 번째 코드(0xE0)인가? ){ 다음에 수신될 키가 확장 코드임을 저장 종료} if( Scan Code와 관계된 조함 키(Shift, Caps Lock, Num Lock)가 눌러졌는가? ){ Scan Code에 해당하는 ASCII 테이블의 조합 코드를 반환}else{ Scan Code에 해당하는 ASCII 테이블의 일반 코드를 반환}     if( 마지막 Scan Code가 확장 키의 첫 번째 코드(0xE0)였는가? ){ 속성 값에 확장 키 옵션 반환}     조합 키(Shift, Caps Lock, Num Lock, Scroll Lock)의 눌림, 떨어짐 상태, LED 상태 갱신종료      (4) Keyboard Interrupt Handler  Keyboard Interrupt가 발생하면, I/O APIC Redirection Table Entry 1에 설정한 Vector값에 해당하는 IRQ(Interrupt Request)가 수행된다. 이 IRQ는 os_cpu_c.c의 KeyHandler()로 구현되어 있다. IRQ는 Interrupt가 수행되는 동안 다른 Interrupt가 방해하지 않도록 Timer Interrupt를 Disable한다. (SiMPu에서는 Interrupt가 Timer와 Keyboard 두 개만 처리한다) 이후, Keyboard IRQ Handler 함수를 호출하여 필요한 작업을 수행한 뒤, EOI를 전달, Timer Interrupt를 Enable하고 IRQ를 종료한다. 
  Keyboard Interrupt Handler는 OS_Keyboard.c 에 GetKeyboardScanCode()에 구현되어 있다. Keyboard의 입력 값은 포트 0x60의 출력 버퍼에 저장된다. 이 값은 Keyboard 각 자판의 Scan Code값이다. 이 버퍼의 값을 읽어 ASCII Code로 변환하여 OS 내에 있는 Keyboard Queue에 삽입한다.(ConvertScanCodeAndPutQueue())  여기까지 수행되면, OS는 언제든 Queue에서 입력 받은 Keyboard 값을 불러올 수 있게 된다.
 (5) 모니터 드라이버  화면에 문자를 출력하려면 현재 동작중인 화면모드와 관련된 비디오 메모리의 어드레스를 알아야 한다. 비디오 메모리는 화면출력과 관계된 메모리로 모드 별로 정해진 형식에 따라 데이터를 채우면 화면에 원하는 문자나 그림을 출력하는 구조로 구성되어 있다.  PC부팅 후 기본으로 설정되는 화면모드는 텍스트모드로 화면크기는 가로 80, 세로 25 문자이며 비디오 메모리 어드레스는 0xB8000에서 시작한다. 또한, 화면에 표시하는 한 문자는 문자 값 1바이트와 속성값1바이트로 구성되어 있다. 따라서 화면크기를 곱한 크기는 80*25*2=4000바이트이다.
 속성값은 하위 4비트 전경색과 상위4비트 배경색으로 구별 된다. 각 전경색과 배경색은 다시 최상위의 특수기능비트와 하위 3비트의 색상으로 구분 된다. 전경색은 강조효과만 지원 하지만, 배경색은 강조와 깜빡임 두 기능을 한다.전경색과 배경색의 최상위 비트를 모두 강조기능으로 사용하면 16가지 색상으로 표현할 수 있으므로 이를 이용하면 그럴듯한 텍스트화면을 표현할 수 있으므로 사용하면 사용하도록 한다.
 이제 화면에 문자를 표시하려면 0xB8000 어드레스에 문자와 속성 순서대로 지정하면 된다. 이를 이용하여 맨위에 'A'을 빨간색 배경에 밝은 녹색으로 출력해 본다.   0xB8000어드레스에 문자와 속성 지정  mov ax, 0xB800  ;AX레지스터에0xB800복사 mov ds, ax  ;DS세그먼트레지스터에AX레지스터의값(0xB800)을복사 DS세그먼트레지스터에0xB800의 값을 설정했으니 각각'A',0x4A를 입력시켜준다.  mov byte[0x00], 'A' mov byte[0x01], 0x4A   커서는 모니터에 깜빡이는 '_' 문자로 표시되며 입력된 텍스트가 출력될 위치를 표시하기 위해 사용한다. 커서는 직접 '_' 문자를 출력하여 표현하는 것이 아니라 모니터 출력을 담당하는 VGA 컨트롤러가 담당한다. 그중 CRTC Controller Address Register와 CRTC Controller Data Register가 담당하며 각 I/O Port 0x3D4와 0x3D5를 사용한다. CRTC 컨트롤러 어드레스 레지스터(0x3D4)는 CRTC의 내부 레지스터를 지정한다. 커서의 위치를 제어하려면 0xE와 0xF를 전달하여 상위 커서 위치 레지스터와 하위 커서 레지스터를 먼저 선택하고 CRTC 컨트롤러의 데이터 레지스터(0x3D5)에 상위, 하위 바이트로 나누어 커서 위치를 전달하면 해당 위치로 커서가 이동하게 된다. 이때 CRT 컨트롤러는 컬럼과 라인 좌표가 아닌 비디오 메모리 내 오프셋을 사용한다.
 
  
그림 9. 텍스트 모드 화면구조 


  커서의 위치제어    void SetCursor( int iX, int iY ) {    int iLinearValue;     iLinearValue = iY * CONSOLE_WIDTH + iX; // Calculate Offset     // Send 0x0E to CRTC(0x3D4), Select upperbyte    outportb( VGA_PORT_INDEX, VGA_INDEX_UPPERCURSOR );    outportb( VGA_PORT_DATA, iLinearValue >> 8 );     // Send 0x0F to CRTC(0x3D4), select lowwerbyte    outportb( VGA_PORT_INDEX, VGA_INDEX_LOWERCURSOR );    outportb( VGA_PORT_DATA, iLinearValue & 0xFF ); gs_stConsoleManager.iCurrentPrintOffset = iLinearValue; // update offset} void GetCursor( int *piX, int *piY ){    *piX = gs_stConsoleManager.iCurrentPrintOffset % CONSOLE_WIDTH;    *piY = gs_stConsoleManager.iCurrentPrintOffset / CONSOLE_WIDTH;} // CONSOLE_WIDTH == 80     필요성 폴링 방식은 프로세서가 키보드 컨트롤러를 주기적으로 확인하므로 프로세서의 소모가 심하고 확인하는 주기에 비례하여 데이터가 수신되는 시간이 길어지는 문제점이 있다. 인터럽트 방식은 컨트롤러에 데이터가 있거나 데이터를 전송할 수 있을 때 인터럽트를 통해 이를 알려주므로 상대적으로 프로세서를 덜 사용하게 된다. 이런 차이는 멀티태스킹 환경에서 더욱 확연히 드러날 것이다. 그래서 최종적으로 키보드 디바이스를 인터럽트 방식으로 변경하려면 버퍼는 필수적으로 필요하게 된다. 왜냐하면 인터럽트는 발생 시점을 예측할 수가 없어 수신된 키 값을 온전히 전달하기가 어렵기 때문이다.
 큐를 다양한 목적으로 활용하기 위해 범용으로 사용할 수 있도록 구현하였다. 구현 시 주의사항은 데이터의 삽입, 제거 후 버퍼의 최대값을 초과하는 경우와 삽입, 제거 시 두 위치가 같아지는 경우이다. 전자는 환형 큐로 구현하여 간단히 해결하였고 후자는 삽입 후 위치가 같아졌는지 아니면 제거 후 위치가 같아졌는지 구분이 필요하다. 

그림 10. Keyboard Queue    환형 큐 구현 typedef struct QueueManagerStruct{        int iDataSize;     int iMaxDataCount;      void* pvQueueArray;    int iPutIndex;    int iGetIndex;     BOOL bLastOperationPut; // Full or Empty} QUEUE;  다음은 큐를 초기화, 데이터 삽입 및 제거, 내부 정보 확인을 위한 함수이다.    큐를 초기화, 데이터 삽입 및 제거, 내부 정보 확인을 위한 함수 void InitializeQueue( QUEUE* pstQueue, void* pvQueueBuffer, int iMaxDataCount,        int iDataSize ){   pstQueue->iMaxDataCount = iMaxDataCount;   pstQueue->iDataSize = iDataSize;   pstQueue->pvQueueArray = pvQueueBuffer; // Buffer Address    pstQueue->iPutIndex = 0;   pstQueue->iGetIndex = 0;   pstQueue->bLastOperationPut = FALSE; // Empty State}BOOL IsQueueFull( const QUEUE* pstQueue ){        if( ( pstQueue->iGetIndex == pstQueue->iPutIndex ) &&        ( pstQueue->bLastOperationPut == TRUE ) ) // Last operation - Input    {        return TRUE;    }    return FALSE;} BOOL IsQueueEmpty( const QUEUE* pstQueue ){     if( ( pstQueue->iGetIndex == pstQueue->iPutIndex ) &&        ( pstQueue->bLastOperationPut == FALSE ) ) // Last operation - Remove    {        return TRUE;    }    return FALSE;} BOOL PutQueue( QUEUE* pstQueue, const void* pvData ){    if( IsQueueFull( pstQueue ) == TRUE )    {        return FALSE;    }     MemCpy( ( char* ) pstQueue->pvQueueArray + ( pstQueue->iDataSize *            pstQueue->iPutIndex ), pvData, pstQueue            ->iDataSize ); // Dst, Src, Size     pstQueue->iPutIndex = ( pstQueue->iPutIndex + 1 ) % pstQueue->iMaxDataCount;    pstQueue->bLastOperationPut = TRUE; // Input    return TRUE;} BOOL GetQueue( QUEUE* pstQueue, void* pvData ){    if( IsQueueEmpty( pstQueue ) == TRUE )    {        return FALSE;    }         MemCpy( pvData, ( char* ) pstQueue         ->pvQueueArray + ( pstQueue->iDataSize *             pstQueue->iGetIndex ), pstQueue->iDataSize );     pstQueue->iGetIndex = ( pstQueue->iGetIndex + 1 ) % pstQueue->iMaxDataCount;    pstQueue->bLastOperationPut = FALSE; // Remove    return TRUE;}      Console Environment구현한 셀은 크게 세 부분으로 구성된다. 키보드에서 키를 입력 받아 커맨드 버퍼를 관리하고, 커맨드에 해당하는 프로그램을 실행하는 부분이다.(커맨드를 구분하여 해당 프로그램 실행) 텍스트 화면을 관리하는 부분이다.(커서 위치 제어, 화면에 데이터 입출력)셀에 의해 실행될 프로그램이다.(각 프로그램은 셀과 독립적) 먼저 자주 반복되어 사용되고 또 앞으로도 계속 사용될 기본적인 기능들을 가지는 함수들을 C언어 표준 라이브러리를 참고하여 작성하였다.  int StrLen( const char* pcBuffer ); // String Lengthlong AToI( const char* pcBuffer, int iRadix ); // String -> Integerint IToA( long lValue, char* pcBuffer, int iRadix ); // Integer -> Stringint HexToString( QWORD qwValue, char* pcBuffer ); // Hex -> Stringint DecimalToString( long lValue, char* pcBuffer ); // Dec -> StringQWORD HexStringToQword( const char* pcBuffer ); // Hex String -> QWORDlong DecimalStringToLong( const char* pcBuffer ); // Dec String -> Longvoid ReverseString( char* pcBuffer ); // reverse order of stringint SPrintf( char* pcBuffer, const char* pcFormatString, ... ); // sprintf()    콘솔은 텍스트 방식이나 명령줄 방식으로 작업을 수행하는 입출력 장치이다. 아마도 모니터가 출력장치, 키보드가 입력장치일 경우가 대부분일 것이다. 현재 텍스트 모드에서 콘솔화면은 최대 80 컬럼에 25 라인까지 가능하여 한 화면에 총 2,000 문자를 출력할 수 있다. 컬럼의 최대값이 80인 점을 이용하여 개행 문자인 'n'은 현재 위치에서 다음 80의 배수 위치로 커서를 옮기는 식으로 처리가 가능할 것이다. 그 외 문자열이 라인의 최대값을 넘을 경우 화면의 스크롤 등의 처리도 고려를 해야 한다. 스크롤은 첫 라인을 제외한 나머지 라인을 하나씩 위로 복사하면 간단하게 구현할 수 있다. 그리고 마지막 라인은 공백으로 두면 마치 스크롤 효과가 일어난 것처럼 느끼게 되는 것이다. 스크롤 처리는 메모리 복사 함수를 사용하여 구현하였다. 제어 문자와 화면의 스크롤 효과를 처리하는 문자열 출력 함수는 다음과 같다.    제어 문자와 화면의 스크롤 효과를 처리하는 문자열 출력 함수 int ConsolePrintString( const char* pcBuffer ){    CHARACTER* pstScreen = ( CHARACTER* )     CONSOLE_VIDEOMEMORYADDRESS;    int i, j;    int iLength;    int iPrintOffset;    iPrintOffset = gs_stConsoleManager.iCurrentPrintOffset;    iLength = StrLen( pcBuffer );        for( i = 0 ; i < iLength ; i++ )    {        if( pcBuffer[ i ] == 'n' ) // Line feed        {            iPrintOffset += ( CONSOLE_WIDTH - ( iPrintOffset % CONSOLE_WIDTH ) ); // multiple 80        }        else if( pcBuffer[ i ] == 't' ) // Tab        {        iPrintOffset += ( 8 - ( iPrintOffset % 8 ) ); // multiple 8        } else {            pstScreen[ iPrintOffset ].bCharactor = pcBuffer[ i ];          pstScreen[ iPrintOffset ].bAttribute = CONSOLE_DEFAULTTEXTCOLOR;            iPrintOffset++;        }     if( iPrintOffset >= ( CONSOLE_HEIGHT *     CONSOLE_WIDTH ) ) // if offset > (80*25)? then scroll        {            MemCpy( CONSOLE_VIDEOMEMORYADDRESS,                      CONSOLE_VIDEOMEMORYADDRESS + CONSOLE_WIDTH * sizeof( CHARACTER ),                     ( CONSOLE_HEIGHT - 1 ) * CONSOLE_WIDTH * sizeof( CHARACTER ) );                for( j = ( CONSOLE_HEIGHT - 1 ) * ( CONSOLE_WIDTH ) ;                  j < ( CONSOLE_HEIGHT * CONSOLE_WIDTH ) ; j++ ) // last null line            {                pstScreen[ j ].bCharactor = ' ';pstScreen[ j ].bAttribute = CONSOLE_DEFAULTTEXTCOLOR;            }       iPrintOffset = ( CONSOLE_HEIGHT - 1 ) *       CONSOLE_WIDTH; // set offet to last line(first col)        }    }    return iPrintOffset;}     (6) Shell  쉘은 유저의 입력을 크게 세 가지로 구분하여 처리하도록 구분하였다. 알파벳이나 숫자 같은 쉘 커맨드를 입력하는 경우이다. 엔터나 백스페이스 키처럼 입력된 커맨드를 조작하는 경우이다. 쉘에서 사용되지 않는 키로 무시되는 경우이다. 이를 구분하여 처리하는 쉘의 반복 루틴은 다음과 같이 구현하였다.    void StartConsoleShell( void ){    char vcCommandBuffer[ CONSOLESHELL_MAXCOMMANDBUFFERCOUNT ]; // Buffer MAX 300    int iCommandBufferIndex = 0;    BYTE bKey;    int iCursorX, iCursorY;        Printf( CONSOLESHELL_PROMPTMESSAGE ); // Command Prompt    while( 1 )    {        bKey = GetCh();        if( bKey == KEY_BACKSPACE )         {            if( iCommandBufferIndex > 0 )            {                GetCursor( &iCursorX, &iCursorY );                PrintStringXY( iCursorX - 1, iCursorY, " " );                 SetCursor( iCursorX - 1, iCursorY );                iCommandBufferIndex--;            }        }        else if( bKey == KEY_ENTER )        {            Printf( "n" );            if( iCommandBufferIndex > 0 )            {                vcCommandBuffer[ iCommandBufferIndex ] = '';                 ExecuteCommand( vcCommandBuffer ); // command exec            }            Printf( "%s", CONSOLESHELL_PROMPTMESSAGE );                        MemSet( vcCommandBuffer, '', CONSOLESHELL_MAXCOMMANDBUFFERCOUNT );            iCommandBufferIndex = 0; // Initialize buffer        }       else if( ( bKey == KEY_LSHIFT ) || ( bKey == KEY_RSHIFT ) ||                 ( bKey == KEY_CAPSLOCK ) || ( bKey == KEY_NUMLOCK ) ||                 ( bKey == KEY_SCROLLLOCK ) )        else        {            if( bKey == KEY_TAB )            {                bKey = ' ';            }            if( iCommandBufferIndex < CONSOLESHELL_MAXCOMMANDBUFFERCOUNT )            {                vcCommandBuffer[ iCommandBufferIndex++ ] = bKey;                Printf( "%c", bKey );            }        }    }}     일반적으로 커맨드 버퍼에 있는 커맨드를 읽어 들여 실행하는 가장 간단한 방법은 if를 이용하여 분기 구문을 사용하는 것이다. 그러나 추후 많은 수의 커맨드가 추가될 지도 모르기 때문에 나중을 위해 함수 포인터를 사용하여 구현하였다.      typedef struct ShellCommandEntryStruct{    char* pcCommand; // command string    char* pcHelp; // command info    CommandFunction pfFunction; // func ptr} SHELLCOMMANDENTRY;void ExecuteCommand( const char* pcCommandBuffer ){    int i, iSpaceIndex;    int iCommandBufferLength, iCommandLength;    int iCount;    iCommandBufferLength = StrLen( pcCommandBuffer );    for( iSpaceIndex = 0 ; iSpaceIndex < iCommandBufferLength ; iSpaceIndex++ )    {        if( pcCommandBuffer[ iSpaceIndex ] == ' ' )        {            break;        }    }     iCount = sizeof( gs_vstCommandTable ) / sizeof( SHELLCOMMANDENTRY );    for( i = 0 ; i < iCount ; i++ ) // look for command in table    {        iCommandLength = StrLen( gs_vstCommandTable[ i ].pcCommand );        if( ( iCommandLength == iSpaceIndex ) &&            ( MemCmp( gs_vstCommandTable[ i ].pcCommand, pcCommandBuffer,                       iSpaceIndex ) == 0 ) )        {            gs_vstCommandTable[ i ].pfFunction( pcCommandBuffer + iSpaceIndex + 1 );            break;        }    }    if( i >= iCount )     {        Printf( "'%s' is not found.n", pcCommandBuffer );    }}      5) 검증을 위한 App 개발 (1) CPU 사용 여부와 상태 체크를 위한 App(2) 성능과 스케줄러 검증을 위한 App


그림 11. CPU 사용 여부와 상태 체크를 위한 App




그림12.  성능과 스케줄러 검증을 위한 App 

 

   5. 개발 환경 개발환경: Windows7 visual studio 2010, Intel Core2                   Quad CPU8300                  VM-ware, ARM Cortex-M3개발언어: C  7. 용어 정리1. SMP: 대칭형 다중 처리 또는 SMP(symmetric multiprocessing)는 두 개 또는 그 이상의 프로세서가 한 개의 공유된 메모리를 사용하는 다중 프로세서 컴퓨터 아키텍처이다. 현재 사용되는 대부분의 다중 프로세서 시스템은 SMP 아키텍처를 따르고 있다.  SMP 시스템은, 작업을 위한 데이터가 메모리의 어느 위치에 있는지 상관없이 작업할 수 있도록 프로세서에게 허용한다. 운영체제의 지원이 있다면, SMP 시스템은 부하의 효율적 분배를 위해 프로세서간 작업 스케줄링을 쉽게 조절할 수 있다. 그러나 메모리는 프로세서보다 느리다. 단일 프로세서라도 메모리로부터 읽는 작업에 상당한 시간을 소비한다. SMP는 이를 더욱 악화시키는데, 한 번에 한 개의 프로세서만이 동일한 메모리에 접근 가능하기 때문이다. 이는 다른 프로세서들을 대기하도록 만든다.2. IPI: IPI(Inter Processor Interrupt)는 멀티 프로세서 시스템에서 하나의 프로세서가 다른 프로세서로 인터럽트를 보내는 특별한 형태의 인터럽트이다.3. BSP: 유니 프로세서와 같이 멀티 프로세서도 부팅 초기에는 하나의 CPU만 구동되는데, 맨처음 부팅을 수행하는 CPU를 BSP(Bootstrap Processor)라고 부른다. 4. AP: 유니 프로세서와 같이 멀티 프로세서도 부팅 초기에는 하나의 CPU만 구동되는데, 맨처음 부팅을 수행하는 CPU를 BSP(Bootstrap Processor)라고 부른다. 그리고 나머지 CPU들을 AP(Application Processor)라고 부른다.  
참고문헌 [1] Intel - 64 and IA-32 Architectures Software Developer's Manual Volume 3A,3B: System Programming Guide[2] Intel? 64 and IA-32 Architectures Software Developer's Manual Volume 2A, 2B:Instruction Set Reference[3] Intel? 8259A Programmable Interrupt Controller (8259A/8259A-2)[4] Intel? Multiprocessor Specification Version 1.4[5] Intel? 82093AA I/O ADVANCED PROGRAMMABLE INTERRUPT CONTROLLER (IOAPIC)[6] 서민열, 단일프로세서 실시간 커널을 SMP 커널로 재구성하는 기법, 2008[7] Jean J. Labrosse, Micro C/OS-II 실시간 커널, 2/E[8] 카와이 히데미, OS 구조와 원리 [9] 노재현, 개발자를 위한 나만의 운영체제 만들기[10] 오재준, OS 제작의 정석, 2/E   

댓글삭제
삭제한 댓글은 다시 복구할 수 없습니다.
그래도 삭제하시겠습니까?
댓글 0
댓글쓰기
계정을 선택하시면 로그인·계정인증을 통해
댓글을 남기실 수 있습니다.