운영체제 개발 – GRUB 부팅과정

그럼 실제로 어떻게 되는지를 보여주기 전에, GRUB이 운영체제 커널을 어떻게 부팅하는 지 좀 정리해서 적어보려고 한다. 일단 아주 간단한 과정에 대해서 정리를 해보면 다음과 같다.

  1. 바이오스가 부팅 장치를 찾고 MBR(Master Boot Record)를 읽어온다.
  2. MBR에 GRUB 스테이지 1이 있으며, GRUB 스테이지 1은 스테이지 1.5나 2를 불러온다.
  3. 스테이지 1.5는 MBR 직후 30킬로바이트 영역에 저장되며 이는 스테이지 2를 불러온다.
  4. GRUB 스테이지 2는 부트 메뉴나 명령 프롬프트를 실행하고 보여준다.
  5. 기본 값으로 지정된 커널이나 사용자가 선택한 커널을 메모리에 적재시켜서 커널 엔트리를 실행한다.

이 정보가 GRUB에 대한 위키피디아 내용을 정리한 것이다. MBR에 대한 설명을 간단하게 적으며, MBR은 저장매체의 첫 번째 섹터를 바이오스가 메모리로 읽어들인다. 그 후에 읽어들인 부트 코드로 제어권이 이양되어 코드가 실행되도록 해주는 녀석이다. 자세한 건 운영체제 이론쪽을 보면 될 것이고, 저는 일단 이렇게 간단하게 요약해서 설명합니다.

하드 디스크에는 논리적으로 여러 개의 하드 디스크(파티션이라고 한다)로 나눠서 쓸 수 있다. 최초 컴퓨터가 부팅된 후 MBR에서 부팅 가능한 논리 하드 디스크를 조회하고 부팅 가능한 논리 하드 디스크가 있을 경우에는 해당 논리 디스크의 부트 섹터를 찾아서 메모리로 읽어오고 그 부트섹터의 코드를 실행하게 된다.

이게 윈도우 운영체제인 경우에는 부트섹터는 Ntldr이라는 커널 로더를 메모리에 적재시키며 제어권을 Ntldr에 이양한다. Ntldr은 커널의 코어와 파일시스템 관련 시스템 파일을 로드해서 운영체제를 가동시키며 커널 코어와 파일 시스템은 시스템 자원을 활용하기 위해 여러 커널 모듈을 동적 또는 정적으로 로드시켜 운영체제를 실행하면서 유저 어플리케이션의 요청에 대응하게된다. (이 내용은 윈도우 시스템 관련 내용에 나와있습니다.)

Windows NT 계열의 운영체제 부트 과정을 그림으로 정리…. 이러면 쉬운데…;ㅅ;

부트로더의 존재를 통해서 커널에 어떻게 보여주는지를 정리하기 위해 윈도우를 기준으로 설명을 먼저 해봤습니다.

GRUB을 통한 리눅스를 어떻게 이용한 방식으로 이야기를 하면, GRUB은 부팅 후 Ntldr 까지의 역할을 GRUB이 실행한다고 보면 된다. 운영체제가 설치된 환경이 하드 이스크던, 시디던, USB던 상관 없다. 한편 우리가 제작할 운영체제의 커널은 GRUB 덕분에 Ntoskrnl.exe 같은 커널 프로세스에만 집중하면 된다. 즉, 커널을 메모리에 적재하는 과정을 구현하지 않아도 된단 것이다!!!! 이건 이전 글에도 작성했지만 운영체제 개발에 있어서 큰 부분을 덜어주는 것이다.

중요한 거니 두번 적습니다. \ㅇㅅㅇ/

그럼 GRUB은 어떻게 되는지를 또 그려서 보여줍니다.

바이오스는 부트 섹터에 기록된 boot.img 512바이트를 메모리에 적재한 뒤 제어권을 넘긴다. (이 과정이 스테이지 1) boot.img 파일은 스테이지 2에 해당하는 core.img를 메모리에 적재 해서 해당 코드를 실행하는데 여기서 menu.lst 파일이나 grub.cfg 설정 파일을 참고해서 커널 리스트를 가져온다. 그리고 커널을 선택해서 실행하면 GRUB은 커널을 메모리에 적재시킨 뒤 커널 엔트리 포인트에 제어권을 넘기고 자신의 일부를 종료해서 일을 끝낸다.

GRUB 같은 경우에는 버전이 1.0과 2.0이 있는데, 1.0대 버전에는 위의 설명과 같이 menu.lst 파일을 사용한다. 근데 2.0에서는 그냥 grub.cfg에서 다 처리한다. 근데 일단 그런 버전 차이에 상관없이 적고 싶어서 하다보니 위에처럼 정리가 되더라….

어려워…ㅠㅠ

이렇게 해서 GRUB을 통한 부팅 과정에 대한 설명을 정리했다. 커널 부팅에 대해서 오래된 자료들에 메모리의 어디 주소 영역을 찍어서 부팅하고 뭐하고 하는 그런 거 없어서 편하게 설명할 수 있었지만 이것도 제대로 다 보면 진짜 산더미만한 내용을 보게 될 것이다. 근데 규링이 만들 운영체제에서는 범용으로 부팅되는 과정에 대한 이해만 하고 제대로 넘기면 되니….ㅇㅂㅇ

이제 자세한 걸 만들어가면서 보여주려고 하는데…. 만들어봐야 할 것들이 있어서 그러니 좀 걸릴 것이다. ;ㅅ;

운영체제 개발 – GRUB

GRUB은 GNU 프로젝트의 부트로더이다. 대부분의 운영체제 커널을 불러올 수 있으며 우리가 제작한 커널 또한 호환되게 개발만 하면 호출할 수 있다. 뭐 이름과 유래에 대해서 내용이 있긴 한데 간단하게 그냥 GRUB도 우리가 운영체제를 부팅할 때 하드 디스크, 플로피, USB 등에 구애받지 않고 부팅 가능하도록 도와준다는 것이 제일 중요한 것이다.

GRUB 화면

이걸 제일 많이 볼 때가 있다면… 윈도우랑 리눅스 멀티부팅 해서 쓰는 분들이 제일 많이 볼 듯 하다. 아니면 여러 운영체제를 선택적으로 설치하거나 부팅해서 쓰기 위해서 자작으로 제작할 때에도 이용하기도 한다.

GRUB은 상용적으로 사용되는 운영ㅇ체제를 로드할 수 있을 뿐 아니라 개인이 자체 제작한 운영체제 커널도 규격에 맞추면 로드할 수 있는 능력을 가지고 있다. 그 능력에 대해서 살펴보면

  • 파일 시스템 직접 접근 기능
  • 다양한 실행 파일 형식 지원 기능
  • 비 멀티부팅 운영체제에 대한 지원
  • 메뉴 인터페이스 지원(부팅 화면)
    • 그래픽 메뉴 및 배경 그림도 사용 가능
    • 콘솔 인터페이스 지원
  • 다양한 파일 시스템 지원

이 정도가 된다. 우리가 순간적으로 그냥 보는 기능 안에 이렇게 많은 기능들이 들어있는 것이다. ㅇㅂㅇ

고마운 녀석입니다. ㅇㅅㅇ

직접 자작한 운영체제를 GRUB이랑 연동하였다면, GRUB은 직접 제작한 커널 파일이 유효한 커널인지 확인한 후 커널을 메모리로 적재한다. 또한 하드웨어 제반사항을 초기화한 후 커널 엔트리를 호출해서 제어권을 커널 코드로 이양할 것이다. 이 과정에서 GRUB은 커널에 하드웨어 초기화 정보를 넘겨준다. 이 과정을 아래와 같이 보면 이해가 쉬울 것이다. 조직도와 화살표 방향에 주목해서 글을 읽어보면 된다.

자바 가상 머신과 유사한 느낌으로 그려봤다. 근데 이렇게 보면 관계도 이해가 좀 더 쉽게 될 것이다.

그리고 당연한 이야기지만 GRUB을 통해서 커널을 메모리에 적재시켜서 실행시키고 싶다면 GRUB에서 요구하는 규격대로 만들어 줘야 한다. 이 내용을 작성하기 전에 GRUB 부팅에 대한 자세한 내용을 다음 글에서 작성하도록 하겠다.

운영체제 개발 – 부트로더 선택(?!)

운영체제는 일반 응용 프로그램과 시작점이 다른 것이 있다면, 메모리에 올리는 녀석이 있냐 없냐의 차이로 보면 쉽게 이해된다. 리눅스의 응용 프로그램이던 윈도우의 응용 프로그램이던, 실행 명령을 입력받는 운영체제가 존재하고, 실행을 하면 운영체제가 해당 프로그램의 실행 정보를 메모리에 올린다. 가상 머신을 쓰는 환경들은 더더욱 쉽다. 자바, 안드로이드를 예로 보면, 자바 프로그램은 바이트코드화 되어서 자바 가상 머신(JVM)에 올라간다.

자바 떡칠인 모 반도에 맞는 설명… (좋은 소리 아니다.)

근데 운영체제는? 운영체제를 실행할 무언가는? 운영체제를 메모리에 올릴 무언가는? 그런 거 없다. 하나하나 직접 올려야 한다. 그걸 위해서 개발해야 하는 것이 바로 부트로더이다.

부트로더는 이런 일들을 주로 한다.

  • 커널(및 모든 커널 부트스트랩 필요)을 메모리로 가져오기
  • 커널에 올바르게 작동하는 데 필요한 정보 제공
  • 커널이 좋게 동작하는 환경으로 전환
  • 커널로 제어 전송

뭐, 이런 기능을 제공하기 위해서는 운영체제 개발 책들에서는 운영체제 로드를 위한 부트로더 개발 작업을 어셈블러로 작성하고 그걸 설명해주는데도 여러모로 시간을 잡아먹는다.

그러면 좋은 것이 컴퓨터 리소스를 쓰기 위해서 16비트 모드, 32비트 모드를 직접 작업해서 넘어오는 걸 해보게 된다. 20비트 세그먼트 가지고 1MB 메모리 주소에 접근하고 그걸 가지고 시작하는 CPU를 제어할 수 있는 하드웨어 종속적인 코드 짜고, 이걸 32비트, 64비트 모드로 점점 넘어오는 작업을 해야 하는 그런 일들을 하고 해서 이제 화면 접근 작업을 통해서 “Hello World!” 찍어내는데만 엄청나게 시간 소요를 하게 되어주는….

이렇게 생각하고 포기해서 1장이나 2장에서 멈춘 당신, 뜨끔합니까?

저러다가 요즘 윈도우 10에서는 저 영역을 건드리지 못하게 막아내는 거 땜에 오류 팍팍 떠서 걍 멘붕하고 포기하는 당신…

부트로더는 개발해서 나쁠 건 없다. 근데 수많은 운영체제 제작 코드들이 자신만의 코드를 이용해서 개발하고 한다면… 운용하기 힘들어진다. 게다가 요즘처럼 다른 플랫폼까지 이용해야 하는 경우라면…? 더더욱 개발 힘들어진다. 아키텍쳐마다 비트 모드에 접근하고 사용하는 방식 자체가 다르다.

해봐서 좋은 경험이 된다. 근데 커널 자체에 집중해서 개발하는 것을 위해서 요즘은 그냥 일련의 커널 로딩 과정을 간편하게 해주기 위해 부트로더를 이용한다. 흔히 대표적으로 이용하는 것이 바로 GRUB인데, 이녀석을 이용하여 개발하는 걸 보여주려고 한다.

부트로더 직접 개발하면 진짜 피곤하다.

부트로더 직접 개발하는 거 어떤지 알고 싶으면 한승훈 저자님의 “64bit 멀티코어 OS 원리와 구조” 이 책 보고 직접 스터디 해봐도 된다. 근데 아마 도서관에 1권만 스터디한다면서 이미 다 빌려가고 없거나 할 수도 있다. ㅇㅅㅇ;;

운영체제 이론 – 스택

스택은 FILO(First in last out이라고도 함) 형태의 자료구조로, 마지막에 입력된 자료가 먼저 나오는 형태이다. 반대로 FIFO(First in first out이라고도 함) 형태의 자료구조는 큐가 있는데, 이건 먼저 입력된 자료가 먼저 출력되는 형태를 가진다.

스택의 구조

이런 자료구조에서 보던 녀석을 왜 운영체제 이론 설명하는데 굳이 끼우냐면… 여러모로 볼 녀석이다. 진짜로….

이런 반응 나올 정도로 지겹게 볼 것이다. 그러니 읽는 분이 자료구조 개판으로 공부했다면 운영체제 공부는 엄청나게 괴로운 공부가 될 것이다. 진심어린 충고다.

콜스택은 특정 함수가 호출될 때에는 지역변수나 함수 파라미터가 특정 공간에 저장되는데 이 공간을 말한다. x86 아키테겨에서는 스택에 변수나 파라미터가 저장될 때 주소 공간이 줄어드는 방향으로 데이터가 저장된다. 이걸 설명하기 위해 다음과 같은 함수가 있다고 보자.

example code

이런 함수를 호출할 때의 콜 스택에 대해서 손으로 직접 그려보았다.

콜스택 상황

함수의 수행이 끝날 경우에는 이 함수를 호출한 실행 코드의 위치로 돌아갈 필요가 있다. 이때 필요한 값은 해당 함수의 복귀 주소와 EBP값이 필요하다. 이런 값들은 함수를 호출할 때마다 자동으로 생성되는데, 이러한 값들의 모음을 스택 프레임(Stack Frame)이라고 한다. 콜스택의 실제 상황을 직접 그린 이유는 이걸 가지고 스택 프레임에 들어갈 포인터 녀석들을 설명하려 한다.

  • ESP(Extended Stack Pointer) 레지스터
    스택의 밑바닥을 가리키는 포인터다. 최초 함수가 호출될 때 EBP와 ESP의 값은 같은 값이며, 로컬변수가 선언되면 ESP는 낮은 값으로 증가한다. (x86 아키텍쳐를 기준으로 설명한다.) ESP는 다음 데이터를 push할 위치가 아니라 pop했을 때 뽑아낼 데이터의 위치를 가리킨다. (이거 헷갈린다.)
  • EBP(Extended Base Pointer) 레지스터
    스택 프레임의 시작 주소를 가리킨다. 새로운 함수가 호출되면 파라미터와 스택 프레임 값이 스택에 채워지는데 EBP 레지스터값은 바로 그 다음 주소를 가리킨다. 즉, 호출된 함수가 로컬 변수를 선언하기 직전의 시작점이 되며 EBP값은 함수 실행 동안 변하지 않으므로(다른 함수를 호출하지 않는 이상 안변함) 파라미터나 로컬 변수를 참조할 수 있는 기준점이 되어준다. EBP 레지스터는 현재 실행중인 함수가 종료되어 리턴되면 이 함수를 호출한 함수의 EBP값으로 변경된다. (스택 프레임에 저장된 EBP값)

프로세스를 적고 정리하면서 거의 항상 꼭 보는 스택과 실제 실행과 관련된 내용에 대해서 정리를 해봤다. 어려운 내용은 아니지만 머릿속엔 있는 게 좋다.

운영체제 이론 – 스레드

프로세스가 프로그램의 주체라면 스레드는 프로세스의 실제 실행 단위이다. 프로세스는 여러개의 스레드를 담고 있으며 커널은 프로세스가 담고 있는 스레드를 관리해서 프로세스의 동작을 조정한다.

가상주소 공간을 공유하고 있는 스레드의 모습을 그려봄…

위에 그린 그림은 스레드가 프로세스의 자원을 공유하는 것을 표현했다. 코드 영역을 동시에 접근하는 것은 문제가 되지 않지만 쓰기 가능한 데이터 영역에 동시에 접근할 경우에는 데이터 무결성 장애를 일으킬 수 있다. 스택은 스레드 고유의 자원이며, 일반적인 경우에는 다른스레드의 간섭을 받지 않는다. 따라서 동기화 걱정 없이 마음대로 이용할 수 있다.

스레드 경합은 자료구조나 데이터에 복수의 스레드가 접근하면 문제가 발생할 수 있는 것을 나타낸다. 이런 상태를 경쟁 상태, 경합 상태라고 하는데, 의외로 쉽게 일어난다. 아래 예시 코드처럼 짜면 거의 반반의 확률로 발생한다.

스레드 경합 관련 예시 코드. 잠안와서 작성한…(?!)

단위 연산이 아닌 작업이 들어있기 때문에 실제로 출력되는 것은 1,1 혹은 1,2가 된다. 이런 문제를 해결하기 위해 동기화를 적용해야 한다. 그리고 그 동기화를 적용하기 위해서 동기화 객체를 사용하여 구현을 하는데 그것들은 다음과 같다.

  • 크리티컬 섹션
  • 뮤텍스
  • 세마포어
  • 스핀 락

이것들에 대한 건 운영체제 책에서 더 자세히 볼 수 있으니 자세한 복습은 운영체제 내용을 참조한다.

TCB(Thread Control Block)은 프로세스에 PCB가 존재하듯, 스레드에도 스레드 정보를 관리하기 위한 블록이 존재한다. TCB는 커널에서 스레드를 관리하기 위해 필요로 하는 정보를 담고 있는 구조체이고, 그 구조는 아래 그림과 같다.

PCB와 TCB의 관계를 보여줌..

그리고 다음과 같은 정보들을 가지고 있다.

  • 스레드 식별자: 고유 아이디는 스레드마다 새롭게 만듬.
  • 스택 포인터: 스레드의 스택을 가리키는 포인터
  • 프로그램 카운터: 스레드가 현재 실행 중인 명령어 주소
  • 스레드 상태: 실행, 준비, 대기, 시작, 완료
  • 레지스터 값들
  • 스레드를 담고 있는 프로세스의 PCB의 포인터

스레드에 대해서는 이정도만 정리해도 충분할 것이다. 간단한 거라서 밤 늦게 정리를 해보고 자려고 한다. ㅇㅅㅇ

그럼 전 일단 자러….

운영체제 이론 – 프로세스

본인이 뭐 어느 정도의 주니어 혹은 시니어 프로그래머 이상급 된다면 사실 이런 내용은 이미 다 알고있는 것일껍니다. 그런 게 아니라서 뭐 간단한 정리를 보고 싶으시다면 이 글이 도움이 되었으면 좋겠군요.

쓸데없이 서론이 길어졌다. ㅠㅠ 오랜만의 블로그라….

그럼 시작해보겠습니다. 주요 용어에 대해 볼드체를 처리했고, 그에 대한 내용을 풀어가는 형태로 글을 써봤습니다.

프로세스는 컴퓨터에서 실행되는 프로그램을 의미합니다. 일반적으로 프로그램은 하드디스크에 저장되어 있는데 프로그램을 실행하면 운영체제의 로더에 의해 메모리로 적재됩니다. 이렇게 메모리에 적재되어 실행되는 프로그램을 프로세스라고 합니다.

싱글테스킹은 운영체제에서 하나의 프로세스가 독점적으로 시스템 자원을 사용하는 것이고, 멀티태스킹은 운영체제에서 하나의 프로세스가 독점적으로 시스템 자원을 사용하는 것을 막기 위해 프로세스에 자원 사용 시간을 적절히 배분하여 이용합니다.

프로세스가 메모리에 로드되었을 때의 대략적인 녀석은 다음과 같습니다.

이미 운영체제 시간이나 프로그래밍 시간에 다 배운 녀석이죠…ㅇㅅㅇ;;

프로세스 컨텍스트(Process Context)는 프로세스 문맥이라도고 하며, 운영체제가 관리하는 프로세스 정보라고 보면 된다. 주로 다음과 같은 녀석들이다.

  • CPU 상태: CPU 레지스터, 현재 프로세스가 수행되고 있는 위치 등 (ex: instruction pointer)
  • PCB(Process Control Block): 커널이 관리하는 프로세스 정보 구조체
  • 가상주소공간 데이터: 코드 데이터, 스택, 힙

위와 같은 내용이 프로세스 컨텍스트를 의미한다. 멀티테스킹 운영체제에서 실행되는 프로세스는 위와 같은 정보를 토대로 커널이 프로세스를 실행하고 있다는 것을 의미하며 컨텍스트 스위칭은 지금 실행하고 있는 프로세스의 실행을 멈추고 다른 프로세스의 컨텍스트를 가져와 실행함을 의미한다.

PCB(Process Control Block)는 운영체제 커널의 자료구조로써 프로세스를 표현하기 위해 사용된다. 커널은 이 자료구조를 사용해서 프로세스를 관리한다. PCB는 프로세스가 생성될 때 같이 생성되며 프로세스 고유의 정보를 포함한다. Win32 프로세스에서의 PCB 구조는 교과서 같은 곳에 많이 나와있는데, 외우고 있는 걸 노트에 그려봤다.

(이런 걸 외우고 있으니 머리가 이상해지는 겁니다, 규링!) (응?)

PCB는 체인으로 다른 PCB랑 연결되어 있다. (우리는 이런 걸 흔히 링크드 리스트라고 한다.) PCB에 포함된 정보가 글씨가 좀 지저분한데… 정리하면 다음과 같이 볼 수 있다.

  • OS가 관리상 사용하는 정보: vm로세스 상태, 프로세스 ID, 스케쥴링 정보, 우선순위 등
  • CPU 수행 관련 하드웨어 값: 프로그램 카운터, 레지스터 등
  • 메모리 정보: 코드, 데이터, 스택의 위치 정보
  • 파일 정보: 열어둔 파일 정보(핸들)

프로세스 상태는 컨텍스트 스위칭에 의해서 프로세스는 실행 상태에 놓일 수도 있고, 정지 상태에 놓일 수도 있는데, 이를 운영체제 교과서에서 이렇게 표현한다.

프로세스 상태 다이어그램

프로세스 상태에 대해서는 아래와 같이 정리해보자.

  • 실행
    프로세스가 CPU를 점유하고 있는 상태.
  • 대기
    프로세스가 CPU를 점유하기 위해 기다리고 있는 상태. 메모리에는 이미 올라와 있을 뿐 아니라 CPU 동작을 위한 모든 조건들을 만족한 상태이다.
  • 블록(wait, sleep)
    당작은 작업이 수행될 수 없는 상태. sleep 함수나 동기화를 위해 대기해야 할 경우에 프로세스는 블록 상태가 된다.
  • 정지
    스케쥴러나 인터럽트 때문에 비활성화 된 상태. 외부에서 다시 재개시켜야 활성화 상태로 변경된다.

컨텍스트 스위칭(Context Switching)은 CPU가 한 프로세스에서 PCB 정보로 스위칭 되는 과정을 의미한다. 그 과정도 일단 손으로 그려보았다.

프로세스 컨텍스트의 스위칭 과정. 스위칭 과정은 엄청 짧으나 v 체크 되어있는 만큼의 작업을 하고 있는 것이다.

위에 그림에서 보면 두 프로세스가 실행과 중지를 반복하여 컨텍스트 스위칭 되는 과정을 보여준다. 프로세스 1이 실행중에 있다가 컨텍스트 스위칭이 발생할 경우, 운영체제는 프로세스 1의 PCB를 저장한 뒤 프로세스 2의 PCB를 복원시킨다. 그러고 나서 프로세스 2의 실행이 진행된다. 그러다가 다시 프로세스 1을 실행할 때에는 프로세스 2 PCB를 저장하고 프로세스 1의 PCB를 불러온다. 이런 과정을 반복하면서 두 프로세스는 실행과 중지를 반복한다. 컨텍스트 스위칭은 시스템 콜이나 외부 인터럽트에 의해서 주로 발생한다.

프로세스에 관해서 간단하게 정리해보면 이 정도가 될 듯 하다. 너무 간단하게 적긴 했다만… 자세한 걸 보고 싶으면 운영체제 책을 가지고 제대로 공부하면 된다.

다음 글에서는 스레드에 대해서 지금처럼 간단하게 작성하려 한다.

p.s. 너무 오랜만에 글을 썼는데… 앞으로 자주 쓰려고 합니다. ;ㅅ;

운영체제 이론 – 시작

운영체제 이론은 사실 대학 다니면서 꼭 배우는 거지만… 운영체제를 직접 개발하려고 하면 반드시 꼭 정리 다시 해야 한다.

니가 아는 걸 직접 짜야 하는 걸 보게 된다.

즐거운 거잖아…!!!!!!

(저리가 by 독자)

그러니 일단 이론도 좀 시작해봅니다.

일을 막 늘리는 거 같아 보이면 기분탓입니다.

64비트 모드까지 부팅시켰는데..! ㅠㅠ

기분전환 삼아서 하는 운영체제 개발….

이제는 ia32-e 모드를 지나서 645비트로 동작시키도록 작업을 하고 있는데…

도중에 자꾸 덤프 뜬다. 작업 실행 도중에 코드 다 안짜고 멈추게 해서 그런가…ㅠㅠ

저기까지 진행한 작업이 메모리 구조를 페이징 구조로 변환해서 페이지와 세그멘테이션을 제대로 정리해서 메모리 관리가 될 수 있도록 한 기본 구조까지 만든 것이다.

틈ㅁ내서 만드는 거라 어차피 진도도 덜 나가고…ㅠㅠ

내 연구 테마가 메모리 매니지먼트닫 보니깐 여러모로 더 생각해서 짜게 되었다. 안그러면 그냥 단순한 레벨에서 걍 구현만 하고 끝내는 수준이었을 텐데…ㅠㅠ

자작 운영체제는 은근 단순한 구조를 가진다

리눅스, FreeBSD 등등 여러 운영체제의 코드가 공개되어 있다보니 많은 사람들이 여러모로 운영체제 구조를 보고 공부하는 데에는 익숙해졌다고 봐도 될 정도이다. (못하면 할 수 있도록 공부하는 사람들이라고 생각하겠다.)

그러나, 실제로 개발을 진행하다 보면 운영체제 개발은 그렇게 쉬운 개발이 아니게 된다. 리눅스 커널만 보더라도 자료구조가 상당히 복잡하다. 그런데, 그런 구조여야 될 수 있는 수준으로 규모가 커진 운영체제 또한 리눅스이다.

그런 걸 보다가 다른 사람들이 자작으로 만든 운영체제를 보면 은근 단순한 구조를 가지고 있는 것을 보게 된다. 실제로 지금 내가 만들고 있는 운영체제도 메모리 구조는 단순한 매칭 형식으로 지원되어 있다. 1:1 매핑이라던가, 단순 세그멘테이션으로만 이루어진 보호모드에서 걍 1:1 페이징을 구성한다던가….

이렇게 만드는 이유는 간단하다. 디버깅이 편하다. ㅠㅠ 운영체제 개발에 필요한 디버깅은 사람의 머리다. 그러다 보니 어느 정도 제어를 할 수 있는 자료구조를 가지고 운영체제 개발을 하게 된다. 그러다가 좀 더 규모가 커지면서 구조를 업데이트 하는 것이고, 그렇게 계속 튜닝을 하다 보면 어느샌가 어느 정도 이상의 규모를 지지할 수 있는 성능의 운영체제가 되는 것이다.

그렇다고 해서 대충 한다? 그건 아니다. 다들 운영체제의 이론에서 볼 법한 그런 내용들을 기반으로 생각해서 만드는 것이다. 그러므로 모르면 모르는대로 또 문제가 된다. 이거 은근 여러모로 사람 머리를 엄청 써야 하는 거니깐 은근 무시할 수 없는 수준까지 올라간다. 그러니, 단순한 구조라고 해서 무시할 것이 못된다.

p.s. 이런 운영체제 혼자서 설계해서 만들 수 있는 사람들은 널리고 널렸다는 게 문제다.

허접한 운영체제 개발 – 부트로더 제작과 운영체제 이미지 로딩, 보호모드 전환, 그리고 C 코드 실행까지 한방에!

리눅스 환경에서의 개발은 내가 갖고있는 바이오 노트북에서 이루어지는데, 바이오 노트북이 사실 해상도가 무지 낮다. 게다가 11인치짜리 모델이라…ㅠㅠ 게다가 책상 위가 난잡해지는 게 싫어서 평상시엔 켜놓고 나서 원격으로 이용중이다. 뭐, 이 글과는 관련은 없다. 스샷과는 관련이 좀 있다. ㅠㅠ

규링은 운영체제를 가지고 석사 논문을 쓰고 있다. (여럿 쓴 것도 있다.) 그런 녀석이 운영체제 하나 직접 만들어보는 게 어려울까 싶어서 도전해 보려 해도… 어렵다. ㅇㅅㅇ

자료는 뒤지면 여러모로 나온다. 게다가 여기까지의 부트로더 기술과 32비트 보호보드는 인텔의 메뉴얼과 몇몇 정형화된 코드들이 있어서 어렵지 않게 작업할 수 있긴 하다. 근데 그걸 머릿속에서 막 쥐어짜면서 직접 코드로 짜니깐 안쉽다. (게다가 어셈블리어다.)

근데 막상 만들고 나니 실제로 다른곳에서 만든 것과 거의 유사한 코드들이 나온다. 그래서 그나마 조금은 덜 걸렸을지도 모르겠다.

p.s. 왜 사람들이 이 수준이 되면 비슷한 변수명을 가져다가 막 예시로 만드는지 알겠다. 그게 제일 안전하다. 내 코드스타일 식의 변수명을 붙이고 하는 건 좀 있다가의 작업이 되겠다. ㅇㅅㅇ