于江濤,曲 波
(1.通化師范學(xué)院 計(jì)算機(jī)科學(xué)系,吉林 通化 134002;2.南京曉莊學(xué)院 數(shù)學(xué)與信息技術(shù)學(xué)院,江蘇 南京 211171)
作為一種開(kāi)源軟件,Linux操作系統(tǒng)自問(wèn)世以來(lái)發(fā)展迅速,成為主流操作系統(tǒng)之一.由于其開(kāi)放性,被國(guó)內(nèi)外眾多大學(xué)作為操作系統(tǒng)課程的教學(xué)平臺(tái)及實(shí)驗(yàn)平臺(tái).然而隨著Linux版本的不斷增長(zhǎng),其代碼量也飛速膨脹,當(dāng)前Linux操作系統(tǒng)代碼量已達(dá)數(shù)百萬(wàn)行之多.對(duì)如此龐大的源代碼作分析研究,對(duì)于普通工程技術(shù)人員及本科計(jì)算機(jī)專(zhuān)業(yè)學(xué)生而言顯然是無(wú)能為力的.
Linux 0.01是Linux操作系統(tǒng)創(chuàng)始人Linus最早實(shí)現(xiàn)的一個(gè)操作系統(tǒng)版本,其全部代碼總共只有9000行左右.盡管代碼量不是很大,卻實(shí)現(xiàn)了操作系統(tǒng)所需要的基本功能[1].
首先,它是一個(gè)真正的多任務(wù)系統(tǒng),具有內(nèi)核模態(tài)和用戶(hù)模態(tài),通過(guò)進(jìn)程實(shí)現(xiàn)系統(tǒng)的多任務(wù)功能.其次,它具備了多用戶(hù)系統(tǒng)的基本功能;盡管沒(méi)有多用戶(hù)登錄功能,但在很多方面,如文件的訪問(wèn)權(quán)限、進(jìn)程的信號(hào)處理等,都已經(jīng)體現(xiàn)出多用戶(hù)的管理機(jī)制.第三,它具備了現(xiàn)代操作系統(tǒng)的四個(gè)主要部分:進(jìn)程管理、設(shè)備管理、內(nèi)存管理及文件系統(tǒng).第四,它是一個(gè)真正的操作系統(tǒng),能夠在X86保護(hù)模式硬件環(huán)境下獨(dú)立啟動(dòng),而不是一個(gè)寄生在其它操作系統(tǒng)環(huán)境下的模擬系統(tǒng).第五,它可以在Shell環(huán)境下運(yùn)行,執(zhí)行常用的Linux基本命令.
總之,Linux 0.01結(jié)構(gòu)清晰、代碼規(guī)模較小,比較適合于初學(xué)者分析學(xué)習(xí).
由于Linux 0.01是Linux的早期版本,因此其編譯環(huán)境及運(yùn)行環(huán)境與當(dāng)前版本有巨大差別,而當(dāng)年的開(kāi)發(fā)及運(yùn)行環(huán)境已不復(fù)存在,所以無(wú)法對(duì)其原始版本直接編譯運(yùn)行,需要對(duì)其源碼作必要修改.然而從目前情況來(lái)看,各種修改版本基本上只是實(shí)現(xiàn)了在當(dāng)前操作系統(tǒng)環(huán)境下的編譯鏈接.所謂運(yùn)行,也只是在X86保護(hù)方式下引導(dǎo)啟動(dòng),并沒(méi)有實(shí)現(xiàn)根文件系統(tǒng),因此也就無(wú)法使用Shell環(huán)境.
對(duì)Linux 0.01及Linux 0.11的文件系統(tǒng)源代碼[2,3]進(jìn)行對(duì)比分析,不難發(fā)現(xiàn)二者在文件系統(tǒng)方面沒(méi)有本質(zhì)差別.二者都是使用早期的Minix文件系統(tǒng),尤其是其運(yùn)行可執(zhí)行文件部分的主要代碼基本一致.筆者參照Linux 0.11源代碼,對(duì)Linux0.01與Shell運(yùn)行相關(guān)的代碼部分作了精心修改,并增加了動(dòng)態(tài)鏈接系統(tǒng)調(diào)用,使用Linux 0.11的根文件系統(tǒng),成功實(shí)現(xiàn)了Linux 0.01的啟動(dòng)運(yùn)行,可在Shell環(huán)境下執(zhí)行Linux基本命令,還可用GCC編譯運(yùn)行C語(yǔ)言程序.
Linux 0.01改造[1]的關(guān)鍵技術(shù)在于:
(1)編譯環(huán)境的選擇.考慮到系統(tǒng)穩(wěn)定性、可靠性及方便性,筆者采用Redhat 9.0平臺(tái)作為開(kāi)發(fā)環(huán)境,GNU工具鏈作為開(kāi)發(fā)工具.
由于Linux 0.01使用了匯編程序,因此涉及到匯編器的選擇.Linux 0.01從一開(kāi)始就使用GNU工具鏈作開(kāi)發(fā)工具,而且除boot.s外的所有匯編程序都使用AT&T語(yǔ)法,所以使用GNU工具鏈的AT&T語(yǔ)法匯編器是最佳選擇.
(2)源代碼語(yǔ)法的修改.由于匯編器及編譯器的改變,涉及到源代碼語(yǔ)法的差別.在匯編語(yǔ)言程序中,主要是變量名的命名等;在C語(yǔ)言程序中主要是內(nèi)嵌匯編(embedded assembly)語(yǔ)法等.
(3)源程序功能的修改.主要是init()函數(shù)的修改,使之更適于Shell命令調(diào)用;其次是增加與動(dòng)態(tài)鏈接相關(guān)的系統(tǒng)調(diào)用.
由于多數(shù)Shell命令運(yùn)行時(shí)需要?jiǎng)討B(tài)鏈接庫(kù)支持,所以動(dòng)態(tài)鏈接系統(tǒng)調(diào)用是必要的先決條件.
(4)根文件系統(tǒng)的創(chuàng)建.根文件系統(tǒng)是Linux0.01啟動(dòng)運(yùn)行的基礎(chǔ),由于編譯版本及文件系統(tǒng)結(jié)構(gòu)的原因,不能直接采用當(dāng)前Linux操作系統(tǒng)的根文件系統(tǒng).筆者在網(wǎng)上下載了多個(gè)Linux 0.11版本的根文件系統(tǒng),經(jīng)實(shí)驗(yàn)比較,最后選定了一個(gè)較合適的版本作為L(zhǎng)inux 0.01的根文件系統(tǒng),其鏡像文件容量為60M,CHS結(jié)構(gòu)為121個(gè)柱面、4個(gè)磁頭、每道63扇區(qū).
(1)修改Makefile文件.首先,修改工具名稱(chēng),gas改為as,gld改為ld,gar改為ar.其次,修改匯編選項(xiàng),將匯編器(as)的-c選項(xiàng)去掉,將編譯器(gcc)的-fcombin-regs及-mstring-insns選項(xiàng)去掉.第三,增加-m386選項(xiàng),使之不含80486及以上CPU指令,使內(nèi)核可以在80386機(jī)器上運(yùn)行.
所有子目錄中的Makefile都要依據(jù)上述要求作相應(yīng)修改.
(2)改寫(xiě)boot/boot.s文件.該文件是Linux 0.01的bootloader,當(dāng)年Linus是用as86匯編語(yǔ)言編寫(xiě)的.為統(tǒng)一使用GNU工具鏈匯編器as,需要將該文件按AT&T語(yǔ)法重寫(xiě).
由于該程序規(guī)模較小,直接手工改寫(xiě)并不困難.也可以用INTEL-AT&T轉(zhuǎn)換工具,但一般也還需要手工整理.
值得注意的是,as匯編后的默認(rèn)格式為ELF,要將boot目標(biāo)文件作為引導(dǎo)扇區(qū)鏡像,還需要用objcopy將其轉(zhuǎn)換成純二進(jìn)制鏡像文件.
與之相關(guān),tools/build.c也要作相應(yīng)修改,以適應(yīng)新的boot鏡像文件.
(3)匯編程序的修改.首先是C語(yǔ)言程序引用變量的表示方法,早期的匯編程序要在引用變量前加一下劃線“_”,而現(xiàn)在的GCC編譯器可直接識(shí)別,因此需要將所有引用變量前的下劃線去掉.其次是內(nèi)存對(duì)齊指令align值的修改,早期版本用乘方數(shù)表示,而現(xiàn)在版本直接使用其值(如早期的align 2,現(xiàn)在表示為align 4).
(4)C語(yǔ)言?xún)?nèi)嵌匯編的修改.Linus當(dāng)年在Linux 0.01中大量使用內(nèi)嵌匯編.隨著匯編器和編譯器版本的增加,內(nèi)嵌匯編的語(yǔ)法有很大改變,主要是不再需要人工指定變量所使用的CPU寄存器.
因此要去掉所有_asm__(“ax”)代碼,以及內(nèi)嵌匯編代碼中所有對(duì)寄存器內(nèi)容的無(wú)效聲明,例如:
…:“cx”,“di”,“si”)
應(yīng)改為
…)
筆者對(duì)init()函數(shù)作了修改,使之更適用于Shell的運(yùn)行,其關(guān)鍵代碼如下:
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ù)首先調(diào)用setup()函數(shù),讀入硬盤(pán)分區(qū)參數(shù),裝入(mount)根文件系統(tǒng);然后,用讀寫(xiě)方式打開(kāi)tty終端設(shè)備,并返回標(biāo)準(zhǔn)輸入設(shè)備句柄,dup(0)用來(lái)復(fù)制句柄,產(chǎn)生標(biāo)準(zhǔn)輸出及標(biāo)準(zhǔn)錯(cuò)誤輸出句柄;接下來(lái)顯示系統(tǒng)信息及版本信息;最后進(jìn)入無(wú)限循環(huán),在其中創(chuàng)建子進(jìn)程,在子進(jìn)程中首先關(guān)閉父進(jìn)程的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出及標(biāo)準(zhǔn)錯(cuò)誤輸出,然后重新以讀寫(xiě)方式打開(kāi)tty設(shè)備并復(fù)制及柄;最后調(diào)用execve()函數(shù),由該函數(shù)將Shell命令(/bin/sh)裝入內(nèi)存并運(yùn)行;wait()函數(shù)使父進(jìn)程暫時(shí)阻塞,等待子進(jìn)程終止.
當(dāng)子進(jìn)程中的Shell程序終止時(shí),引起_exit()函數(shù)的執(zhí)行返回,使子進(jìn)程終止;這時(shí),父進(jìn)程被喚醒,產(chǎn)生一條系統(tǒng)提示后進(jìn)入下一輪循環(huán).
筆者基于Redhat 9.0平臺(tái),按照上述方法成功改造了Linux 0.01系統(tǒng),在bochs虛擬機(jī)成功運(yùn)行,其運(yùn)行腳本的關(guān)鍵代碼如下:
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
上述腳本表示:虛擬機(jī)內(nèi)存容量為16M;使用的軟盤(pán)鏡像文件為linux-fd.img;硬盤(pán)鏡像文件為hdc-0.11.img,其CHS結(jié)構(gòu)為121個(gè)柱面、16個(gè)磁頭、每道63個(gè)扇區(qū);操作系統(tǒng)由A盤(pán)引導(dǎo)啟動(dòng).
圖1顯示了運(yùn)行的結(jié)果.操作系統(tǒng)啟動(dòng)后,首先顯示系統(tǒng)信息及版本信息,然后調(diào)用Shell命令,等待用戶(hù)鍵盤(pán)輸入.
在本例中,演示了如下命令:
df命令,列出了當(dāng)前裝入系統(tǒng)的文件設(shè)備;
pwd命令,列出當(dāng)前工作目錄;
ls命令,顯示當(dāng)前目錄內(nèi)容;
rm命令,刪除hello文件;
gcc命令,編譯鏈接C語(yǔ)言程序hello.c,生成可執(zhí)行文件hello.
./hello,運(yùn)行用GCC編譯鏈接生成的可執(zhí)行文件hello,顯示運(yùn)行結(jié)果:Hello,world!

圖1 Linux 0.01改進(jìn)版的運(yùn)行演示
Linux 0.01雖然是Linux的早期版本,但具備了操作系統(tǒng)最重要最基本的組成部分;其規(guī)模較小,便于初學(xué)者及計(jì)算機(jī)本科專(zhuān)業(yè)學(xué)生學(xué)習(xí)研究.本文提出的方法實(shí)現(xiàn)了Linux 0.01版在Redhat 9.0平臺(tái)下的編譯鏈接,并在bochs虛擬機(jī)上運(yùn)行成功,具有重要的實(shí)用價(jià)值.
參考文獻(xiàn):
[1]盧軍.Linux 0.01內(nèi)核分析與操作系統(tǒng)設(shè)計(jì)[M].北京:清華大學(xué)出版社,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內(nèi)核完全注釋[M].北京:機(jī)械工業(yè)出版社,2007.