본문 바로가기

혼자서꿍시렁

VC에서 운영체제 만들기 가이드

VC에서 운영체제 만들기 가이드
 (Make your own Operating System On VC++ !)
  상세제목: 예제 부트섹터 분석 및 OS 빌드하기^^

몇년 전(2003.09.02)에 제 홈페이지(AiRPAGE.ORG)에 올려 놓았던 글을 정리삼아 이곳에 퍼 둡니다.


뭐?  
어렵게만 여겨지던 운영체제의 베일이 벗겨지고 있습니다. 이것도 유행일까요? 소프트웨어 개발자라면 알고 싶어지고, 구현해 보고픈 운영체제. 그속을 들여다보고 연구하며 스스로 제작해 보는 사이트들이 계속 늘고 있습니다. 저역시 그 만들기의 시작을 조심스럽게 열어 보려 합니다. 여기-저기, 이곳-저곳, 앞-뒤-옆집에서 짬짬히 얻은 정보들을 토대로(어쩌면 짜집기(^^)로 보일 수도 있지만) 한몫 해보고픈 마음에 글을 써 봅니다.

아래의 내용들은 제가 직접 작성한 부트섹터 코드의 간단한 설명과 컴파일 방법 그리고 x86 Protected Mode로 포팅된μC/OS-II소스를 가지고 빌드하는 설명을 가지고 있습니다. 

이 강좌는 절대로 기술적인 내용이 없습니다. 어셈코드의 지식과 부트섹터, 커널에 관한 어느정도의 지식을 갖추었다는 가정하에 내용을 진행하게 됩니다. 단지 예제로 제시한 코드를 컴파일 하고, 실제 운영체제처럼 동작하는 모습을 보여줌 으로써 Visual C++환경하에서도 운영체제 개발 작업을 할 수 있다는 것을 보이기 위함입니다.
만약, 그러한 것들의 개념이 필요하다면 먼저 위의 메뉴에서 PROJECT -> OS PAGE에 링크된 여러 자료를 참고 하시기 바랍니다. 


준비물  
 

- 환경
	Microsoft Windows® 98, 2000, XP에서 테스트 되었습니다. 

 - Tool
	Borland-C++® 3.0(2.0도 됩니다^^) [Turbo-C® 2.0 download]
	Microsoft Visual C++® 6.0
	Microsoft Macro Assembler® 6.11 (다른 버전은 안됩니다.)
	NASM 0.98.36 (다른버전도 괜찮을지(?) 모릅니다.^^) [NASM site link]

 - Source
	μC/OS-II(Port : x86 Protected Mode) [UCOS-ii(intel port) Site link]
	LestatOS(TM) Boot Sector [bootsect.asm download]
	 : 장진호(devilnis@nownuri.net)님께서 고맙게도 버그를 하나 지적해 주셨습니다. 수정버전입니다.^^
	Boot Sector를 디스켙의 젤 첫섹터에 옮겨주는 C 소스 [scopy.c download]
	윈도우 환경에서 부트 섹터 정보를 읽고 써주는 유틸리티 [GSector.exe download]

 


구성  

우리가 빌드해볼 전체 목록 구성은 대충 이렇습니다.

- μC/OS-II 커널 이미지
- 커널 이미지를 로드할 부트섹터

위의 준비물들은 모두 예제로 다뤄볼 위에서 나열한 운영체제와 부트섹터를 빌드하기 위함입니다.
μC/OS-II 커널 이미지를 빌드하는 방법은 UCOS-II 웹 사이트의 http://www.micrium.com/intel/index.html 페이지를 참고하시면 됩니다. 거기엔 Jean Louis Gareau가 제작한 부트섹터와 커널이미지 그리고 커널이미지 로더 소스와 태스크 소스가 있습니다. 이를 참고로 하는 이유는 장점이 커널제작 작업을 Visual C환경에서 할 수 있다는 것입니다.
	
μC/OS-II에 관해 좀 더 알고 싶으시다면, http://www.micrium.com/사이트를 참조하시기 바랍니다. 아주 소형인 리얼타임 커널이면서도 탁원한 스케쥴링 알고리즘과 여러 시스템에 유연하게 포팅된 운영체제입니다.

 




구현  
 

자, 그럼 시작합니다!
제가 제작한 부트섹터는 NYAOS부트섹터를 참고했습니다.
부트섹터 설명 : bootsect.asm

부트 섹터는 BIOS에 의해 (물리주소)0x7C00번지에 로드 됩니다. 512 BYTE의 크기를 가져야 하며, 파일의 끝은0x55,0xaa캐릭터가 포함됩니다. 그리고 디스크의 젤 첫번째 섹터에 위치시키면 되는데요, 이왕이면 파일시스템정보를 포함함으로써 윈도우/리눅스나 도스등에서 접근할 수 있는 편리함을 이용해도 되겠죠. 무슨 말이냐 하면, 커널 이미지를 나중에 완성해 보시면 아시겠지만, 이미지를 특정 섹터에 위치 시켜 두고 부트로더가 로드하는 방식은 소스가 간단할 수도 있지만, 업데이트 된 커널을 매번 그렇게 위치시키느니 차라리 적당한 파일이름으로 복사만 시켜 주더라도 부트섹터가 알아서 커널을 로드한다면 많이 
편하다는 것을 느끼실 겁니다. 그렇기에 부트섹터의 소스 첫부분은 파일시스템 정보를 채웁니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fatOEM       db "LestatOS"               ; OEM
fatSectSize  dw 0x200                    ; Bytes per sector = 512
fatClustSize db 1                        ; Sectors per cluster
fatRessect   dw 1                        ; # of reserved sectors
fatFatCnt    db 2                        ; # of fat copies
fatRootSize  dw 224                      ; size of root directory
fatTotalSect dw 2880                     ; total # of sectors if < 32 meg
fatMedia     db 0xF0                     ; Media Descriptor
fatFatSize   dw 9                        ; Size of each FAT
fatTrackSect dw 18                       ; Sectors per track
fatHeadCnt   dw 2                        ; number of read-write heads
fatHidenSect dd 0                        ; number of hidden sectors
fatHugeSect  dd 0                        ; if fatTotalSect is 0 this value is
                                         ; the number of sectors
fatBootDrv   db 0                        ; holds drive that the fat came from
fatReserv    db 0                        ; not used for anything
fatBootSign  db 29h                      ; boot signature 29h
fatVolID     dd 0                ; Disk volume ID also used for temp
                                         ; sector # / # sectors to load
fatVoLabel   db "LestatOS",13,10,0       ; Volume Label
fatFSType    db "FAT12   "               ; File System type

물론 부트섹터가 하는 제일 첫 동작은 점프입니다. 확장자가 COM인 파일의 포멧과 비슷합니다. 점프를 끝내고 인터럽트를 디스에이블 시킨 후 현재 부트된 드라이브의 정보를 얻어 옵니다. 즉, 바이오스는 DL레지스터에 현재 부트된 드라이브의 정보를 담아 둡니다. dl에 0x00이 들어있으면 A드라이브, 0x80이면 C드라이브 입니다. 
1
2
3
mov ax,0x9000                ; put stack at 0x91000
mov ss,ax
mov sp,0x1000

그런다음 위처럼 스택공간을 잡아 줍니다. 넉넉히 메모리의 저 위쪽으로 말이죠^^ 이후, 플로피 컨트롤러를 초기화 하고, 에러면 에러 메세지를 출력하면서 리붓을 한다거나 하는 루틴은 먼저 말씀드린 
NYAOS의 부트섹터 소스 설명을 참고 하시구요(^^). 본인이 수정/삽입한 중요 부분들의 설명을 이어 나가겠습니다.^^ 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
MOV AX,0x8000           ;80000번지로 이동...
MOV ES,AX                             
XOR DI,DI                             
MOV AX,0x07C0                           
MOV DS,AX                             
XOR SI,SI                             
MOV CX,0x0100                           
REPZ                                      
MOVSW             
            
JMP 0x8000:go       ;0x8000:go 의 위치로 점프           
             
go: mov ax,0x8000
    mov ds,ax 

위의 코드는 현재 메모리의 0x07c0:0x0000의 위치에 있는 부트섹터 코드를 물리주소0x80000번지로 이동시킵니다. 이동이 끝난후 당연히 수행된 코드의 다음으로 점프를 해야겠죠? 그렇기에 JMP 0x8000:go 명령을 내립니다. 그리고 DS레지스터를 이동된 세그먼트 위치 0x8000으로 설정 합니다.
  
이후, 루트 디렉토리부터 파일이름으로 지정된 커널이미지를 검색하고, 찾으면 물리주소0x40000번지에 이미지를 로드 합니다. 커널이미지 까지 로드된 메모리의 모습은 아래와 같습니다.
  
그런 다음 플로피 디스크 드라이브의 모터를 아래의 코드와 같이 끄고, 
1
2
3
mov al, 0x0c
mov dx, 0x03fe
out dx, al
죄총적으로 커널 이미지를 다시 최하위 
0x0000:0000번지로 이동 시킨 후, 물리주소 0x01000번지로 점프 합니다. 0x1000번지부터 커널이미지의 실제 코드가 시작되기 때문입니다. (컴파일 된 커널이미지를 덤프해 보시면 압니다.^^) 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cli
          
MOV AX,0x0000           ;40000번지에서 0x0000:0000으로 이동...
MOV ES,AX                             
XOR DI,DI                             
MOV AX,0x4000                           
MOV DS,AX                             
XOR SI,SI                             
MOV CX,0x7000                           
REPZ                                      
MOVSW                   
            
mov ax,0x0000               ; set segment registers and jump
mov es,ax
 
mov ds,ax
jmp 0x0000:0x1000

아래의 그림은 부트섹터와 커널이미지가 로드된 후 전체적인 호출 순서를 보여 줍니다. 제일 먼저 부트섹터는 Entry.asm에 의해 컴파일 된 Entry.obj로 점프하게 되며 Entry.obj는 커널의 main함수를 호출하게 됩니다.
Entry.asm이 하는 일은 CPU를 보호모드로 변환함과 동시에 GDT,IDT를 세팅하는 역할을 합니다. 그작업이 끝나면 곧바로 커널의 main함수를 호출합니다.
  

실전! (Build)

네^^; 이제부터 실제로 부트섹터와 운영체제를 빌드해 봅시다. 먼저, 부트섹터를 컴파일 하는 방법은 아래와 같습니다.nasm이 당연히 설치되어 있어야 겠죠?
: (bootsect.asm 컴파일 방법)
  
부트섹터를 디스켙의 첫번째 섹터에 옮기는 방법은 몇가지가 있습니다. 예를 들면 도스의 디버거에서 아래와 같이 하셔도 상관없습니다.
: (bootsect를 디스켙의 첫번째 섹터에 옮기는 방법)
  
그렇지만, 좀더 멋지게- 우리만의 운영체제 인스톨러를 만들고 싶다면, 부트섹터를 복사해주는 코드를 가지고 있어도 나쁘진 않겠죠? scopy.c의 코드는 아래와 같이 간단합니다. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//***************START************************
#include "bios.h"
#include "stdio.h"
  
void main()
{
      FILE *in;
       unsigned char buffer[520]; /* 버퍼를 잡고 */
 
        if((in = fopen("bootsect", "rb"))==NULL) /* bootsect 파일을 바이너리로 연다음 */
        {
                printf("Error loading file\n");
                exit(0);
       }
  
        fread(&buffer, 512, 1, in); /* 512바이트를 읽고 */
  
       while(biosdisk(3, 0, 0, 0, 1, 1, buffer)); /* 디스크의 첫섹터에 씁니다 */
  
        fclose(in);
}
//***************E N D************************

아래의 방법으로 scopy.c를 컴파일 합니다.
: (scopy.c 컴파일 방법)
  
컴파일된 scopy.exe와 같은 디렉토리에 컴파일된 bootsect파일이 있으면 단지, scopy.exe를 실행하는 것만으로 디스켙의 첫번째 섹터에 bootsect가 옮겨집니다.
그리고, Entry.asm을 컴파일 해야겠죠? nmake가 세팅되어 있다면, Entry.asm디렉토리에서 아래그림과 같이 nmake만 실행하면 됩니다.
: (Entry.asm 컴파일 방법)
  
만약, 그렇지 않다면, 아래 그림처럼 ml.exe 파일을 Entry디렉토리에 복사한 후 옵션과 함께 ml.exe를 실행합니다.
: (Entry.asm 컴파일 방법)
  
ml.exe가 Path되어 있다면, Visual C에서 MyTask.dsw 프로젝트는 제대로 컴파일이 되어 MyTask.IMG 파일을 생성할 겁니다. 만약 그렇지 않다면, ml.exe를 MyTask디렉토리에 복사한 후에 F7키를 눌러 빌드하세요. (물론! 프로젝트 리소스 탶에서 os_cpu_a.asm를 선택하고 Project -> Setting에서 컴파일러 패스를 수정하셔도 됩니다.)
  
자, 이제 빌드는 모두 끝났습니다. 부트섹터까지 디스켙에 옮겼다면, MyTask.IMG파일만 디스켙에 복사해 주면 됩니다. 만약, MyTask.Img라는 커널이미지 이름이 마음에 안든다면 bootsect.asm의 소스코드의 젤 아랫줄 근처를 보시면'MyTask__IMG'라는 데이터가 보일겁니다. 이부분을 12칸에 맞춰서 바꿔 주시면 됩니다. 즉, 커널이미지를 1.IMG라는 파일로 명명하고 싶다면, '1_______IMG'로 바꾸시면 됩니다.(여기서 '_'는 공백입니다.) 
1
FileName    db "MYTASK  IMG"

이젠, 모든 것이 준비된 디스켙을 가지고 부팅만 하시면 됩니다. 자주 수정하고, 부팅하고 또 에러를 확인한 후 다시 부팅하는 과정이 너무 버겁습니다. 그럴땐 VMware®나 Bochs등을 사용하시면 편할 겁니다.(저의 경우엔 Bochs보단 VMware®가 좀더 잘 동작하는 것 같았습니다.)
  

  
이제 몰하나?

^^; 이제 몰 하다니요. 이젠 자신만의 커널 알고리즘을 개발하여 운영체제를 만드셔야죠.(ㅋ...) 즉, ucos-ii.obj파일을 제거하고 기타 Task관련 소스들을 제거하거나 수정해서 자신만의 운영체제를 만드는 겁니다. 물론, 기존 커널을 그대로 이용하셔도 상관없습니다. 자신만의 UI를 개발하여 포팅하고, 좀 더 나아가서 파일 시스템, 네트웍 그리고 사운드 관련 드라이버도 작성해 보는 등 갈길이 멀답니다.
이글로 인해 그 모든 절차나 길을 조금이라도 보았으면 하는 바람입니다.
건투를 빕니다!

2003.09.02 이건우

 


기타 질문&답변 : [ 질문&답변 게시판 ]

 참고 자료 : 
	- 디스크 부팅 이미지 만들기 가이드
	- PC 보호모드로 포팅된 uCOS-II 에 인터럽트 추가 예제

 참고 사이트 :
	- 오재준님이 작성하신 Bellona 운영체제 사이트입니다. http://www.bellona2.com/
	- 기타 관련사이트는 OS PAGE에 연결된 링크를 참조하시기 바랍니다.



원본이 있는 사이트 : http://airpage.org/
원본 주소 : http://airpage.org/xe/index.php?mid=airpos_vc_1