13. 콘솔 셸을 만들자

들어가기

셸을 구현하는데 필요한 콘솔 라이브러리를 구현하고 콘솔 라이브러리를 구성하는 함수를 구현하는데 필요한 가변 인자 처리 방법.

본론

13.1 sprintf()와 가변 인자 처리

13.1.1 포맷 스트링과 가변 인자

// 가변 인자 예시
printf("%s", "test")
  • 포맷 스트링에는 함수의 파라미터가 가변적이라는 전제가 포함.
  • 함수를 호출하는 쪽에서 넘기는 파라미터의 정보를 알려줘야함.
  • 포맷스트링을 보고 대략적인 파라미터의 수를 파악할 수 있지만 정확하지 않음.
void main (void) {
    int iSum;

    iSum = sum(5, 1, 2, 3, 4, 5);
    printf("Sum = %d\n", iSum); 
}

int sum(int iParameterCount, ...) {
    int i;
    int iSum = 0;
    QWORD qwArgumentStartAddress;

    qwArgumentStartAddress = ((QWORD) (&iParameterCount)) + 8;
    for(i = 0; i < iParameterCount; i ++) {
        iSum += *((int *) (qwArgumentStartAddress));
        qwArgumentStartAddress += 8;
    }

    return iSum;
}
  • 파라미터를 모두 스택에 넣어 전달하고, 호출되는 함수는 포맷 스트링을 참조하여 스택에서 파라미터를 직접 참조.
int Sum(int iParameterCount, ...) {
    va_list va;
    int i;
    int iSum = 0;

    // 가변 인자의 주소를 가변 인자 리스트에 등록
    va_start(va, iParameterCount);

    for(i = 0; i < iParameterCount; i ++) {
        // 가변 인자 리스트에서 int 데이터를 꺼내고 다음 파라미터로 이동
        iSum += (int) va_arg(va, int);
    }

    va_end(va);
    return iSum;
}
  • C 언어는 가변 인자 처리를 위해 매크로가 존재.
    • va_list : 데이터 타입.
    • va_start() : 가변 인자의 시작 주소.
    • va_arg() : 데이터 타입에 해당하는 값을 꺼내고 이동.
    • va_end() : 가변 인자 리스트 종료.

13.1.2 sprintf() 함수와 vsprintf() 함수 구현

int printf(const char* pcFormatString, ...) {
    ...
    // 문법 오류 -> '...'를 다른 함수의 파라미터로 전달할 수 없음
    sprintf(vcBuffer, pcFormatString, ...);

    // 가능
    va_start(va, pcFormatString);
    vsprintf(vcBuffer, pcFormatString, va);
    va_end(va);
    ...
}
  • vsprintf() 함수는 sprintf() 함수의 실질적인 기능을 담당, 가변 인자 대신 va_list 타입의 가변 인자 리스트를 받아서 처리.
while(포맷 스트링의 끝이 아님) {
    if(데이터 타입을 나타내는 '%' 문자인가) {
        '%' 이후의 문자에 따라 가변 인자의 타입을 해석하여 출력 버퍼에 복사.
    } else {
        포맷 스트링의 현재 문자를 출력 버퍼에 복사
    }
    포맷 스트링 내의 문자 위치를 다음을 이동
}
  • vsprintf()의 함수의 알고리즘.

13.1.3 itoa() 함수와 atoi() 함수 구현

int kIToA(long iValue, char* buffer, int iRadix) {
    int iReturn;

    switch(iRadix) {
        case 16:
            정수 값을 16진수 문자열로 변환하는 함수
            break;

        case 10:
            정수 값을 10진수 문자열로 변환하는 함수
            break;
    }

    return iReturn;
}
  • C에서의 itoa를 구현한 알고리즘.
  • 정수 값을 문자열로 바꾸는 방법은 여러가지가 존재.
  • 전체 코드는 13.4 참조.

13.2 콘솔 입출력 처리

13.2.1 콘솔 자료구조 생성과 printf() 함수 구현

#pragma pack(push, 1)

typedef struct kConsoleManagerStruct {
    // 문자와 커서를 출력할 위치
    int iCurrentPrintOffset;
}CONSOLEMANAGER

#pragma pack(pop)
  • 커서 위치를 추가할 자료 구조.
  • 그래픽 모드로 전환하기 전까지 텍스트 모드 사용, 80 컬럼에 25라인 출력.
  • 전체 코드는 13.4 참조.

13.2.2 커서 제어

  • 커서는 모니터 출력을 담당하는 VGA 컨트롤러가 담당.
    • 컨트롤러 어드레스 레지스터, 0x3D4.
    • 컨트롤러 데이터 레지스터, 0x3D5.
  • 커서 위치 제어를 위해 컨트롤러 어드레스 레지스터에 0xE, 0xF를 전달.
  • 컨트롤러 데이터 레지스터에 상위 바이트, 하위 바이트로 나누어 커서 위치를 전달.

13.3 셸 구현

13.3.1 프롬프트, 커맨드 버퍼, 사용자 입력 처리

  • 프롬프트는 셸이 사용자로부터 키를 입력받을 준비가 되어있다는 것.
  • 전체 코드는 13.4 참조.

13.3.2 커맨드 비교와 커맨드 실행

typedef void(* CommandFunction) (const char* pcParameter);

typedef struct kShellCommandEntryStruct {
    // 커맨드의 도움말
    char* pcHelp;
    // 커맨드를 수행할 함수 포인터
    CommandFunction pfFunction;
}SHELLCOMMANDENTRY;
  • 함수 포인터를 통해 커맨드 함수 실행, if-else 구조보다 유지보수에 있어 수월.
  • 전체 코드는 13.4 참조.

13.4 콘솔 라이브러리와 셀의 통합과 빌드

13.4.1 콘솔 파일 추가

Console.c 코드 보기
Console.h 코드 보기

13.4.2 콘솔 셸 파일 추가

ConsoleShell.c 코드 보기
ConsoleShell.h 코드 보기

13.4.3 유틸리티 파일 수정

Utility.c 코드 보기
Utility.h 코드 보기

13.4.4 인터럽트 핸들러 파일 수정

InterruptHandler.c 코드 보기

13.4.5 C 언어 커널 엔트리 포인트 파일 수정

Main.c 코드 보기

13.4.6 빌드와 실행



마치며

끝!!

Share