linux內(nèi)核ubuntu部落-實(shí)驗(yàn)教程_第1頁(yè)
linux內(nèi)核ubuntu部落-實(shí)驗(yàn)教程_第2頁(yè)
linux內(nèi)核ubuntu部落-實(shí)驗(yàn)教程_第3頁(yè)
linux內(nèi)核ubuntu部落-實(shí)驗(yàn)教程_第4頁(yè)
linux內(nèi)核ubuntu部落-實(shí)驗(yàn)教程_第5頁(yè)
已閱讀5頁(yè),還剩110頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第1章LinuxLab概 Linux0.11操作系 第2章Linux0.11編程基 Linux0.11內(nèi)核源代碼的結(jié) C和匯編的相 使用工具閱讀Linux0.11源代 實(shí)驗(yàn)一實(shí)驗(yàn)環(huán)境的使 實(shí)驗(yàn)二操作系統(tǒng)的啟 實(shí)驗(yàn)三 程序設(shè) 實(shí)驗(yàn)四系統(tǒng)調(diào) 實(shí)驗(yàn)五進(jìn)程的創(chuàng) 實(shí)驗(yàn)六進(jìn)程的狀態(tài)和進(jìn)程調(diào) 實(shí)驗(yàn)七信號(hào)量的實(shí)現(xiàn)和應(yīng) 實(shí)驗(yàn)八地址與內(nèi)存共 實(shí)驗(yàn)九頁(yè)面置換算法與動(dòng)態(tài)內(nèi)存分 實(shí)驗(yàn)符顯示的控 實(shí)驗(yàn)十一proc文件系統(tǒng)的實(shí) 實(shí)驗(yàn)十二MINIX1.0文件系統(tǒng)的實(shí) 1LinuxLab最好的參考資料是博士編寫(xiě)的《Linux內(nèi)核完全注釋》,其可以從OldLinux。Linux0.11其中包含的內(nèi)容基本上都是Linux的精髓。雖然讀者在使用Linux0.11的過(guò)會(huì)遇到很多處,才給了讀者的想象空間。Linux0.11C語(yǔ)言編寫(xiě),有少量的匯編語(yǔ)言代碼。Linux0.11開(kāi)放了全中文注釋?zhuān)岄喿x和理解Linux0.11源代碼更加容易。Linux0.11X86Linux0.11應(yīng)用程序之間(1-1所示扮演了極其重要的角色。一方面,Linux0.11X86平臺(tái)中的各種硬件進(jìn)行統(tǒng)一的管理,提高了系統(tǒng)資源的利用率。另一方面,Linux0.11操作系統(tǒng)提供了一個(gè)“虛擬機(jī)”和一Linux0.11Linux0.11操作系統(tǒng)組系統(tǒng)調(diào)用函數(shù),Linux0.11Linux0.11Linux0.11操作系統(tǒng)集成實(shí)驗(yàn)環(huán)LinuxLab是使用Linux0.11進(jìn)行操作系統(tǒng)實(shí)驗(yàn)的IDE(IntegratedDevelopmentEnvironmentIDE環(huán)境可以直接Windows操作系統(tǒng)上安裝和卸載,用戶界面和操作與VisualStudio完全類(lèi)似,有經(jīng)驗(yàn)的讀者可以迅速上手。正因?yàn)橛辛诉@樣一個(gè)要精力放在對(duì)操作系統(tǒng)原理和Linux0.11源代碼的分析與理解上。所示。編輯功能可以用來(lái)閱讀和修改Linux0.11源代碼;編譯功能可以將Linux0.11源代時(shí)顯示對(duì)應(yīng)位置的C碼等功能。靈活運(yùn)用各種調(diào)試功能對(duì)分析Linux0.11的源代碼有很大幫助。IDE環(huán)境除了提供以上的主要功能外,還提供了一些工具軟件。使用FloppyImageWindowsLinux0.11IDEBochsBochsLinux0.111-2:使IDE環(huán)境編輯、編Linux0.11(1-3:使LinuxLab進(jìn)行操作系Windows平臺(tái)上使GDB交叉調(diào)試內(nèi)核源代碼。并且,由于生成的調(diào)試信息能夠aoutLinuxLabGCC1.4為L(zhǎng)inux0.11開(kāi)發(fā)應(yīng)用程序的過(guò)程。(setup這與國(guó)內(nèi)讀者學(xué)習(xí)的IBM匯編語(yǔ)言相差較大。LinuxLab使用與IBM匯編語(yǔ)法更加原版中無(wú)法正常使用od、mkdir、rm行,從而允許讀者可以通過(guò)使用這些命令,來(lái)練習(xí)Linux中的基本文件操作。原版使用命令行工具在軟盤(pán)BFAT12內(nèi)的文件,操作十分不便。LinuxLabFloppyimageeditorBLinux0.11操作系統(tǒng)內(nèi)核從源代碼變?yōu)榭梢栽谔摂M機(jī)上時(shí),會(huì)將bootse 文件生成為setup.bin文件(加載程序),將head.s和其它的源代碼文件生成為linux011.bin文件。其中l(wèi)inux011.bin文件是Linux0.11操作系統(tǒng)的內(nèi)核。在IDE環(huán)境生成Linux0.11內(nèi)核項(xiàng)目時(shí),就會(huì)將bootsect.bin、setup.bin和linux011.binLinux0.11Linux0.111-4:Linux0.11操作系統(tǒng)內(nèi)核從源代碼變?yōu)榭梢栽谔摂M機(jī)上運(yùn)行的過(guò)Linux內(nèi)核集成實(shí)驗(yàn)環(huán)LinuxLab虛擬機(jī)工具BochsLinux0.11操作系統(tǒng),這里對(duì)Bochs這種虛擬機(jī)工具軟件進(jìn)行簡(jiǎn)單的介紹。BochsCIA-32(x86)PCInx86CPU、通用I/O設(shè)備以及可定制的BIOS程序。這種完全使用軟件模擬的方式又被(Emulatormachine。大多數(shù)操作系統(tǒng)都可以在Bochs上運(yùn)行,例如LinuxDOSWindows95/98/NT/2000/XP/Vista。由于Bochs完全使用軟件模擬X86硬件平臺(tái),所以可以用來(lái)調(diào)試BIOS程序和操作系統(tǒng)第一條指令)處開(kāi)始調(diào)試。LinuxLab正是利用了Bochs的這個(gè)特點(diǎn),使用Bochs調(diào)試軟盤(pán)行時(shí)占用大量CPU資源且性能較差。為了使Bochs能夠更好的調(diào)試Linux0.11操作系統(tǒng),對(duì)Bochs的源代碼進(jìn)行了必要的修改,重新編譯生成了能夠與LinuxLab無(wú)縫融合的Bochs版本。所以,必須使用與LinuxLab一起提供的Bochs,而不能直接使用Bochs提供的安裝包。類(lèi)命操csnqvbpbdinforx/nufn用數(shù)字代替,表示數(shù)量,ub認(rèn)使用十六進(jìn)制表示。例如命令x/1024b0x0000:0x00000x0000xp/nuf顯示從指定物理地址處開(kāi)始的內(nèi)存中的數(shù)據(jù)。u2Linux0.11本章主要介紹在Linux0.11源代碼中涉及到的C語(yǔ)言、匯編語(yǔ)言、數(shù)據(jù)結(jié)構(gòu)等一些基Linux0.11內(nèi)核源代碼的結(jié)2-1Linux0.11linux011開(kāi)文件夾,可以瀏覽文件夾所包含的文件。Linux0.11塊或其實(shí)現(xiàn)的功能,組織在不同的文件夾中,詳細(xì)的說(shuō)明可以參見(jiàn)表2-2。2-1:Linux0.11內(nèi)核源代碼的樹(shù) ,包括進(jìn)程調(diào)度、系統(tǒng)調(diào)用函數(shù)以及blk_dev 下的字符設(shè)備驅(qū)動(dòng)程序和math 2-2:Linux0.11內(nèi)核源代碼組織方式的詳細(xì)說(shuō)NASM匯MASMMASMNASMNASM需要方括號(hào)來(lái)內(nèi)存地址中的內(nèi)fooequ1bardw2movmovax,movmovax,NASM的操作則不需要。所以,形如“movax,foo”的指令總是代表一個(gè)編譯時(shí)常數(shù),無(wú)論它是“movax,word[bar]”。這就NASM不需MASM“OFFSET”關(guān)鍵字,因MASM代碼“movaxoffsetbarNASM的“movax,word[bar]”是完全等效的。wordtable+bx。同MASM“movax,es:[diNAMS中應(yīng)該是“movax,wordNASM不變量的類(lèi)NASM不會(huì)記住的變量的類(lèi)型。然而,MASM在看到“vardw0”時(shí)會(huì)記住類(lèi)型,也就是var是一個(gè)字大小的變量,然后就可以隱式地使用“movvar,2”給變量賦值。NASM[var],2因此,NASMNASM不支持內(nèi)存NASM同樣不含有任何操作符來(lái)支持不同的16位內(nèi)存模型。在使用NASM16代碼時(shí),必須自己哪些函數(shù)需要FARCALL,哪些需要NEARCALL,并有責(zé)任確定放置正確的必須在調(diào)用外部函數(shù)時(shí)在需要的地方編寫(xiě)CALLFAR指令,并必須哪些外部變量定義是FAR,哪些是NEAR。NASM不支持pushbpmovebp,sp以,以上由MASM自動(dòng)添加的代碼在NASM中必須手動(dòng)添加。NASM可以生成BIN外,還可以將ASM文件匯編成一個(gè)只包含編寫(xiě)的代碼的BIN文件。BIN文件主要用于制作操作系統(tǒng)的引導(dǎo)程序和加載程序,例如由Linux0.11內(nèi)核源代碼文件boot/bootsem和boot/setup.asm所生成的boot.binsetup.bin文件。此行語(yǔ)句告訴NASMBIN文件將要被加載到內(nèi)存偏移地址0x7C00處,這樣NASM匯編器就可以根據(jù)此偏移地址定位程序中變量和的位置。times510-($-$$)db節(jié)都填充0。C和匯編的相法就是使用匯編語(yǔ)言。Linux0.11本的硬件操作包裝成可供C語(yǔ)言調(diào)用的函數(shù)。本節(jié)內(nèi)容主要介紹C和匯編在相用時(shí)應(yīng)該遵守的一些約定,Linux0.11中的代碼也同樣遵守這些約定。下面示例中的匯編代碼使用的是NASM匯編語(yǔ)法,如果讀者有不明白的地方,可以參考2.2節(jié)。CC在硬件上執(zhí)行的機(jī)器語(yǔ)言。下面簡(jiǎn)單介紹各種C器在將C代碼翻譯成匯編時(shí)所遵守的mov[_c],mov[_c],c[sectiondata]_cdd[sectiontext] (CallStackFrame) push movebp, ebp subesp, L2:moveax,[ebp+ ;eax=a。通過(guò) movecx,[ebp+ ;ecx= addeax, mov[ebp–4], push movebp,L1:pushdword pushdword call L3:addesp, xoreax,;eax=}} return c=add(5,}int{ return int c=a+intc=//定義求和函數(shù)。intadd(inta,intb){NASM匯編Cmoveax,[ebp-;;;movesp,;pop表2-3:C代碼對(duì)應(yīng)的NASM匯編代碼表2-3使用一段非常簡(jiǎn)單的C程序和其對(duì)應(yīng)的NASM匯編代碼來(lái)舉例說(shuō)明上面的各項(xiàng)約定。仔細(xì)觀察表2-3代碼可以發(fā)現(xiàn),C代碼中的全局變量cadd到了匯編代碼中都在其前面添加了一個(gè)下劃線,這是符合第一條調(diào)用約定的。在main函數(shù)和add函數(shù)X86CPU從硬件層次就支持堆棧操作,提供了SS、ESP、EBP等寄存器,其中SS寄存器保存PUSH、POP等。由于X86CPU的堆棧是從高地址向低地址生長(zhǎng)的,所以,PUSH指令會(huì)先減少ESP寄存器的值(CPU16位實(shí)模式下減232護(hù)模式下數(shù)據(jù)放入棧頂,POP指令會(huì)先從棧頂取出數(shù)據(jù),再增加ESP寄存器的值。強(qiáng)調(diào)了關(guān)于堆棧的基本知識(shí),我們32main()返回地main()返回地

圖2-4:初始的堆棧

參數(shù)參數(shù)參數(shù)main()返回地圖2-5:參數(shù)入棧后的

自動(dòng)變自動(dòng)變量add()返回地參數(shù)參數(shù)main()返回地圖2-6:進(jìn)入函數(shù)后的自動(dòng)變自動(dòng)變量add()返回地參數(shù)參數(shù)main()返回地圖2-7:LEAVE指令執(zhí)行后的堆棧狀

自動(dòng)變自動(dòng)變量add()返回地參數(shù)參數(shù)main()返回地2-8:RET指令執(zhí)行后的堆棧2-8所示的狀態(tài)。至此,調(diào)用約定二和調(diào)用約定三也講解完畢。CCC原語(yǔ)操CPUCPU恢復(fù)響應(yīng)外部中斷。X86CPU是根據(jù)eflags狀態(tài)字寄存器中的IF位來(lái)決定是否響應(yīng)外部中斷的,并提供了STI指令設(shè)置IF位從而允許響應(yīng)外部中斷,還提供了CLI指令清空IF停止響應(yīng)外部在Linux0.11內(nèi)核中應(yīng)該成對(duì)使用指令STI和CLI來(lái)實(shí)現(xiàn)原語(yǔ)操作。例如,內(nèi)核中的A需要實(shí)現(xiàn)一個(gè)原語(yǔ)操作,就應(yīng)該按照下面的方式編寫(xiě)代碼:voidAvoidA{ }voidBvoidB{BOOLBOOL}C語(yǔ)言中變量的內(nèi)存布體來(lái)同時(shí),例如,CPU可以同時(shí)1、2、4或個(gè)字節(jié)。可以這樣來(lái)理解,CPU在數(shù)據(jù)類(lèi)型描述的內(nèi)存chara='A';shortb=0x1234;longc=0x12345678;void*p=&c;量c的地址放入變量p所在的內(nèi)存。(void地 內(nèi)

bcp圖2-9:最簡(jiǎn)單的數(shù)據(jù)類(lèi)型所描述的內(nèi)存布局unsignedcharByteArray[8]={0x10,0x20,0x30,0x40,0x50,0x60,0x70,typedefstruct_FOO long long}FOO,PFOOPointer=指針Pointer指向的內(nèi)存基址和內(nèi)存布局可以像圖2-10所示的樣子。此時(shí),表達(dá)式可以說(shuō)明第一條準(zhǔn)則是成立的;表達(dá)式&Pointer->Memeber2得到的地址大于&Pointer->Member1

圖2-10:結(jié)構(gòu)體類(lèi)型所描述的內(nèi)存布結(jié)構(gòu)體FOO4,與Pointer0x402000x40204,再結(jié)合域Member2的數(shù)據(jù)類(lèi)型(內(nèi)存布局)內(nèi)存中的數(shù)據(jù)。內(nèi)存的速度,默認(rèn)情況下,C語(yǔ)言編譯器會(huì)保證基本數(shù)據(jù)類(lèi)型的內(nèi)存基址是某個(gè)數(shù)k(通常為2或4)的倍數(shù),這就是所謂的內(nèi)存對(duì)齊,而這個(gè)k則被稱(chēng)為該數(shù)據(jù)類(lèi)型的對(duì)齊模數(shù)。以用來(lái)編譯Linux0.11源代碼的GCC編譯器為例,默認(rèn)情況下,任何基本數(shù)據(jù)類(lèi)型的對(duì)齊模數(shù)就是該數(shù)據(jù)類(lèi)型的大小。比如對(duì)于double類(lèi)型(大小為8字節(jié)),就要求該數(shù)據(jù)類(lèi)型的內(nèi)//#pragmatypedefstruct_FOO1 short long}FOO1,typedefstruct_FOO2 unsignedchar short long}FOO2,//#pragma以像圖2-11所示的樣子。地 內(nèi)

圖2-11:默認(rèn)情況下結(jié)構(gòu)體的內(nèi)存布Cpragmapack(n)”用來(lái)指定數(shù)據(jù)類(lèi)型的對(duì)齊模數(shù)。將地 內(nèi)

圖2-12:按照1字節(jié)對(duì)齊后的結(jié)構(gòu)體的內(nèi)存布局typedefstruct_FOO3 union short long long}FOO3,圖2-13所示的樣子。Member2Member2

圖2-13:聯(lián)合體的內(nèi)存Linux0.11typedefstruct_FOO4 long long long}FOO4,2-14long0x4020004Pointer->Head的值為0x10(0000010000),表達(dá)式Pointer->Middle的值為0x08(0000001000)Pointer->Tail的值為0x403(010000001

3130292827262524232221 765432101000000001100000010000000010000 圖2-14:位域的內(nèi)存布局變量在內(nèi)存中的位形式參數(shù)和局部變量(未加static。(stack字節(jié)順Little-endian與Big-字節(jié)順?lè)绞揭话阆嗤?。?duì)于多字節(jié)數(shù)據(jù),在不同的處理器的存放方式主要有兩種,一種是Little-endian,另一種是Big-endian。Little-a0x0Da+10x0Ca+20x0Ba+3Big-a0x0Aa+10x0Ba+20x0Ca+3Motorola6800,Motorola68000,PowerPC970,System/370,SPARC(V9)等處理器為Bigendian。字節(jié)順序才能完成工作,例如在采用InX86處理器的計(jì)算機(jī)上調(diào)試程序,如果想查看一樣在人工的時(shí)候才能夠識(shí)別出正確的數(shù)據(jù)。使用工具閱Linux0.11源代LinuxLab提供的源代碼編輯器為多種源代碼文件(.c,.cpp,.h,.asm等)提供了符號(hào)圖2-15:使用大綱將源代碼折文本查找工LinuxLab提供了“查找和替換”框,用于在多個(gè)文件中查找文本。如果在閱讀代碼時(shí)遇到了一個(gè)函數(shù)調(diào)用Fun(),想查看此函數(shù)是如何實(shí)現(xiàn)的(即函數(shù)的定義,可以使用鼠標(biāo)選中函數(shù)的名稱(chēng),然后按Ctrl+Shift+F快捷鍵彈出“查找和替換”框,在框Lab 書(shū)簽工Ctrl+F2快捷鍵編譯器工具可以幫助讀者定位代碼中的警告和錯(cuò)者可以適時(shí)的使用快捷鍵調(diào)試器工望分析例程的操作時(shí)按F11實(shí)驗(yàn)一實(shí)驗(yàn)環(huán)境的使一、實(shí)驗(yàn)?zāi)慷?、預(yù)備知請(qǐng)讀者認(rèn)真閱讀本書(shū)第1章的內(nèi)容,同時(shí)可以閱讀博士編寫(xiě)的《Linux內(nèi)核完全注三、實(shí)驗(yàn)內(nèi)啟動(dòng) 在桌面上雙擊“EngintimeLinuxLab”圖標(biāo)。EngintimeLinuxLab”“EngintimeLinuxLab”。練習(xí)LinuxLab編寫(xiě)Windows臺(tái)應(yīng)用程序,熟LinuxLab使用方新建Windows控制臺(tái)應(yīng)用程序項(xiàng)LinuxLabC項(xiàng)目的源代碼主要包含一個(gè)頭文件“console.h”和一個(gè)C語(yǔ)言源文件“console.c”。圖1-1:打開(kāi)Windows控制臺(tái)應(yīng)用程序項(xiàng)目后的“項(xiàng)目管理器生成項(xiàng)含語(yǔ)法錯(cuò)誤,會(huì)在最后提示生成成功,如圖1-2:圖1-2:成功生成Windows控制臺(tái)應(yīng)用程序項(xiàng)目后的“輸出控制臺(tái)應(yīng)用程序項(xiàng)目后,默認(rèn)會(huì)在“C:\test\lab1\debug"下生成一個(gè)名稱(chēng)為“console.o”的對(duì)象文件和名稱(chēng)為“console.exe”的Windows控制臺(tái)應(yīng)用程序,可以使用Windows資源管理器查看這些文件。執(zhí)行項(xiàng)LinuxLabWindows控制臺(tái)應(yīng)用程序。啟動(dòng)執(zhí)行后會(huì)彈出一個(gè)Windows控制臺(tái)窗口,顯示控制臺(tái)應(yīng)用程序輸出的內(nèi)容。按任意鍵即可關(guān)閉此Windows控制臺(tái)窗口。調(diào)試項(xiàng)LinuxLab圖1-3:添加func.c文件后的“項(xiàng)目管理器”窗intFuncintFunc(int{n=n+1;returnn;}intmain(intargc,char* int intFuncintn); intn= printf o printf o return}使用斷點(diǎn)中斷執(zhí)intn=圖1-4:在console.c文件的main函數(shù)中添加斷點(diǎn)后的代行代碼1-5:圖1-5:Windows控制臺(tái)應(yīng)用程序啟動(dòng)調(diào)試后在斷點(diǎn)處中斷單步調(diào)F15按Shift+F5查看變量的n速監(jiān)視”,可以使用“快速監(jiān)視”框查看變量n的值。然后,可以點(diǎn)擊“關(guān)nnF10量n的值會(huì)變?yōu)?,如圖1-6:圖1-6:使用“監(jiān)視”窗口查看變量的值和類(lèi)如果需要使用十進(jìn)制查看變量的值,可以點(diǎn)擊上的“十六進(jìn)制”按鈕,從而在調(diào)用堆按F11“逐語(yǔ)句”功能的快捷鍵)調(diào)試,直到進(jìn)入Func函數(shù),查看“調(diào)用堆?!贝翱诳梢园l(fā)現(xiàn)在堆棧上有兩個(gè)函數(shù)Funcmain。其中當(dāng)前正在調(diào)試的Func棧頂位置,mainmainFuncmainmain在的源代碼文件打開(kāi),并也使用一個(gè)綠色箭頭指向Func函數(shù)返回后的位置。Funcmain口中n的值,可以看到在不同的堆棧幀被激活時(shí),LinuxLab調(diào)試器會(huì)自動(dòng)更新之前練習(xí)了Windows控制臺(tái)應(yīng)用程序項(xiàng)目的各項(xiàng)操作,對(duì)Linux0.11內(nèi)核項(xiàng)目的各種此項(xiàng)目就是一個(gè)Linux0.11操作系統(tǒng)內(nèi)核項(xiàng)目,包含了Linux0.11操作系統(tǒng)內(nèi)核的所使用Windows資源管理器打開(kāi)項(xiàng)目所在的文件夾C:\linux011,查看所有源代碼文件。0.11操作系統(tǒng)需要運(yùn)行的可執(zhí)行文件。LinuxLab在成功生成Linux0.11操作系統(tǒng)內(nèi)核后,會(huì)將這三個(gè)二進(jìn)制文件寫(xiě)入大小為1.44MB的軟盤(pán)鏡像文件floppya.img中,并將該軟盤(pán)鏡像文件虛擬機(jī)的軟盤(pán)驅(qū)動(dòng)器A中,然后讓虛擬機(jī)從軟盤(pán)鏡像A開(kāi)始引導(dǎo),并最終運(yùn)行其中的Linux0.11操作系統(tǒng)(相當(dāng)于將寫(xiě)有三個(gè)二進(jìn)制文件的軟盤(pán)一個(gè)物理機(jī)的軟盤(pán)驅(qū)動(dòng)器A中,然后按下開(kāi)機(jī)按鈕。使用源代碼編輯器打開(kāi)main.c文件。的第一個(gè)內(nèi)核函數(shù)就是這個(gè)start函數(shù)。1-8Linux0.11內(nèi)核項(xiàng)init/main.c文件中添加一此時(shí),激活虛擬機(jī)窗口可以看到Linux0.11也不再繼續(xù)運(yùn)行了。各種調(diào)試功能(包括單步調(diào)試、查看變量的值和各個(gè)調(diào)試工具窗口)Windows制臺(tái)程序完全相同,讀者可以在這里自己練下。F5Linux0.11且Linux0.11的終端已經(jīng)打開(kāi),可以接受用戶輸入令了。命命令功選項(xiàng)含命令命令形選功- 件- - 件--cp[選項(xiàng)]源 2- - rm- -- - 錄符][mode]ugoa符文件屬主同組的用戶有相同的權(quán)s注意練習(xí)使用查找功Linux0.11的源代碼雖然不足兩萬(wàn)行,僅為任何一個(gè)具有的軟件代碼量的十閱讀和理解Linux0.11的源代碼帶來(lái)不小的。即便是一位有豐富經(jīng)驗(yàn)的開(kāi)發(fā)者,在沒(méi)請(qǐng)讀者按照下面的步驟練習(xí)使用查找功能,查找Linux0.11內(nèi)核源代碼中對(duì)fork函數(shù)的、定義以及所有調(diào)用fork函數(shù)的代碼:1-10:“查找范新建Linux0.11應(yīng)用程序項(xiàng)目此項(xiàng)目就是一個(gè)Linux0.11WindowsC:\linux011\Floppya.img文件拷貝覆蓋C:\linuxapp\Floppya.img文件。這樣Linux0.11應(yīng)用程序就可以使用版本的Linux0.11操作系統(tǒng)內(nèi)核了。1-11Linux0.11應(yīng)用程打開(kāi)C:\linuxapp\debug文件夾,查看生成的對(duì)象文件和目標(biāo)文件。其中的linuxapp.exeLinux0.11aout不能在Windows中運(yùn)行,只能在Linux0.11中運(yùn)行。LinuxLab每次生成應(yīng)用程序項(xiàng)目時(shí),都會(huì)將應(yīng)用程序的可執(zhí)行文件自動(dòng)寫(xiě)入軟盤(pán)鏡像文件floppyb.img中。查看軟盤(pán)鏡像文件中floppyb.imgFloppyImageEditor開(kāi)該項(xiàng)目中的floppyb.img文件,查看軟盤(pán)鏡像中的文件。linuxapp.exe是從查看一下該文件的修改日期,如圖1-12:執(zhí)行Linux0.11應(yīng)用程序項(xiàng)目Linux0.11“mcopyb:linuxapp.exelinuxapp”命令將軟盤(pán)鏡像文件floppyb.img中的可執(zhí)行文件linuxapp.exe拷貝到硬盤(pán)的當(dāng)前l(fā)inuxapp1-圖1-13:將可執(zhí)行文件linuxapp.exe拷貝至硬而沒(méi)有x權(quán)限,如圖1-14:圖1-14:使用ls命令查看文件linuxapp的權(quán)使用“od+xlinuxapp”ls認(rèn)文件權(quán)限修改后,就可以運(yùn)行該文件了,如圖1-15:1-15linuxapp增加可執(zhí)行權(quán)限并LinuxLab有必要深入學(xué)下這種操作方式,為今后在Linux操作系統(tǒng)中編程打下基礎(chǔ)。vi編輯器的使用方編寫(xiě)程序的得力工具。vi有3種操作模式:命令模式、模式和末行模式。入模式下,才可以進(jìn)行文字?jǐn)?shù)據(jù)輸入及添加代碼,按Esc鍵可回到命令模式。命令類(lèi)命令形說(shuō)vi:wq要確認(rèn)是否放棄修改的內(nèi)容:iaoscr命說(shuō)xXYpvi和GCCLinux0.11Linux系統(tǒng)上工作時(shí),恐怕就只有使用這種方法來(lái)寫(xiě)程序了。按F5啟動(dòng)Linux0.11,使用ls命令查看當(dāng)前,并用rm命令將o.c文一個(gè)可以輸出“oworld”的程序,保存源代碼文件并退出vi。gcco.c-oo編寫(xiě)Makefile文件管理Makefile可以用來(lái)管理一個(gè)項(xiàng)目中多個(gè)源代碼文件的編譯和過(guò)程,也可以用來(lái)管讀者今后在Linux下開(kāi)發(fā)程序還是非常必要的。gcco.c-o第一行描述依賴(lài)關(guān)系,目標(biāo)文件o依賴(lài)于源代碼文件o.c,第二行描述操作命令,要用gcc將源代碼文件o.c編譯為可執(zhí)行文件o。當(dāng)修改o.c文件后,需要編譯時(shí),只需在相同make令即可(makefile依賴(lài)關(guān)系命令行之后的操作命令行必須用制表符(Tab鍵)縮進(jìn),否則會(huì)收到“missing o.o-o gcc- o.c- o.o-o gcc- o.c- rm-f rm-f 用于清理操作??稍谌魏螘r(shí)刻激活clean目標(biāo),刪掉makefile生成的文件??梢詷?gòu)建任何一個(gè)目標(biāo),例如指定clean目標(biāo)令如下:make完成下面的練習(xí)在LinuxLab關(guān)閉后默認(rèn)會(huì)自動(dòng)使用Windows資源管理器打開(kāi)數(shù)據(jù)文件所在的文件夾,并且選中剛剛保存的數(shù)據(jù)文件(OUD。將數(shù)據(jù)文件備份(例如拷貝到自己的U盤(pán)中或者發(fā)送到服務(wù)器上實(shí)驗(yàn)注意事LinuxLabLinux0.11Linux0.11應(yīng)用LinuxLabLinux0.11像文floppyb.img中Linux0.11后需mcopy命令將軟B中的可執(zhí)行文件到硬盤(pán)中才能運(yùn)行。在Linux0.11中對(duì)硬盤(pán)上的文件進(jìn)行修改后,需要執(zhí)行sync命令才能將修改保存在硬盤(pán)上,否則,關(guān)閉Linux0.11后,修改會(huì)丟失。四、思考與練練習(xí)使用Linux0.11提供的各種文件操作命令。使用這些命令瀏覽硬盤(pán)中的結(jié)構(gòu),了解Linux中各個(gè)的主要功能。五、相關(guān)閱http:/ 可以參與中的討論或者進(jìn)行答疑。實(shí)驗(yàn)二操作系統(tǒng)的啟一、實(shí)驗(yàn)?zāi)?二、預(yù)備知現(xiàn)代操作系統(tǒng)啟動(dòng)比較復(fù)雜,涉及較多的名詞,如:GDT、GDTR、IDT、A20地址源代碼文件,分別是boot/bootse NASMbootsect.bin。此二進(jìn)制文0x7c00處,并從該地址開(kāi)始執(zhí)行引導(dǎo)程序。引導(dǎo)程序會(huì)首先把自己移動(dòng)到物理內(nèi)存區(qū)中的內(nèi)核模塊(linux011.bin)0x10000處。最后,在確定根文件系統(tǒng)所在的磁盤(pán)后,跳轉(zhuǎn)運(yùn)行setup模塊。NASMsetup.bin。此二進(jìn)制文件floppya.img14個(gè)扇區(qū),作為操作系統(tǒng)加載程序。它首先利用BIOS中斷機(jī)器參數(shù),并放置在0x90000開(kāi)始的物理地址處以備描述符表寄存器(idtr)等,并在開(kāi)啟A20地址線后對(duì)8259A中斷控制重新編程。最后進(jìn)入保護(hù)模式,跳轉(zhuǎn)到地址0:0(即內(nèi)核模塊中head.s的第一條指令處)運(yùn)行。在時(shí)寫(xiě)入內(nèi)核模塊linux011.bin的代碼段的開(kāi)始位置。head.s在執(zhí)行時(shí)會(huì)首先加載idt,并使各個(gè)表項(xiàng)(256項(xiàng))指向一個(gè)/init/main.cstart函數(shù)的地址彈出,并跳轉(zhuǎn)到start函數(shù)中去執(zhí)行。三、實(shí)驗(yàn)內(nèi)bootbootsemsetup.asm兩個(gè)匯F7WindowsDebug文件夾。找到0.11文件中的內(nèi)核函數(shù)(start函數(shù))的地址首先按照下面的步驟記錄下start函數(shù)的地址,留待后續(xù)使用:添加的斷點(diǎn)處中斷。在start函數(shù)名稱(chēng)上點(diǎn)擊右鍵,選擇菜單中的“添加監(jiān)視start2-1:圖2-1:start函數(shù)的地為了調(diào)試BIOS程序和軟盤(pán)引導(dǎo)扇區(qū)程序,需要按照下面的步驟將目標(biāo)機(jī)修改Bochs 值修改為“BochsDebug 點(diǎn)擊“確定”按鈕關(guān)閉“屬性頁(yè)”框。接下來(lái)就可以使用Bochs模擬器調(diào)F5Bochs窗口。標(biāo)題為“BochsforwindowsDisplay”的窗口相當(dāng)于計(jì)算機(jī)的顯示器,顯示操作系統(tǒng)的輸出。標(biāo)題為“BochsforwindowsConsole”的窗口是Bochs的控制臺(tái),用來(lái)輸入調(diào)試命令和輸出調(diào)試信息。啟動(dòng)調(diào)試后,BochsCPU要執(zhí)行的第一條指令(BIOS的第一條指令)處中斷。此時(shí),Display窗口沒(méi)有顯示任何內(nèi)容,ConsoleBIOS的第一條指令,并等待用戶輸入調(diào)試命令,如圖2-2:圖2-2:Console窗口顯示在BIOS第一條指令處中圖2-3:使用sreg查看寄存器中的令r后按回車(chē),顯示當(dāng)前CPU中各個(gè)通用寄存器的值,如圖2-4。其中令的地址,可以驗(yàn)證CPU將要執(zhí)行的指令地址為CS:IP。圖2-4:使用r寄存器查看寄存器中令 0,說(shuō)明軟盤(pán)引導(dǎo)扇區(qū)還沒(méi)有被加載到此處。IP寄存器的值是一致的。由于內(nèi)存還沒(méi)有被使用,所以其中的值都0。(以bootsem為例表文件,此文件中的信息可以幫助開(kāi)發(fā)者調(diào)試bootsem文件中的匯編代碼。 movBIOS在執(zhí)行完自檢和初始化工作后,會(huì)將軟盤(pán)引導(dǎo)扇區(qū)(512字節(jié))加載到物理地址vb0x0000:0x7c000x0000:0x7c00(要執(zhí)行的指令,即軟盤(pán)引導(dǎo)扇區(qū)程序的第一條指令,如圖2-5:圖2-5:軟盤(pán)引導(dǎo)扇區(qū)程序的第一條指(b8c007程序此時(shí)已經(jīng)執(zhí)行完畢,輸入調(diào)試命令 0x0000驗(yàn)證此512b0x7c00顯示軟盤(pán)引導(dǎo)扇區(qū)程序的所有字節(jié)碼。觀察此塊內(nèi)存最開(kāi)始的三個(gè)0x550xaa操作系統(tǒng),這兩個(gè)字節(jié)對(duì)應(yīng)bootsem中最后一行語(yǔ)句(注意,字節(jié)endiandw0xAA55輸入調(diào)試命令xp 動(dòng)到0x9000:0x0000處。輸入調(diào)試命令 xp/512b0x9000:0x0000,并與之前的內(nèi)存比較,可以知道引導(dǎo)程序已經(jīng)將自己移到了0x90000處,并在0x9000:0x0018處中斷執(zhí)行了。輸入調(diào)試命令xp 根據(jù)bootsect.lst文件下面一行的內(nèi)容(執(zhí)行此行指令時(shí)說(shuō)明setup.bin加載完畢) 000000310F830B00 jncok_load_setup 輸入調(diào)試命令vb 0x9000:0x0031,在邏輯地址0x9000:0x0031處設(shè)置一個(gè)斷點(diǎn)。xp/512b0x9000:0x0200,此塊內(nèi)存已經(jīng)發(fā)生改變,可以知道此時(shí)setup.bin模塊已經(jīng)被載入內(nèi)存。xp/512b0x1000:0x0000,可以看到除前面有少量數(shù)據(jù)(BIOS自檢119 call輸入調(diào)試命令 0x9000:0x006e。在0x9000:0x006e處設(shè)置一個(gè)斷點(diǎn)輸入調(diào)試命令xp 明Linux0.11將要開(kāi)始執(zhí)行setup模塊。圖2-6:在斷點(diǎn)0x9020:0x0000處中 圖2-7:取得機(jī)器參數(shù)前物理內(nèi)存0x90000處的值 輸入調(diào)試命令 入0x90000-0x901FF的物理內(nèi)存中。 圖2-8:取得機(jī)器參數(shù)后0x90000 jmp輸入調(diào)試命令 會(huì)跳轉(zhuǎn)到head.s的第一條指令處執(zhí)行。s movl 0x0000處設(shè)置一個(gè)斷點(diǎn)(注意執(zhí)行到這里時(shí),CPU已經(jīng)處于保護(hù)模式了pb0x0000。c2-9 pushl 圖2-10:在將start函數(shù)地址壓棧的指令處中是start函數(shù)的地址。最終就是通過(guò)這個(gè)地址跳轉(zhuǎn)到操作系統(tǒng)內(nèi)核的點(diǎn)函數(shù)開(kāi) 輸入調(diào)試命令:pb0x54a5令c繼續(xù)執(zhí)行,在ret指令處中斷。然后接著輸入調(diào)試命令s,單步執(zhí)行此行的ret指令,可以得到圖2-11:圖2-11:準(zhǔn)備執(zhí)行start函數(shù)的第一start函數(shù)中的指令了。將內(nèi)核入調(diào)試到這里,init/main.c的start函數(shù)之前部分就全部結(jié)束了,系統(tǒng)將跳入2-12-11init/main.cstart函數(shù)中去執(zhí)行。說(shuō)明此時(shí)已經(jīng)進(jìn)入了內(nèi)核,但從剛進(jìn)入內(nèi)核到系統(tǒng)最終穩(wěn)定下來(lái),全注釋》第7章的內(nèi)容。在LinuxLab的“新建項(xiàng)目”框中可以選擇mkfloppy項(xiàng)目模板或pe2bin項(xiàng)目模板來(lái)Linux0.11linux011.binLinuxLab時(shí),已經(jīng)將這兩個(gè)mkfloppy.exepe2bin.exeLinuxlabbin文件夾中,但是的功能,從而加深對(duì)Linux0.11系統(tǒng)的理解。mkfloppy項(xiàng)目模板創(chuàng)建的應(yīng)用程序mkfloppy.exe用于將bootsect.bin、setup.bin和linux011.binfloppya.img512字節(jié),bootsect.bin0扇區(qū),setup.bin1-4扇區(qū),linux011.bin文件占用軟盤(pán)的第5-388扇區(qū)。pe2bin項(xiàng)目模板創(chuàng)建的應(yīng)用程序pe2bin.exe用于將生成的PElinux011.exelinux011.bin文件。PElinux011.exe中存放著Linux0.11操作系統(tǒng)內(nèi)核的數(shù)據(jù)和指令以及調(diào)試信息,由于其格式比較復(fù)雜,還無(wú)法被bootsect程序識(shí)別,所以無(wú)法直接從軟盤(pán)加載到內(nèi)存中,所以,pe2bin.exe應(yīng)用程序的目的linux011.exelinux011.bin文件,從而允許bootsect程序直接將其加載到內(nèi)存中。linux0.11linux011節(jié)點(diǎn),單擊彈出菜單中echo正在生成內(nèi)核映像文件strip"$(TargetDir)$(TargetName).tmp"del"$(TargetDir)$(TargetName).tmp"echo正在制作引導(dǎo)軟盤(pán)映像文件mkfloppy.exe"$(OutDir)\bootsect.bin""$(OutDir)\setup.bin""$(TargetDir)$(TargetName).bin"這些Windows控制臺(tái)命令會(huì)在生成Linux0.11內(nèi)核項(xiàng)目的最后階段自動(dòng)執(zhí)行。其中echo命令用于打印信息copy命令linux011.exe文件拷貝成一個(gè)linux011.tmp臨時(shí)文件;striplinux011.tmp文件中的調(diào)試信息刪除;pe2bin.exelinux011.tmp文件轉(zhuǎn)換linux011.bin文件;del命令將linux011.tmp臨時(shí)文件刪除;mkfloppy.exe將bootsect.bin、setup.binlinux011.binfloppya.img軟盤(pán)鏡像文件中。四、思考與練習(xí)mkfloppy模板新建一個(gè)應(yīng)用程序項(xiàng)目,仔細(xì)閱讀其中的源代碼。此應(yīng)用程序默認(rèn)linux011.bin5號(hào)扇區(qū)開(kāi)始的位置,嘗試修改該應(yīng)用程序linux011.bin8F7生成項(xiàng)目,將生成的mkfloppy.exe拷貝替換Linuxlab安裝bin文件夾下的mkfloppy.exe,修改boot\bootsem中的匯編代碼,使之能夠從軟盤(pán)A的8號(hào)扇區(qū)加載linux011.bin文件,確保Linux0.11能夠正常啟動(dòng)。echo正在生成內(nèi)核映像文件...strip"$(TargetPath)"echo正在制作引導(dǎo)軟盤(pán)映像文件mkfloppy.exe"$(OutDir)\bootsect.bin"$(OutDir)\setup.bin"$(TargetPath)floppya.img"PElinux011.exe直接寫(xiě)入軟盤(pán)鏡像文件中,這就需要讀者Linux0.11能夠正常啟動(dòng)??梢詤⒖紁e2bin項(xiàng)目模板中的源代碼完成此練習(xí)。五、相關(guān)閱打開(kāi)LinuxLab的“幫助”菜單,選擇“其它幫助文檔”中的“NASM手冊(cè)”,閱讀該手可以參看第10章的10.1和10.2節(jié)。閱讀第14章可以了解關(guān)于實(shí)模式的信息。實(shí)驗(yàn)三S序設(shè)建議學(xué)時(shí):2學(xué)時(shí)一、實(shí)驗(yàn)?zāi)?二、預(yù)備知S為用戶和Linux操作系統(tǒng)之間令交互提供最基本的接口,在使用者和操作系統(tǒng)早期S主要用作命令解釋器,經(jīng)過(guò)不斷擴(kuò)充和發(fā)展,現(xiàn)在已是命令語(yǔ)言、命令解釋器、S是一種解釋型程序設(shè)計(jì)語(yǔ)言,它支持大多數(shù)高級(jí)程序設(shè)計(jì)語(yǔ)言中能見(jiàn)到的程序結(jié) 包含一系列命令,運(yùn)行就是執(zhí)行中的每個(gè)命令,可用一個(gè)在一次運(yùn)行中三、實(shí)驗(yàn)內(nèi)3.1準(zhǔn)備實(shí)1.啟動(dòng)EngintimeLinuxLab 3.2回顯語(yǔ)echo語(yǔ)句用來(lái)顯示變量值或字符串。 使用vi新建 編輯S oworld o 3.3特殊字字作星號(hào)問(wèn)號(hào)編寫(xiě)如下S 3.4變 用戶自定義變用戶自定義變量也稱(chēng)局部變量,是用戶在S 編寫(xiě)如下S o oecho2.位置變位置變量類(lèi)似于C語(yǔ)言程序令行參數(shù),可以寫(xiě)在S程序文件名之后,共有9個(gè),用$n表示(n為一個(gè)十進(jìn)制數(shù)。在參數(shù)傳遞時(shí)必須使命令行中提供的參數(shù)與程位置變量名為$1,第2個(gè)位置變量名為$2,以此類(lèi)推。編寫(xiě)如下S echo$0echo$1echo3.S變S變量的名字和含義是固定的。$?為得到上次命令的十進(jìn)制返回碼,$$為得到編寫(xiě)如下S 文件S5.sh,并觀察運(yùn)行結(jié)果:echo$$echo4.命令替編寫(xiě)如下S 文件S6.sh,并觀察運(yùn)行結(jié)果: $ll3.5運(yùn)算語(yǔ)編寫(xiě)如下S 文件S7.sh,并觀察運(yùn)行結(jié)果:echo$k3.6函 {} return關(guān)鍵字返回,返回值只能為整數(shù)或整數(shù)型數(shù)值 參數(shù)傳 函數(shù)的參數(shù)傳遞與S 的位置變量類(lèi)似,也是通過(guò)$n表示,n是一個(gè)十調(diào)在 編寫(xiě)如下S functionMul(){ let return} 3.7控制語(yǔ)的條件執(zhí)行不同的流,這就要求S提供各種流程控制語(yǔ)句。 測(cè)試語(yǔ)測(cè)試語(yǔ)句計(jì)算表達(dá)式的值,并返回真(0)或假(1。其在S 中常??s寫(xiě)為[expression]測(cè)試條返回值說(shuō) -r -w -x -b -c -d -f -p -s -t 測(cè)試條返回值說(shuō) -z -n 測(cè)試條返回值說(shuō) int1-gt int1-eq int1-ne int1-lt int1-le int1-ge if條件表達(dá)式thenelse命令else后的語(yǔ)句。條件表達(dá)式也可以使用兩個(gè)預(yù)定義的值truefalse。編寫(xiě)如下S文件S9.sh,用于判斷一個(gè)普通文件是否存在。然后在控制 9.sh, thenecho“Fileelseecho“Cannotfindfile.” case字符串in模式字符1)命令1;;…模式n)n;;case允許多重條件選擇,執(zhí)行過(guò)程是:用字符串去匹配各模式字符串,發(fā)現(xiàn)某個(gè)編寫(xiě)如下S 文件S10.sh,用于判斷傳遞給S “0 “1argument” “2 循環(huán)語(yǔ)while測(cè)試命令do命令表回false為止。編寫(xiě)如下S echo$iforfor變量indo束for循環(huán)。編寫(xiě)如下S 012345untiluntil測(cè)試命令do時(shí)結(jié)束until語(yǔ)句。請(qǐng)讀者編寫(xiě)一個(gè)S 編寫(xiě)如下S - `echo let echo aaaaaaaaaa 讀入語(yǔ)編寫(xiě)如下 echo“pleaseinput readaecho6.退出語(yǔ)break[n]n1。continue[nn層,7.退出程序語(yǔ) 編寫(xiě)如下S 123 echo echo 等待語(yǔ)四、思考與練習(xí)編寫(xiě)一個(gè)S 2+4=6編寫(xiě)一個(gè)S文件,要求執(zhí)行時(shí),如果第一個(gè)位置參數(shù)是合法的,那么就把該及其所有子下的文件名稱(chēng)顯示出來(lái),如果第一個(gè)位置參數(shù)不是合法的,則顯示名不對(duì)。(提示:函數(shù)也可以使用遞歸)實(shí)驗(yàn)調(diào)一、實(shí)驗(yàn)?zāi)慷㈩A(yù)備知行,從而可以操作的各種資源,實(shí)現(xiàn)應(yīng)用程序與內(nèi)核的交互。這些系統(tǒng)調(diào)用號(hào)定義在文件include/unistd.h中,系統(tǒng)調(diào)用號(hào)實(shí)際上對(duì)應(yīng)于include/linux/sys.hsys_call_table[]中項(xiàng)的索在Linux中規(guī)定int0x80指令是系統(tǒng)調(diào)用的總,系統(tǒng)調(diào)用號(hào)存放在EAX寄存器中,應(yīng)用程序通過(guò)系統(tǒng)調(diào)用傳遞給內(nèi)核函數(shù)的參數(shù)依次存放EBX、ECXEDX個(gè)寄存器中(即系統(tǒng)調(diào)用最多只能有3個(gè)參數(shù)。接或間接(通過(guò)庫(kù)函數(shù))int0x80函數(shù)(kernel/system_call.s件中第101);最后,由_system_call調(diào)用對(duì)應(yīng)的內(nèi)核函數(shù),該內(nèi)核函數(shù)將被執(zhí)行并通過(guò)EAX寄存器返回結(jié)果。三、實(shí)驗(yàn)內(nèi)準(zhǔn)備試EngintimeLinuxLab添加新的系統(tǒng)調(diào)為L(zhǎng)inux0.11添加一個(gè)新的系統(tǒng)調(diào)用max函數(shù),該函數(shù)實(shí)現(xiàn)比較兩個(gè)參數(shù)的大小文件,在第162行添加新的系統(tǒng)調(diào)用號(hào),如圖4-1:圖4-1添加新的系統(tǒng)調(diào)用圖4-2修改Linux內(nèi)核的系統(tǒng)調(diào)用總在include/linux/sys.h文件中的第88行使用C語(yǔ)言內(nèi)核函數(shù)的原型,如圖義函數(shù)的返回值為int類(lèi)型,參數(shù)為空,能夠讓sys_max標(biāo)識(shí)符代表一個(gè)函數(shù)名稱(chēng)圖4-3使用C語(yǔ)言內(nèi)核函數(shù)的原sys_call_table[]中添加新系統(tǒng)調(diào)用函引一一對(duì)應(yīng)4-4所示:圖4-4在系統(tǒng)調(diào)用函數(shù)指針表中增加內(nèi)核函數(shù)的指圖4-5系統(tǒng)調(diào)用對(duì)應(yīng)的的源代碼(如圖4-6所示。其中,需要定義 NR_max宏,并使用_syscall2宏(在文件就會(huì)在源代碼文件中使用C語(yǔ)言添加max函數(shù)的完整實(shí)現(xiàn)。4-6main.c文依次執(zhí)行下面令gcc-Emain.c-omain.txtmcopymain.txt具打開(kāi)。將其中的main.txt文件到windows的某個(gè)文件夾中。將文件夾中的main.txt文件拖動(dòng)到Linuxlab中釋放,在文件的最后部分就可以看到_syscall2宏展開(kāi)后得到的max函數(shù)了。可以重點(diǎn)學(xué)下通過(guò)在C代碼中嵌AT&Tint0x80根據(jù)_syscall0、_syscall1、_syscall2、_syscall3這四個(gè)宏(在文件include/unistd.h167)可知系統(tǒng)調(diào)用執(zhí)行的過(guò)程如下:應(yīng)用程序通過(guò)用EAX寄存器返回結(jié)果。斷點(diǎn)條件框,在編輯框中設(shè)置斷點(diǎn)條件$eax==87后按“確定”,如下圖:maxmax圖4-9使用“監(jiān)視”窗口查看EBX和ECX寄存器的F10119行。其中119EAX寄存4-10進(jìn)入系統(tǒng)調(diào)用對(duì)應(yīng)的內(nèi)恢復(fù)為200(返回值,100(參數(shù)一,200(參數(shù)二4-12出棧操作EAX,EBX和ECX寄存器的四、思考與練intIam(constchar*name);將字符串name的內(nèi)容保存到內(nèi)核中,返回值是intWhoami(char*name,intsize);將Iam保存到內(nèi)核中的字符串拷貝到數(shù)據(jù)緩沖區(qū)name中,sizename的長(zhǎng)size小于所需空間,則返回-1,并置errnoEINVAL。編寫(xiě)兩個(gè)應(yīng)用程序。其中,Iam序調(diào)用Iam函數(shù),并使用命令行中的第一個(gè)參數(shù)作為Iam函數(shù)的參數(shù);Whoami應(yīng)用程序調(diào)用Whoami函數(shù),并打印輸出獲取的字4-13測(cè)試應(yīng)用程序的執(zhí)行結(jié)用“查找和替換”框找到errno是在哪里定義的。該數(shù)組和兩個(gè)系統(tǒng)調(diào)用的內(nèi)核函數(shù)定義在kernel/sys.c文件中。不是用戶空間的數(shù)據(jù)。所以,在Iam函數(shù)中需要在一個(gè)循環(huán)中重復(fù)調(diào)用include/asm/segment.h文件。實(shí)驗(yàn)的創(chuàng)一、實(shí)驗(yàn)?zāi)慷?、預(yù)備知在Linux操作系統(tǒng)上可以同時(shí)運(yùn)行多個(gè)進(jìn)程。分時(shí)技術(shù)的基本原理是把CPU的運(yùn)行時(shí)間分時(shí),操作系統(tǒng)就利用調(diào)度程序切換到另一個(gè)進(jìn)程去運(yùn)行。因此,本質(zhì)上對(duì)于具有單個(gè)對(duì)于Linux0.11內(nèi)核來(lái)講,系統(tǒng)最多可有64個(gè)進(jìn)程同時(shí)存在。除了第一個(gè)進(jìn)程用“手工”建立以外,其余的都是現(xiàn)有進(jìn)程使用系統(tǒng)調(diào)用fork函數(shù)創(chuàng)建的新進(jìn)程。被創(chuàng)建的進(jìn)程稱(chēng)為程,創(chuàng)建者則稱(chēng)為父進(jìn)程。Linux內(nèi)核使用進(jìn)程標(biāo)識(shí)號(hào)來(lái)標(biāo)識(shí)每個(gè)進(jìn)三、實(shí)驗(yàn)內(nèi)準(zhǔn)備試EngintimeLinuxLab調(diào)用fork函數(shù)創(chuàng)建intmain(intargc,char*argv[]{ intprintf(oworld\n",getpid()pid=fork();if(pid=fork();if(pid!=0){{ printf("%dparentprocess\n",getpid() printf("%dchildprocess\n",getpid() printf("%dexit\n",getpid() return}mcopyb:linuxapp.exe od+x od+xls–lfork的神奇之處,調(diào)用一次,返回兩次。兩個(gè)函數(shù)的原型在include/sys/wait.h文件中定義如下:pid_twait(pid_t*wait_locpid_twaitpid(pid_tpid,pid_t*wait_loc,intoptionswait(NULL調(diào)試fork函數(shù)的執(zhí)行過(guò)Kernel項(xiàng)目。Linux011harddisk.img$eax==2&¤t->executable->i_size=appfork數(shù)時(shí),才會(huì)命中此條件斷“$eax==2”中2fork函數(shù)的系統(tǒng)調(diào)用號(hào)current是一個(gè)全局變量(kernel/sched.c文件76行定義),此時(shí),由于在應(yīng)用程序中調(diào)用了fork函數(shù),所以就進(jìn)入了int0x80的中斷處理程文件kernel/fork.c的第30行定義)記錄了的進(jìn)程號(hào)。current->pid的值是counter=11查看“監(jiān)視”窗口,可看到last_pid的值已經(jīng)發(fā)生了變化,該值即為程的進(jìn)F10279后按F11copy_process278行將此時(shí)在“快速監(jiān)視”窗口查看表達(dá)式*p的值,可以看到新創(chuàng)建的程控制塊中各個(gè)成員的值都為0。 和相同,所以,程和父進(jìn)程會(huì)從相同的位置(fork函數(shù)返回的位置)繼續(xù)運(yùn)行置程的父進(jìn)程號(hào);第122行將p->tss.eax的值設(shè)置為0,就是fork()在在第165行添加一個(gè)斷點(diǎn),按F5繼續(xù)調(diào)試,命中后刪除該斷點(diǎn)。剛剛執(zhí)行的這窗口分別查看*current和*p的值,比較二者的異同。F10copy_process函數(shù)返回kernel/system_call.s的第280行。copy_process函數(shù)的返回值是程的進(jìn)程號(hào),會(huì)被放入EAX寄存器中,也就是父進(jìn)程從fork函數(shù)返回時(shí)得到的返回值。繼續(xù)按F10單步調(diào)試,直到第133行。可以看到在從fork系統(tǒng)調(diào)用返回之前,并reschedule函數(shù),所以父進(jìn)程會(huì)繼續(xù)運(yùn)行。但是,如果fork返回后,在父進(jìn)程調(diào)用了wait或waitpid系統(tǒng)調(diào)用函數(shù)等待程結(jié)束的情況下,就會(huì)調(diào)用execve使用fork系統(tǒng)調(diào)用函數(shù)可以為父進(jìn)程創(chuàng)建一個(gè)程,但是程和父進(jìn)程執(zhí)行的按照下面的步驟編寫(xiě)一個(gè)Linux0.11execveintmain(intargc,char*argv[] oworld\n",getpid() return}mcopyb:linuxapp.exe od+x od+xintmain(intargc,char*argv[] oexecve\n",getpid() execve("newapp",NULL,NULL oexit\n",getpid() return}intexecve(char*file,char**argv,char**envp程序參數(shù)file是需要被加載的程序文件名,參數(shù)argv是傳遞給新程序令行參數(shù)指針數(shù)組,參數(shù)envp是傳遞給新程序的環(huán)境變量指針數(shù)組。mcopyb:linuxapp.exe od+x od+xls-lexecve系統(tǒng)調(diào)用會(huì)清理掉當(dāng)前進(jìn)程的頁(yè) 和頁(yè)表項(xiàng),并釋放對(duì)應(yīng)的物理頁(yè),然后該進(jìn)開(kāi)始執(zhí)行新程序的代碼。Linux0.11為加載執(zhí)行新程序提供了exec函數(shù)族調(diào)試execve函數(shù)的執(zhí)行過(guò)Kernel項(xiàng)目。和newapp文件了。$eax==11&¤t->executable->i_size程序可執(zhí)行文件app的大小。此時(shí),由于在應(yīng)用程序中調(diào)用了execve函數(shù),所以就進(jìn)入了int0x80的中斷處理程序并命中了斷點(diǎn)。接下來(lái)會(huì)調(diào)用execveF10262行F11do_execve載執(zhí)在第314行添加一個(gè)斷點(diǎn),按F5繼續(xù)調(diào)試,命中后刪除該斷點(diǎn)。第303到第304行初始化參數(shù)和環(huán)境變量空間的頁(yè)面指針數(shù)組。306執(zhí)行i節(jié)點(diǎn)號(hào)。第309到第310行計(jì)算參數(shù)個(gè)數(shù)和環(huán)境變量個(gè)數(shù)。472添加一個(gè)斷點(diǎn),按F5調(diào)試,命中后刪除該斷點(diǎn)。該過(guò)程主要完成對(duì)IDID在第515行添加一個(gè)斷點(diǎn),按F5繼續(xù)調(diào)試,命中后刪除該斷點(diǎn)。第508到第509行初始bss段數(shù)據(jù)。513514forkexecve(task_structLinux開(kāi)發(fā)應(yīng)用程序時(shí),往往會(huì)同forkexecve。一個(gè)程序在使用fork函數(shù)創(chuàng)建了一個(gè)程時(shí),通常會(huì)在該調(diào)用execve函數(shù)加載執(zhí)行另一個(gè)if(fork(){ /*parentprocess}{ /*childprocess }四、思考與練內(nèi)容編寫(xiě)一Linux應(yīng)用main函數(shù)中使fork函數(shù)創(chuàng)建一個(gè)使用execve函數(shù)加載執(zhí)行另外一個(gè)程序的可執(zhí)行文件(例如newapp,并且讓父進(jìn)程退出后再結(jié)束運(yùn)行。實(shí)驗(yàn)六進(jìn)程的狀態(tài)和進(jìn)程調(diào)建議學(xué)時(shí):2學(xué)時(shí)一、實(shí)驗(yàn)?zāi)慷?、預(yù)備知一個(gè)進(jìn)其整個(gè)生命周期內(nèi),不同階段可能具有不同的進(jìn)程狀態(tài)(在include/linux/sched.h文件的第20行定義。一個(gè)進(jìn)程的狀態(tài)由其進(jìn)程控制塊中的state字段用戶運(yùn)0內(nèi)核運(yùn)0內(nèi)核運(yùn)系統(tǒng)調(diào)用或中返中斷,中斷返睡終 睡暫調(diào)狀 喚2喚0繼4interruptib不可中

就緒(TASK_RUNNING這些狀態(tài)在內(nèi)核中表示方法相同,都被稱(chēng)為處于TASK_RUNNING狀態(tài)。例如,(TASK_INTERRUPTIBLE(TASK_UNINTERRUPTIBLEwake_up(TASK_STOPPED僵死狀態(tài)(TASK_ZOMBLE。當(dāng)程已停止運(yùn)行,但其父進(jìn)程還沒(méi)有調(diào)用wait,去執(zhí)行其他進(jìn)程,此進(jìn)程則進(jìn)入睡眠狀態(tài)(TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE內(nèi)核中的調(diào)度程序schedule(在文件kernel/sched.c中的第135行定義)用于選Linux三、實(shí)驗(yàn)內(nèi)準(zhǔn)備試EngintimeLinuxLab編寫(xiě)一個(gè)多進(jìn)程程intmain(intargc,char*argv[]{ if(0==fork() printf("childprocesspid=%dppid=%d getpid(),getppid(),LINE elseif(0==fork() printf("childprocesspid=%dppid=%d getpid(),getppid(),LINE elseif(0==fork() printf("childprocesspid=%dppid=%d getpid(),getppid(),LINE wait(NULL printf("parentprocesspid=%dppid=%d\n",getpid(), return}mcopyb:linuxapp.exe od+x od+x進(jìn)程的運(yùn)行軌在系統(tǒng)初始化時(shí)打開(kāi)日志文件process.log文件。內(nèi)核的函數(shù)是init/main.c文件中的start函數(shù),其中從第181行開(kāi)始的一段if(!fork()) /*wecountonthisgoing }程1,程1接著調(diào)用了本文件中的init()函數(shù)。setup((void*)&drive_info(void)open("dev/tty0",O_RDWR,0);(void)dup(0);(void)dup(0輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤。可以在這里把process.log文件關(guān)聯(lián)到文件描述符3。為了能盡早process.log文件,可以讓上述建立文件描述符的工作在進(jìn)程0中完成。所以,將這一碼從init函數(shù)中移動(dòng)到start函數(shù)中,而且要放在調(diào)用修改后的start函數(shù)如下: /************yourcodebegin setup((void*)&drive_info (void)open("dev/tty0",O_RDWR,0 (void)dup(0 (void)dup(0 (void)open("/var/process.log",O_CREAT|O_TRUNC|O_WRONLY,0666 /************yourcodeend if(!fork()) /*wecountonthisgoing 編寫(xiě)fprintk函數(shù)用于向process.log文件寫(xiě)入數(shù)內(nèi)核狀態(tài)下調(diào)用printf函數(shù),而只能調(diào)用printk函數(shù)。編寫(xiě)可以在內(nèi)核狀態(tài)下被調(diào)用的fprintk函數(shù)的難度較大,所以這里直接給出源代碼,主要是參考了printk函數(shù)和sys_write函數(shù)而寫(xiě)成的:staticstaticcharintfprintk(intfd,constchar*fmt,...{intintva_liststructstructfile*structstructm_inode*va_startva_start(args,ii=vsprintf(logbuf,fmt,va_endva_endif(if(fd<3{{"push"push ("push"pop"pop"pushl"pushl"pushl"pushl"pushl"pushl"call"call"addl"addl"popl"popl"pop"pop::"r"::"r"(i),"r"(fd):"ax",}}{{if(if(!(file=task[0]->filp[fd])returnreturn ("push"push"push"pop"pushl"pushl"pushl"pushl"call"addl"popl"pop::"r"(i),"r"(file),"r" }}kernel/printk.c文件中添加該函數(shù)的。第一個(gè)參數(shù)fd是文件描述符,為1時(shí)將信息輸入標(biāo)準(zhǔn)輸出,一個(gè)參數(shù);后面的是可變參數(shù)列表,類(lèi)似于printf的可變參數(shù)列表。記錄進(jìn)程的運(yùn)行軌就緒到運(yùn)行:schedule函數(shù)(在文件kernel/sehed.c135行)運(yùn)行到就緒:通過(guò)schedule運(yùn)行到睡眠:sleep_on函數(shù)(kernel/sehed.c202)和192行)sys_waitpid(在文件kernel/exit.c183行)完成。睡眠到就緒:wake_up函數(shù)(在文件kernel/sehed.c251行)完成進(jìn)程的創(chuàng)建:copy_process函數(shù)(在文件kernel/fork.c89行)完成進(jìn)程的退出:do_exit函數(shù)(kernel/exit.c122行)定義process.log文件中每行日志的格式為: 意NJRWEjiffies(參見(jiàn)之后的詳細(xì)介紹。這三個(gè)字段之間用制表符“\t”分隔。例如“06J5296529jiffies的值增加1(在文件kernel/system_call.s243)。outb_p(LATCH&0xff,0x40);outb(LATCH>>8,0x40);宏定義,在文件kernel/sched.c的第57行定義如下:#defineLATCH#defineHZ1/100(10)產(chǎn)生一次時(shí)鐘中斷。所以,jiffies歷了多少個(gè)10毫秒。為了進(jìn)程的創(chuàng)建,可以修改kernel/fork.c文件中的copy_process函數(shù)。在114行后面增加語(yǔ)句(一定在程控制塊的pid和start_time被賦值之后):>start_time=fprintk(3,"%ld\t%c\t%ld\n",p->pid,'N',jiffiesfprintk(3,"%ld\t%c\t%ld\n",p->pid,'J',jiffies中的do_exit函數(shù)。在第159行將進(jìn)程狀態(tài)設(shè)置為T(mén)ASK_ZOMBIE的后面一條語(yǔ)current->state=fprintk(3,"%ld\t%c\t%ld\n",current->pid,'E',jiffies接下來(lái),修改kernel/sched.c文件中的Schedule函數(shù),進(jìn)程進(jìn)入就緒狀態(tài)或運(yùn)行狀態(tài)。注意,Schedule函數(shù)找到的next進(jìn)程是接下來(lái)將要運(yùn)行的進(jìn)程。如果fprintk(3,"%ld\t%c\t%ld\n",(*p)->pid,'J',jiffiesif(current->state==TASK_RUNNING&¤t!= fprintk(3,"%ld\t%c\t%ld\n",current->pid,'J',jiffies);if(current!=task[next]) fprintk(3,"%ld\t%c\t%ld\n",task[next]->pid,'R',另外,只要操作系統(tǒng)處于空閑狀態(tài),就會(huì)讓進(jìn)程0運(yùn)行,進(jìn)程0會(huì)不停的調(diào)用等待。所以,在修改sys_pause函數(shù)時(shí),添加的語(yǔ)句應(yīng)該為:(在第202行if(current->pid!= fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies在Schedule函數(shù)的最后,被選中為next進(jìn)程才會(huì)進(jìn)入實(shí)際的運(yùn)行狀態(tài)(使用標(biāo)志R也就是說(shuō),在讀者添加的所有調(diào)用fprintkSchedule函15)。使用在第3.2節(jié)創(chuàng)建的Linux011用程序項(xiàng)目的硬盤(pán)鏡像文件harddisk.img前Linux011Kernel項(xiàng)目的harddisk.img文件,這樣就可以繼續(xù)使用之前生成的應(yīng)用程序可執(zhí)行文件app了。mcopy/var/process.log 盤(pán)B中的log.txt文件到Windows的C盤(pán)根 06 06 04 06 07 07 08 08 09 09 06 09 09 06 08 08 07 07 06 06 I/OI/Ovoidcpuio_bound(intlast,intcpu_time,intio_time{ structtmsstart_time, clock_tutime, int while(last>0 times(&start_time times(¤t_time }while(((utime+stime)/HZ)<cpu_time last- if(last<=0 while(sleep_time<io_time sleep(1 last- }lastCPUI/OCPUI/Ostructtmsinclude/sys/times.h義,clock_tinclude/time.hmain函數(shù)修改為如下的代碼:intmain(intargc,char*argv[]{ pid_tp1,p2,p3, if((p1=fork())==0 printf("inchild1\n");cpuio_bound(5,2,2 elseif((p2=fork())==0 printf("inchild2\n");cpuio_bound(5,4,0elseelseif((p3=fork())==0 printf("inchild3\n");cpuio_bound(5,0,4 elseif((p4=fork())==0 printf("inchild4\n");cpuio_bound(4,2,2 printf("========Thisisparentprocess=======\n" printf("pid=%d\n",getpid() printf("pid1=%d\n",p1 printf("pid2=%d\n",p2 printf("pid3=%d\n",p3 printf("pid4=%d\n",p4 wait(NULL return}mcopyb:linuxapp.exe od+x od+x結(jié)束調(diào)試,用Linux011應(yīng)用程序項(xiàng)目中的硬盤(pán)鏡像文件harddisk.img覆蓋用剛剛生成的app可執(zhí)行文件了。mcopy/var/process.log 盤(pán)B中的log.txt文件到Windows的C盤(pán)根下。log.txt(log.txt文件拖LinuxLab。查看日志文件中記錄的信息,根據(jù)之前記idapp應(yīng)用程序運(yùn)行時(shí)產(chǎn)生的進(jìn)程完成下面練習(xí)根據(jù)信息以及app的源文件思考app運(yùn)行時(shí)父進(jìn)程及生命周期狀態(tài)轉(zhuǎn)換的統(tǒng)計(jì)并分析數(shù)“stat_log.py”文件到C盤(pán)根下。(在“學(xué)生包”本實(shí)驗(yàn)對(duì)應(yīng)文件夾下提Python的安裝包,有需要的讀者可自行安裝到自己的電腦上)C:\>stat_log.pylog.txt-gC:\>stat_log.pylog.txt-g> (Unit:08014030445312060778090040302090 Throughout:名意CPUI/O因?yàn)檫M(jìn)程0比較特殊,進(jìn)程0運(yùn)行軌跡沒(méi)有進(jìn)完全行記錄,信息誤差比較大可以忽略進(jìn)程0;其它進(jìn)程的信息可能也存在誤差,但在誤差允許范圍之內(nèi)。根據(jù)Linux0.11的進(jìn)程調(diào)度函數(shù)schedule的代碼(在文件kernel/sched.c的135行,可知Linux0.11的調(diào)度算法是選取counter(時(shí)間片)最大的就緒進(jìn)程占用函數(shù)中完kernel/sched.c416所以是一種比較典型的時(shí)間片輪轉(zhuǎn)調(diào)度算法。另外,由schedule函數(shù)可以看出,當(dāng)沒(méi)有counter大于0的就緒進(jìn)程時(shí),要對(duì)所有進(jìn)程做“(*p)->counter=((*p)->counter>>1)+(*p)->priority;”操作(kernel/sched.c182),效果是對(duì)所有的進(jìn)程(包括阻塞進(jìn)程)進(jìn)行counter列中停留的時(shí)間越長(zhǎng),其優(yōu)先級(jí)越大,被分配的時(shí)間片就越大(不會(huì)大于優(yōu)先級(jí)的2以總的來(lái)說(shuō),Linux0.11*p=p->counter=p-時(shí),由于每個(gè)進(jìn)程的優(yōu)先級(jí)都是從父進(jìn)程繼承的,除非自己通過(guò)調(diào)用nicenice00INIT_TASK(在文件include/linux/sched.h的第176行)定義如下:#defineINIT_TASK{0,15,15,\//state,counter,實(shí)驗(yàn)七信號(hào)量的實(shí)現(xiàn)和應(yīng)建議學(xué)時(shí):2學(xué)時(shí)一、實(shí)驗(yàn)?zāi)慷㈩A(yù)備知信號(hào)信號(hào)量(semaphore)最早由荷蘭科學(xué)家、獲得者E.W.Dijkstra設(shè)計(jì)。Linux還沒(méi)有實(shí)現(xiàn)信號(hào)量,也不支持”man”命令。Linux0.11Linux0.11函數(shù)原說(shuō)sem_t*sem_open(constchar*name,unsignedintvalue)創(chuàng)建/打開(kāi)信號(hào)量。name就是信號(hào)量的名字,不同的進(jìn)程可以通過(guò)提供同樣的name一個(gè)信號(hào)量。value是信號(hào)量的初始值,僅當(dāng)intsem_wait(sem_t*semintsem_post(sem_t*intsem_unlink(constchar*生產(chǎn)者—消費(fèi)者問(wèn)三、實(shí)驗(yàn)內(nèi)準(zhǔn)備實(shí)EngintimeLinuxLab3.2按照下面的內(nèi)容在Linux0.11內(nèi)核項(xiàng)目中實(shí)現(xiàn)簡(jiǎn)易版的信號(hào)量。只需要修改量的四個(gè)系統(tǒng)調(diào)用函數(shù)sem_open、sem_wait、sem_post和sem_unlink即可:#defineNR_sem_open87#defineNR_sem_wait88#defineNR_sem_post89#defineNR_sem_unlink90nr_system_calls=externintsys_sem_open(

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論