于江濤,曲 波
(1.通化師范學院 計算機科學系,吉林 通化 134002;2.南京曉莊學院 數(shù)學與信息技術學院,江蘇 南京 211171)
作為一種開源軟件,Linux操作系統(tǒng)自問世以來發(fā)展迅速,成為主流操作系統(tǒng)之一.由于其開放性,被國內外眾多大學作為操作系統(tǒng)課程的教學平臺及實驗平臺.然而隨著Linux版本的不斷增長,其代碼量也飛速膨脹,當前Linux操作系統(tǒng)代碼量已達數(shù)百萬行之多.對如此龐大的源代碼作分析研究,對于普通工程技術人員及本科計算機專業(yè)學生而言顯然是無能為力的.
Linux 0.01是Linux操作系統(tǒng)創(chuàng)始人Linus最早實現(xiàn)的一個操作系統(tǒng)版本,其全部代碼總共只有9000行左右.盡管代碼量不是很大,卻實現(xiàn)了操作系統(tǒng)所需要的基本功能[1].
首先,它是一個真正的多任務系統(tǒng),具有內核模態(tài)和用戶模態(tài),通過進程實現(xiàn)系統(tǒng)的多任務功能.其次,它具備了多用戶系統(tǒng)的基本功能;盡管沒有多用戶登錄功能,但在很多方面,如文件的訪問權限、進程的信號處理等,都已經體現(xiàn)出多用戶的管理機制.第三,它具備了現(xiàn)代操作系統(tǒng)的四個主要部分:進程管理、設備管理、內存管理及文件系統(tǒng).第四,它是一個真正的操作系統(tǒng),能夠在X86保護模式硬件環(huán)境下獨立啟動,而不是一個寄生在其它操作系統(tǒng)環(huán)境下的模擬系統(tǒng).第五,它可以在Shell環(huán)境下運行,執(zhí)行常用的Linux基本命令.
總之,Linux 0.01結構清晰、代碼規(guī)模較小,比較適合于初學者分析學習.
由于Linux 0.01是Linux的早期版本,因此其編譯環(huán)境及運行環(huán)境與當前版本有巨大差別,而當年的開發(fā)及運行環(huán)境已不復存在,所以無法對其原始版本直接編譯運行,需要對其源碼作必要修改.然而從目前情況來看,各種修改版本基本上只是實現(xiàn)了在當前操作系統(tǒng)環(huán)境下的編譯鏈接.所謂運行,也只是在X86保護方式下引導啟動,并沒有實現(xiàn)根文件系統(tǒng),因此也就無法使用Shell環(huán)境.
對Linux 0.01及Linux 0.11的文件系統(tǒng)源代碼[2,3]進行對比分析,不難發(fā)現(xiàn)二者在文件系統(tǒng)方面沒有本質差別.二者都是使用早期的Minix文件系統(tǒng),尤其是其運行可執(zhí)行文件部分的主要代碼基本一致.筆者參照Linux 0.11源代碼,對Linux0.01與Shell運行相關的代碼部分作了精心修改,并增加了動態(tài)鏈接系統(tǒng)調用,使用Linux 0.11的根文件系統(tǒng),成功實現(xiàn)了Linux 0.01的啟動運行,可在Shell環(huán)境下執(zhí)行Linux基本命令,還可用GCC編譯運行C語言程序.
Linux 0.01改造[1]的關鍵技術在于:
(1)編譯環(huán)境的選擇.考慮到系統(tǒng)穩(wěn)定性、可靠性及方便性,筆者采用Redhat 9.0平臺作為開發(fā)環(huán)境,GNU工具鏈作為開發(fā)工具.
由于Linux 0.01使用了匯編程序,因此涉及到匯編器的選擇.Linux 0.01從一開始就使用GNU工具鏈作開發(fā)工具,而且除boot.s外的所有匯編程序都使用AT&T語法,所以使用GNU工具鏈的AT&T語法匯編器是最佳選擇.
(2)源代碼語法的修改.由于匯編器及編譯器的改變,涉及到源代碼語法的差別.在匯編語言程序中,主要是變量名的命名等;在C語言程序中主要是內嵌匯編(embedded assembly)語法等.
(3)源程序功能的修改.主要是init()函數(shù)的修改,使之更適于Shell命令調用;其次是增加與動態(tài)鏈接相關的系統(tǒng)調用.
由于多數(shù)Shell命令運行時需要動態(tài)鏈接庫支持,所以動態(tài)鏈接系統(tǒng)調用是必要的先決條件.
(4)根文件系統(tǒng)的創(chuàng)建.根文件系統(tǒng)是Linux0.01啟動運行的基礎,由于編譯版本及文件系統(tǒng)結構的原因,不能直接采用當前Linux操作系統(tǒng)的根文件系統(tǒng).筆者在網上下載了多個Linux 0.11版本的根文件系統(tǒng),經實驗比較,最后選定了一個較合適的版本作為Linux 0.01的根文件系統(tǒng),其鏡像文件容量為60M,CHS結構為121個柱面、4個磁頭、每道63扇區(qū).
(1)修改Makefile文件.首先,修改工具名稱,gas改為as,gld改為ld,gar改為ar.其次,修改匯編選項,將匯編器(as)的-c選項去掉,將編譯器(gcc)的-fcombin-regs及-mstring-insns選項去掉.第三,增加-m386選項,使之不含80486及以上CPU指令,使內核可以在80386機器上運行.
所有子目錄中的Makefile都要依據(jù)上述要求作相應修改.
(2)改寫boot/boot.s文件.該文件是Linux 0.01的bootloader,當年Linus是用as86匯編語言編寫的.為統(tǒng)一使用GNU工具鏈匯編器as,需要將該文件按AT&T語法重寫.
由于該程序規(guī)模較小,直接手工改寫并不困難.也可以用INTEL-AT&T轉換工具,但一般也還需要手工整理.
值得注意的是,as匯編后的默認格式為ELF,要將boot目標文件作為引導扇區(qū)鏡像,還需要用objcopy將其轉換成純二進制鏡像文件.
與之相關,tools/build.c也要作相應修改,以適應新的boot鏡像文件.
(3)匯編程序的修改.首先是C語言程序引用變量的表示方法,早期的匯編程序要在引用變量前加一下劃線“_”,而現(xiàn)在的GCC編譯器可直接識別,因此需要將所有引用變量前的下劃線去掉.其次是內存對齊指令align值的修改,早期版本用乘方數(shù)表示,而現(xiàn)在版本直接使用其值(如早期的align 2,現(xiàn)在表示為align 4).
(4)C語言內嵌匯編的修改.Linus當年在Linux 0.01中大量使用內嵌匯編.隨著匯編器和編譯器版本的增加,內嵌匯編的語法有很大改變,主要是不再需要人工指定變量所使用的CPU寄存器.
因此要去掉所有_asm__(“ax”)代碼,以及內嵌匯編代碼中所有對寄存器內容的無效聲明,例如:
…:“cx”,“di”,“si”)
應改為
…)
筆者對init()函數(shù)作了修改,使之更適用于Shell的運行,其關鍵代碼如下:
static char * argv[] ={“-”,NULL};
static char * envp[]={“HOME=/usr/root”,NULL};
static char * vmsg=“ Linux-0.01-rh9===”;
static char * gmsg=“Adapted... ”;
void init(void)
{int i,pid;
setup();
(void) open(“/dev/tty0”,O_RDWR,0);
(void) dup(0);(void) dup(0);
printf(“%d buffers=%d bytes buffer space ”,NR_BUFFERS,NR_BUFFERS*BLOCK_SIZE);
printf(vmsg);printf(gmsg);
while(1){
if((i=fork())<0)
printf(“Fork failed in init ”);
else if (!i){
close(0);close(1);close(2);
setsid();
(void) open(“/dev/tty0”,O_RDWR,0);
(void) dup(0);(void) dup(0);
_exit(execve(“/bin/sh”,argv,envp));
}
pid=wait(&i);
printf(“child %d died with code %04x ”,pid,i);
sync();
}
_exit(0);/* NOTE! _exit, not exit()*/
}
該函數(shù)首先調用setup()函數(shù),讀入硬盤分區(qū)參數(shù),裝入(mount)根文件系統(tǒng);然后,用讀寫方式打開tty終端設備,并返回標準輸入設備句柄,dup(0)用來復制句柄,產生標準輸出及標準錯誤輸出句柄;接下來顯示系統(tǒng)信息及版本信息;最后進入無限循環(huán),在其中創(chuàng)建子進程,在子進程中首先關閉父進程的標準輸入、標準輸出及標準錯誤輸出,然后重新以讀寫方式打開tty設備并復制及柄;最后調用execve()函數(shù),由該函數(shù)將Shell命令(/bin/sh)裝入內存并運行;wait()函數(shù)使父進程暫時阻塞,等待子進程終止.
當子進程中的Shell程序終止時,引起_exit()函數(shù)的執(zhí)行返回,使子進程終止;這時,父進程被喚醒,產生一條系統(tǒng)提示后進入下一輪循環(huán).
筆者基于Redhat 9.0平臺,按照上述方法成功改造了Linux 0.01系統(tǒng),在bochs虛擬機成功運行,其運行腳本的關鍵代碼如下:
megs:16
floppya:1_44=“l(fā)inux-fd.img”,status=inserted
ata0-master: type=disk,path=“hdc-0.11.img”,mode=flat,cylinders=121,heads=16,spt=63
boot: a
上述腳本表示:虛擬機內存容量為16M;使用的軟盤鏡像文件為linux-fd.img;硬盤鏡像文件為hdc-0.11.img,其CHS結構為121個柱面、16個磁頭、每道63個扇區(qū);操作系統(tǒng)由A盤引導啟動.
圖1顯示了運行的結果.操作系統(tǒng)啟動后,首先顯示系統(tǒng)信息及版本信息,然后調用Shell命令,等待用戶鍵盤輸入.
在本例中,演示了如下命令:
df命令,列出了當前裝入系統(tǒng)的文件設備;
pwd命令,列出當前工作目錄;
ls命令,顯示當前目錄內容;
rm命令,刪除hello文件;
gcc命令,編譯鏈接C語言程序hello.c,生成可執(zhí)行文件hello.
./hello,運行用GCC編譯鏈接生成的可執(zhí)行文件hello,顯示運行結果:Hello,world!
圖1 Linux 0.01改進版的運行演示
Linux 0.01雖然是Linux的早期版本,但具備了操作系統(tǒng)最重要最基本的組成部分;其規(guī)模較小,便于初學者及計算機本科專業(yè)學生學習研究.本文提出的方法實現(xiàn)了Linux 0.01版在Redhat 9.0平臺下的編譯鏈接,并在bochs虛擬機上運行成功,具有重要的實用價值.
參考文獻:
[1]盧軍.Linux 0.01內核分析與操作系統(tǒng)設計[M].北京:清華大學出版社,2005.
[2]Pramode C.E,Gopakumar C.E.The Linux Kernel 0.01 Commentary [EB/OL].http://ranger.uta.edu/~dliu/courses/os/kc.pdf,2008.
[3]趙炯.Linux內核完全注釋[M].北京:機械工業(yè)出版社,2007.