怎樣用C語言開發作業系統[1]
--------------------------------------------------------------------------------
作者:tianxiangyuan 來源:原創 更新日期:2005-01-07 瀏覽次數:24
儘管我的題目是“用C語言寫作業系統”,但是,僅僅使用C語言是寫不出作業系統的。我看到很多篇關於自己動手開發作業系統的文章,幾乎全來自一篇叫“Write Your Own Operating System Tutorial”英文文章,而且,使用的全是組合語言。如今能夠精通組合語言的人已屬鳳毛麟角,而且現代作業系統的主體部分是用C語言寫的,難道所謂的“Own Operating System”只能用組合語言嗎?在下不才,使用C語言(在某些部分必須結合組合語言)寫了一個可以與上述文章中提到的作業系統相媲美的作業系統,拋磚引玉,希望能夠將其發展成為真正的中國人自己的作業系統。
我是在Windows環境下開發的,而不是大多數人選擇的Linux環境。開發工具也是Microsoft公司的開發工具:masm615和VC15。微軟公司的masm流傳甚廣,大家應該不陌生。對vc15可能較陌生,vc15堪稱開發DOS程式的最“高檔”編譯器(儘管有很多BUG)。如果實在找不到這些開發工具,使用TASM、Turbo C或者Borland C++都是可以的。所給出的例子以masm615和vc15為準,轉移到TASM、TC或BC平台,應該不難。有一條原則需要注意,在這裡,源碼必須使用TINY模式編譯,也就是說,必鬚生成實模式代碼。
1. 建立開發環境
這一步非常的簡單。
將masm613和vc15的壓縮包分別解壓到e:/masm615和e:/msvc15目錄下。你也可以放到其他目錄下,根據自己的情況而定,但是下面用到的編譯命令需要作相應的修改。也不需要添加或修改任何的環境變數。
2. IBM PC的啟動及當時的記憶體使用量情況
這一部分內容已經是老生常談了,但又不能不說。我們只說從硬碟引導的情況。
當BIOS經過POST(Power On Test Self)後,將硬碟MBR讀到記憶體0x0000:0x7C00的位置,然後從這裡開始執行。一般的情況,MBR將選擇使用中的磁碟分割進行作業系統的啟動。在MBR開始執行時,記憶體使用量的情況如所示,地址資料用16進位表示:
這已經是老掉牙的內容了,但是,在20年前卻十分流行。如果想更詳細的瞭解這方面的內容,找本講解DOS的書看看吧。
我們自己的作業系統將被載入到0x1000:0x0100。這不是必需或者必然的,是人為選擇的,你也可以將其放在0x4321:1234等其他地方。但是,中註明有其他用途的記憶體地區,應該保留,否則,你會後悔的。
怎樣用C語言開發作業系統[2]
--------------------------------------------------------------------------------
作者:tianxiangyuan 來源:原創 更新日期:2005-01-07 瀏覽次數:29
3. 開發作業系統
我們自己的作業系統運行在實模式環境下(如果您不知道什麼是實模式,也請看看20年前出版的當時非常流行的書,或者直接請教當時的前輩高手)。即使你的電腦是P4的CPU,剛啟動時,也只相當於主頻較高的8086而已。但是,沒有關係。
首先,使用組合語言寫一個架構,檔案名稱是entry.asm:
;
; entry.asm
; Copyright (C) 2004, Tian XiangYuan
;
.MODEL TINY,C
.386p
option expr32
option casemap:none
cmain PROTO NEAR C
.CODE
ORG 0100h ;位移地址
_start:
jmp begin
nop
DB 'TianXiangYuan',0 ;the magic of my os
begin:
cli
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0FFFFh
sti
call cmain ;調用C語言寫的主函數
mov ax,4c00h ;調用DOS的功能(為了調試),與我們自己的作業系統無關
int 21h
這段代碼非常簡單,應該沒有什麼問題。
已經說了,作業系統將從0x1000:0x0100載入,說是無心,實則有意。我們知道,TINY模式的程式,在DOS下運行時,其起始地址就是0x0100,前面的256Byte是參數部分。如果直接將作業系統在系統啟動時載入到0x1000:0x0100,調試時非常麻煩。我們將其起始地址設為0x0100,使其可以在DOS下運行(這也是在程式的最後包含int 21h指令的原因),確認正確無誤後,再進行下一步的開發。
下面再看C語言的代碼,檔案名稱是main.c:
……
static void InitShell()
{
}
void cmain()
{
InitShell();
TermShell();
}
顧名思義,其中實現了一個簡單的shell。因為該程式本身是作業系統的一部分,所以,平時經常使用的一些C庫函數,在這裡就不能使用了。總之,一切都要自己動手實現。幸好,在實模式下,幾乎所有的裝置的驅動都包含在BIOS中了,我們可以直接使用。否則,連從鍵盤讀一個索引值這樣的事都需要自己寫鍵盤的驅動程式,實在太難了。也是這個原因,我們自己的作業系統沒有將CPU轉到保護模式下,有心之人可以試試。
下面的事情幾乎都可以使用C語言實現了。
第一,初始化顯示模式。系統啟動時,顯卡已經被初始化成3模式了,就是80X25的彩色模式(除非你的顯示器是單色顯示器),我們不需要再做什麼了。當然,你也可以將顯卡設成VGA甚至SVGA模式,只要你的BIOS和顯卡支援。
第二,實現一個具有簡單互動功能的shell。代碼不全,請自己補齊,或參看附件。
/*
*從鍵盤讀一個字元,如果沒有輸入,則等待;傳回值的低位元組為asii碼,高位元組為鍵盤掃描碼
*/
static int getch()
{
int chr=0;
__asm
{
mov ah,00h
int 16h
mov chr,ax
}
return chr;
}
/*
*使用TTY模式向螢幕輸出一個字元
*/
static void putch(unsigned char key)
{
__asm
{
mov bh,0
mov al,key
mov ah,0Eh
int 10h
}
}
#define KEY_BACKSPACE 0x08
#define KEY_ENTER 0x0D
#define KEY_NEWLINE 0x0A
#define KEY_ESCAPE 0x1B
static int printk(const char* str,...)
{
…… //給大家一點空間,自己實現吧
}
static void endline()
{
putch(KEY_NEWLINE); //Line Feed (LF)
putch(KEY_ENTER); //Enter (CR)
}
static char msg_prompt[]="CMD:";
static void deal_cmd(char* cmd_line,int cmd_len)
{
…… //也請大家自己實現吧,例如,可以實現help,dir,cls,halt等命令
…… //其實,就是字串比較的過程
}
static void TermShell()
{
char cmd_line[80]={0,};
int cmd_len=0;
endline();
printk(msg_prompt,sizeof(msg_prompt));
for (;;)
{
cmd_line[cmd_len]=getch();
switch(cmd_line[cmd_len])
{
case KEY_ENTER:
if (cmd_len>1)
deal_cmd(cmd_line,cmd_len);
//break;
case KEY_ESCAPE:
cmd_len=0;
endline();
printk(msg_prompt,sizeof(msg_prompt));
break;
case KEY_BACKSPACE:
if (cmd_len>0)
{
putch(0x08);
putch(' ');
putch(0x08);
cmd_len--;
}
break;
default:
putch(cmd_line[cmd_len]);
cmd_len++;
}
}
}
更複雜、功能更強大的方法請參考BIOS的相關文檔。也請大家發揮想象力,不斷的擴充功能。說心裡話,這個 “作業系統”比dos還原始!但畢竟是自己的作業系統。
4. 編譯方法
下面是build.bat的內容。各種編譯選項參看相關編譯器的說明文檔。
作系統載入到記憶體的0x1000:0x0100,並且cpu工作在實模式下。如果實在找不到合適的引導程式,只有自己動手寫一個了。
@echo off
set PATH=e:/masm615/bin;e:/msvc15/bin;
set AS=e:/masm615/bin/ml.exe
set AFLAGS=/AT /W3 /X /Gd /Zp1 /nologo
set CC=e:/msvc15/bin/cl.exe
set CFLAGS=/Od /G3 /Gd /Gs /Zl /Zp1 /X /W3 /nologo
del *.obj *.com *.cod *.dbg *.pdb *.map *.lst
%AS% /c %AFLAGS% entry.asm
if errorlevel 1 goto error
%CC% /c %CFLAGS% /Fc main.c
if errorlevel 1 goto error
%AS% %AFLAGS% /Fe"boot.com" entry.obj main.obj
if errorlevel 1 goto error
goto exit
:error
echo Failure......
:exit
pause
@echo on
5. 引導程式
我們自己的作業系統雖然寫完了,但是還沒有辦法引導,使其擁有系統的控制權。你可以使用第三方的引導程式,只要它能夠將我們的作業系統載入到記憶體的0x1000:0x0100,並且cpu工作在實模式下。如果實在找不到合適的引導程式,只有自己動手寫一個了。
;
; bootsect.asm
; Copyright(C) 2004, Tian XiangYuan
;
.MODEL TINY,C
.386p
option expr32
option casemap:none
SYSSEG EQU 1000h
SYSOFF EQU 0100h
.CODE
ORG 7C00h
_start:
jmp begin
nop
DB 'BOOTSECT',0 ;magic
pack_size DB 16
DB 0 ;reserved
DW 60 ;sectors
DW SYSOFF ;buf_addr_off
DW SYSSEG ;buf_addr_seg
DD 2 ;sector_from
DD 0 ;sector_from_high
begin:
cli
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0FFFFh
sti
mov cx,msg_load_len ;length
lea bp,msg_load ;es:bp
call display_msg
; read disk for my OS
lea si,pack_size
mov dl,80h
mov ax,4200h
int 13h ;使用LBA方式讀硬碟
jc error
; test magic of my os
lea si,magic_test ;ds:si
mov ax,SYSSEG
mov es,ax
mov di,SYSOFF
add di,3 ;es:di
mov cx,magic_test_len
cld
test_again:
cmpsb
jnz error
loop test_again
push SYSSEG
push SYSOFF
retf ;轉入作業系統執行
error:
mov ax,cs
mov es,ax
lea bp,msg_error ;es:bp
mov cx,msg_error_len
call display_msg
failure:
hlt
jmp failure
;cx : length of message
;es:bp : address of message
;void display_msg();
display_msg PROC NEAR C
;scrollup a line
push cx
push bp
mov ax,0601h
mov bh,07h
mov cx,0000h ;y/x
mov dx,184Fh ;y2/x2, 24/79
int 10h
pop bp
pop cx
;display message
mov ax,1301h
mov bx,000Ah
;mov cx,msg_error_len
mov dl,0 ;x
mov dh,24 ;y
;lea bp,msg_error ;es:bp
int 10h
ret
display_msg endp
.DATA
msg_load DB 'Loading......',0
msg_load_len DW $ - msg_load
msg_error DB 'NO BOOTER,please reboot!',0
msg_error_len DW $ - msg_error
magic_test DB 'TianXiangYuan',0
magic_test_len DW $ - magic_test
end _start
上面這段代碼,是MBR的內容,他使用LBA方式讀硬碟(支援大硬碟),將我們自己的作業系統讀入0x1000:0x0100,然後轉入作業系統執行。
很顯然,我們的作業系統必須放在硬碟的No.2(LBA定位方式)扇區開始的60個扇區之內(實際上我們的作業系統遠沒有這麼大),這時我們的引導程式的硬性規定,如果使用第三方的引導程式,也許會更方便。
可以自己開發安裝程式,也可以使用WinHex工具將其寫入硬碟。請一定注意資料安全,不要將硬碟分區搞壞,建議做好備份工作,或者使用沒有重要資料的測試硬碟。順便說一下,我是使用WinHex寫入測試硬碟的。
這段引導程式的編譯方式與作業系統一樣,build.dat檔案如下:
@echo off
set PATH=e:/masm615/bin;
set AS=e:/masm615/bin/ml.exe
set AFLAGS=/AT /W3 /WX /Gd /Zp1 /X /nologo
del *.obj *.com *.cod *.dbg *.pdb *.map
%AS% /c %AFLAGS% bootsect.asm
if errorlevel 1 goto error
%AS% %AFLAGS% /Fe"bootsect.com" bootsect.obj
if errorlevel 1 goto error
goto exit
:error
echo Failure......
:exit
pause
@echo on
6. 後記
我們自己的作業系統太原始了,連DOS都不如!在這裡,主要介紹的是一種開發方法,旨在說明使用C語言如何開發像作業系統這樣底層的軟體。
閑暇時,將其移至Linux平台下開發,並且使用cpu的保護模式,畢竟這樣才能發揮80x86系列cpu的強大功能。
歡迎提出寶貴意見:tianxiangyuan@sina.com.cn