 
         
         
         
         
        版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux系統的啟動
2.1操作系統的啟動
一般來說,操作系統的引導過程分兩個步驟。首先,電腦硬體經過開機自檢(PowerOnSelf-Test,POST)之後,從軟碟或硬碟的固定位置裝載一小段代碼,這段代碼一般稱為“引導裝載器”。然後,由引導裝載器負責裝入並運行操作系統。引導裝載器非常小,一般只有幾百個位元組,而操作系統龐大而複雜。上述分成兩階段的引導過程,可將電腦中的固化軟體保持得足夠小,同時也便於實現對不同操作系統的引導。2.1.1系統引導過程簡介
系統啟動過程主要由以下幾個步驟組成(以硬碟啟動為例):(1)開機(2)BIOS加電自檢(PowerOnSelfTest,POST),記憶體地址為0ffff:0000(3)將硬碟第一個扇區(0頭0道1扇區,也就是BootSector)讀入記憶體地址0000:7c00處。(4)檢查(WORD)0000:7dfe是否等於0xaa55,若不等於則轉去嘗試其他啟動介質,如果沒有其他啟動介質則顯示"NoROMBASIC"然後死機。(5)跳轉到0000:7c00處執行MBR中的程式。(6)MBR首先將自己複製到0000:0600處,然後繼續執行。(7)在主分區表中搜索標誌為活動的分區。如果發現沒有活動分區或有不止一個活動分區,則停止。(8)將活動分區的第一個扇區讀入記憶體地址0000:7c00處。(9)檢查(WORD)0000:7dfe是否等於0xaa55,若不等於則顯示"MissingOperatingSystem"然後停止,或嘗試軟碟啟動。(10)跳轉到0000:7c00處繼續執行特定系統的啟動程式。(11)啟動系統。以上步驟中2,3,4,5步是由BIOS的引導程式完成。6,7,8,9,10步由MBR中的引導程式完成。一般多系統引導程式(如SmartBootManager、BootStar、PQBoot等)都是將標準主引導記錄替換成自己的引導程式,在運行系統啟動程式之前讓用戶選擇要啟動的分區。而某些系統自帶的多系統引導程式(如LILO,NTLoader等)則可以將自己的引導程式放在系統所處分區的第一個扇區中,在Linux中即為是兩個扇區的SuperBlock。注:以上各步驟中使用的是標準MBR,其他多系統引導程式的引導過程與此不同。2.1.2硬碟結構
1.硬碟參數當硬碟的容量還非常小的時候,人們採用與軟碟類似的結構生產硬碟。硬碟盤片的每一條磁軌都具有相同的扇區數。由此產生了所謂的3D參數(DiskGeometry)以及相應的尋址方式。到目前為止,通常還是沿用這種CHS(Cylinder/Head/Sector)來表示硬碟參數。其中:磁頭數(Heads)表示硬碟總共有幾個磁頭,也就是有幾面盤片,最大為256(用8個二進位位存儲);柱面數(Cylinders)表示硬碟每一面盤片上有幾條磁軌,最大為1024(用10個二進位位存儲);扇區數(Sectorspertrack)表示每一條磁軌上有幾個扇區,最大為63(用6個二進位位存儲)。每個扇區一般是512個位元組。2.基本INT13H調用簡介
BIOSint13H調用是BIOS提供的磁片基本輸入輸出中斷調用,它可以完成磁片(包括硬碟和軟碟)的複位、讀寫、校驗、定位、診斷和格式化等功能。它使用的是CHS尋址方式,因此最大只能訪問8GB左右的硬碟(本文中如不作特殊說明,均以1M=1048576位元組為單位)。3.現代硬碟結構簡介在老式硬碟中,由於每個磁軌的扇區數相等,所以外道的記錄密度要遠低於內道,因此會浪費很多磁片空間。為了進一步提高硬碟容量,人們改用等密度結構生產硬碟。也就是說,外圈磁軌的扇區比內圈磁軌多。採用這種結構後,硬碟不再具有實際的3D參數,尋址方式也改為線性尋址,即以扇區為單位進行尋址。為了與使用3D尋址的老軟體相容(如使用BIOSINT13H介面的軟體),在硬碟控制器內部安裝了一個地址翻譯器,由它負責將老式3D參數翻譯成新的線性參數。不同的工作模式(如LBA、LARGE、NORMAL)對應不同的3D參數。4.擴展INT13H
雖然現代硬碟都已經採用了線性尋址,但是由於基本INT13H的制約,使用BIOS的INT13H介面的程式(如DOS等)還只能訪問8G以內的硬碟空間。為了打破這一限制,Microsoft等幾家公司制定了擴展INT13H標準(ExtendedINT13H),採用線性尋址方式存取硬碟,突破了8G的限制,並還加入了對可拆卸介質(如活動硬碟)的支持。2.1.3引導扇區
1.BootSector的組成
BootSector也就是硬碟的第一個扇區,它由MBR(MasterBootRecord),DPT(DiskPartitionTable)和BootRecordID三部分組成。MBR又稱作主引導記錄,佔用BootSector的前446個位元組(0to0x1BD),存放系統主引導程式(它負責從活動分區中裝載並運行系統引導程式)。DPT即主分區表佔用64個位元組(0x1BEto0x1FD),記錄了磁片的基本分區資訊。主分區表分為四個分區項,每項16位元組,分別記錄了每個主分區的資訊(因此最多可以有四個主分區)。BootRecordID即引導區標記佔用兩個位元組(0x1FEand0x1FF),對於合法引導區,它等於0xAA55,這是判別引導區是否合法的標誌。BootSector的具體結構如下圖所示:2.分區表結構簡介分區表由四個分區項構成,每一項的結構如下:BYTEState:分區狀態,0=未啟動,0x80=啟動(注意此項)BYTEStartHead:分區起始磁頭號WORDStartSC:分區起始扇區和柱面號,底位元組的低6位為扇區號,高2位為柱面號的第9,10位,高位元組為柱面號的低8位BYTEType:分區類型,如0x0B=FAT32,0x83=Linux等,00表示此項未用BYTEEndHead:分區結束磁頭號WORDEndSC:分區結束扇區和柱面號,定義同前DWORDRelative:線上性尋址方式下的分區相對扇區地址(對於基本分區即為絕對地址)DWORDSectors:分區大小(總扇區數)
在DOS/Windows系統下,基本分區必須以柱面為單位劃分(Sectors*Heads個扇區),如對於CHS為764/256/63的硬碟,分區的最小尺寸為256*63*512/1048576=7.875MB。由於硬碟的第一個扇區已經被引導扇區佔用,所以一般來說,硬碟第一個磁軌(0頭0道)的其餘62個扇區是不會被分區佔用的。某些分區軟體甚至將第一個柱面全部空出來。3.擴展分區由於主分區表中只能分四個分區,無法滿足需求,因此設計了一種擴展分區格式。基本上說,擴展分區的資訊是以鏈表形式存放的,但也有一些特別的地方。
首先,主分區表中要有一個基本擴展分區項,所有擴展分區都隸屬於它,也就是說其他所有擴展分區的空間都必須包括在這個基本擴展分區中。對於DOS/Windows來說,擴展分區的類型為0x05或0x0F(LBA模式)。除基本擴展分區以外的其他所有擴展分區則以鏈表的形式級聯存放,後一個擴展分區的資料項目記錄在前一個擴展分區的分區表中,但兩個擴展分區的空間並不重疊。
擴展分區類似於一個完整的硬碟,必須進一步分區才能使用。但每個擴展分區中只能存在一個其他分區。此分區在DOS/Windows環境中即為邏輯盤。因此每一個擴展分區的分區表(同樣存儲在擴展分區的第一個扇區中)中最多只能有兩個分區資料項目(包括下一個擴展分區的資料項目)。以上所有擴展分區表中的第二個分區項(指向下一個擴展分區)的相對扇區地址均相對於主擴展分區,而不是前一個擴展分區。
2.2Linux的引導過程
不同電腦平臺引導過程的區別主要在於第一階段的引導過程。對PC機上的Linux系統而言,電腦(即BIOS)負責從軟碟或硬碟的第一個扇區(即引導扇區)中讀取引導裝載器,然後,由引導裝載器從磁片或其他位置裝入操作系統。從軟碟引導時,BIOS讀取並運行引導扇區中的代碼。引導扇區中的代碼讀取軟碟前幾百個塊(依賴於實際的內核大小),然後將這些代碼放置在預先定義好的記憶體位置。利用軟碟引導Linux時,沒有檔系統,內核處於連續的扇區中,這樣安排可簡化引導過程。但是,如果利用LILO(LInuxLOader)也可從包含檔系統的軟碟上引導Linux。
從硬碟引導時,由於硬碟是可分區的,因此引導過程比軟碟複雜一些。BIOS首先讀取並運行硬碟主引導記錄中的代碼,這些代碼首先檢驗主引導記錄中的分區表,尋找到活動分區(即標誌為可引導分區的分區),然後讀取並運行活動分區之引導扇區中的代碼。活動分區引導扇區的作用和軟碟引導扇區的作用一樣:從分區中讀取內核映象並啟動內核。和軟碟引導不同的是,內核映象保存在硬碟分區檔系統中,而不象軟碟那樣保存在後續的連續扇區中,因此,硬碟引導扇區中的代碼還需要定位內核映象在檔系統中的位置,然後裝載內核並啟動內核。Linux系統的常見引導方式有兩種:LILO引導和Loadin引導;其中LILO可實現多重引導,Loadin可在DOS下引導Linux。此外,Linux內核也自帶了一個bootsect-loader。由於bootsect-loader只能實現Linux的引導,不像前兩個那樣具有很大的靈活性,所以在普通應用場合實際上很少使用。但由於bootsect-loader短小、沒有多餘的代碼、並且是內核源碼的有機組成部分。下麵將主要對bootsect-loader檔進行分析。bootsect-loader在內和源碼中對應的程式是/arch/i386/boot/bootsect.s。幾個相關檔是:(1)/arch/i386/boot/bootsect.s(2)/include/linux/config.h(3)/include/asm/boot.h(4)/include/linux/autoconf.h2.2.1Linux系統引導過程分析
開啟Intelx86PC的電源後,機器就會開始執行ROMBIOS的一系列系統測試動作,包括檢查RAM、鍵盤、顯示器和軟硬磁片等。接著控制權轉移給ROM中的啟動程式(ROMbootstraproutine);該程式會將磁片上的第0軌第0扇區(稱為bootsector或MasterBootRecord,MBR,系統的引導程式就放在此處)讀入記憶體,放入自0x07C0:0x0000開始的512個位元組處;然後處理機跳轉到該處開始執行(位於該處的)MBR引導程式,CS:IP=0x07C0:0x0000。加電後處理機運行在與8086相相容的實模式下。如果要用bootsect-loader進行系統引導,則必須把bootsect.s編譯連接後對應的二進位代碼置於MBR;當ROMBIOS把bootsect.s編譯連接後對應的二進位代碼裝入記憶體後,機器的控制權就完全轉交給bootsect;也就是說,bootsect將是第一個被讀入記憶體中並執行的程式。Bootsect接管機器控制權後,將依次進行以下一些動作:(1)首先,bootsect將它“自己”(自位置0x07C0:0x0000開始的512個位元組)從被ROMBIOS載入的地址0x07C0:0x0000處搬到0x9000:0000處;這一任務由bootsect.s的前十條指令完成;第十一條指令“jmpigo,INITSEG”則把機器跳轉到“新”的bootsect的“jmpigo,INITSEG”後的那條指令“go:movdi,#0x4000-12”;之後,繼續執行bootsect的剩下的代碼;在bootsect.s中定義了幾個常量:BOOTSEG=0x07C0bios載入MBR的約定位置的段址;INITSEG=0x9000bootsect.s的前十條指令將自己搬到此處(段址)SETUPSEG=0x9020裝入Setup.s的段址SYSSEG=0x1000系統區段址這些常量的定義可參見/include/asm/boot.h;在下面的分析中這些常量會經常用到。(2)以0x9000:0x4000-12為棧底,建立自己的棧區;其中0x9000:0x4000-12到0x9000:0x4000的一十二個位元組預留作磁片參數表區;(3)在0x9000:0x4000-12到0x9000:0x4000的一十二個預留位元組中建立新的磁片參數表。由於設計者考慮到有些老的bios不能準確地識別磁片每個磁軌的扇區數,使得bios建立的磁片參數表妨礙磁片的最高性能發揮,所以設計者在bios建立的磁片參數表的基礎上使用枚舉法測試,試圖建立準確的“新”的磁片參數表(這是在後繼步驟中完成的);並把參數表的位置由原來的0x0000:0x0078搬到0x9000:0x4000-12;且修改老的磁片參數表區使之指向新的磁片參數表;(4)接下來是load_setup子過程;它調用0x13中斷的第2號服務;把第0道第2扇區開始的連續的setup_sects(為常量4)個扇區讀到緊鄰bootsect的記憶體區;,即0x9000:0x0200開始的2048個位元組;而這四個扇區的內容就是/arch/i386/boot/setup.s編譯連接後對應的二進位代碼。換句話說,如果要用bootsect-loader進行系統引導,不僅必須把bootsect.s編譯連接後對應的二進位代碼置於MBR,而且還要將\把setup.s編譯連接後對應的二進位代碼置於緊跟MBR後的連續的四個扇區中。由於setup.s對應的可執行碼是由bootsect裝載的,所以可以根據需要修改bootsect來設置setup.s對應的可執行碼;(5)load_setup子過程的惟一出口是probe_loop子過程;該過程通過枚舉法測試磁片“每個磁軌的扇區數”;(6)接下來幾個子過程比較清晰:顯示“Loading”;讀入系統到0x1000:0x0000;關掉軟驅馬達;根據的5步測出的“每個磁軌的扇區數”確定磁片類型;最後跳轉到0x9000:0x0200,即setup.s對應的可執行碼的入口,將機器控制權轉交setup.s;整個bootsect代碼運行完畢。引導過程執行完後的記憶體映像圖如圖2.3所示。完成了系統的引導後,系統將進入到初始化處理階段。系統的初始化分為實模式和保護模式兩部分。為了簡單起見,在上面的分析中,忽略了對大內核的處理的分析。因為對大內核的處理,只是此引導過程中的一個很小的部分,並不影響對整體的把握。2.2.2實模式下的初始化
實模式下的初始化,主要是指從內核引導成功後,到進入保護模式之前系統所做的一些處理。在內核源碼中對應的程式是/arch/i386/boot/setup.s;以下部分主要對該檔進行分析。這部分的分析要弄懂它的處理流程和INITSEG(9000:0000)段參數表的建立的過程。該參數表包含了很多硬體參數,這些都是以後進行保護模式下初始化以及核心建立的基礎。相關檔有:
/arch/i386/boot/bootsect.s/include/linux/config.h/include/asm/boot.h /include/asm/segment.h/include/linux/version.h/include/linux/compile.h
setup.s完成在實模式下版本檢查,並將硬碟、滑鼠及記憶體的參數寫入到INITSEG中,並負責進入保護模式。實模式下的初始化過程如圖2.4及圖2.5所示:
圖2.4實模式下的初始化過程1圖2.5實模式下的初始化過程2表2.1INITSEG(9000:0000)段參數表:(參見include/linux/tty.h)參數名偏移量(段址均為0x9000)長度Byte參考檔PARAM_CURSOR_POS0x00002arch/i386/boot/video.sextendedmemSize0x00022arch/i386/boot/setup.sPARAM_VIDEO_PAGE0x00042arch/i386/boot/video.sPARAM_VIDEO_MODE0x00061arch/i386/boot/video.sPARAM_VIDEO_COLS0x00071arch/i386/boot/video.s未用0x00082include/linux/tty.hPARAM_VIDEO_EGA_BX0x000a2arch/i386/boot/video.s未用0x000c2include/linux/tty.hPARAM_VIDEO_LINES0x000e1arch/i386/boot/video.sPARAM_HAVE_VGA0x000f1arch/i386/boot/video.sPARAM_FONT_POINTS0x00102arch/i386/boot/video.sPARAM_LFB_WIDTH0x00122arch/i386/boot/video.sPARAM_LFB_HEIGHT0x00142arch/i386/boot/video.sPARAM_LFB_DEPTH0x00162arch/i386/boot/video.sPARAM_LFB_BASE0x00184arch/i386/boot/video.sPARAM_LFB_SIZE0x001c4arch/i386/boot/video.s暫未用①0x00204include/linux/tty.hPARAM_LFB_LINELENGTH0x00242arch/i386/boot/video.sPARAM_LFB_COLORS0x00266arch/i386/boot/video.s暫未用②0x002c2arch/i386/boot/video.sPARAM_VESAPM_SEG0x002e2arch/i386/boot/video.sPARAM_VESAPM_OFF0x00302arch/i386/boot/video.sPARAM_LFB_PAGES0x00322arch/i386/boot/video.s保留0x0034--0x003f
include/linux/tty.hAPMBIOSVersion③0x00402arch/i386/boot/setup.sBIOScodesegment0x00422arch/i386/boot/setup.sBIOSentryoffset0x00444arch/i386/boot/setup.sBIOS16bitcodeseg0x00482arch/i386/boot/setup.sBIOSdatasegment0x004a2arch/i386/boot/setup.s支持32位標誌④0x004c2arch/i386/boot/setup.sBIOScodeseglength0x004e4arch/i386/boot/setup.sBIOSdataseglength0x00522arch/i386/boot/setup.shd0參數0x008016arch/i386/boot/setup.shd0參數0x009016arch/i386/boot/setup.sPS/2device標誌⑤0x01ff1arch/i386/boot/setup.s
注:①include/linux/tty.h:CL_MAGICandCL_OFFSEThere②include/linux/tty.h:unsignedcharrsvd_size;//0x2cunsignedcharrsvd_pos;//0x2d③0表示沒有電源管理(APM)BIOS④0x0002置位表示支持32位模式⑤0表示沒有,0x0aa表示有滑鼠器2.2.3保護模式下的初始化
保護模式下的初始化,是指處理機進入保護模式後到運行系統第一個內核程式過程中,系統所做的一些處理。保護模式下的初始化在內核源碼中對應的程式是/arch/i386/boot/compressed/head.s和/arch/i386/kernel/head.s;以下部分主要是針對這兩個檔進行的分析。在i386體系結構中,因為i386本身的問題,在"arch/alpha/kernel/head.s"中需要更多的設置,最終是通過callSYMBOL_NAME(start_kernel)轉到start_kernel()這個體系結構無關的函數中去執行了。
在i386系統中,當內核以bzImage的形式壓縮,即大內核方式(BIG_KERNEL)壓縮時就需要預先處理bootsect.s和setup.s,按照大核模式使用$(CPP)處理生成bbootsect.s和bsetup.s,然後再編譯生成相應的.o檔,並使用"arch/i386/boot/compressed/build.c"生成的build工具,將實際的內核(未壓縮的,含kernel中的head.s代碼)與"arch/i386/boot/compressed"下的head.s和misc.c合成到一起,其中的head.s代替了"arch/i386/kernel/head.s"的位置,由Bootloader引導執行(startup_32入口),然後它調用misc.c中定義的decompress_kernel()函數,使用"lib/inflate.c"中定義的gunzip()將內核解壓到0x100000,再轉到其上執行"arch/i386/kernel/head.s"中的startup_32代碼。幾個有關檔是:/arch/i386/boot/compressed/head.s/arch/i386/kernel/head.s//arch/i386/boot/compressed/MISC.c/arch/i386/boot/setup.s/include/asm/segment.h/arch/i386/kernel/traps.c/include/i386/desc.h/include/asm-i386/processor.h保護模式下的初始化過程大致如下:1./arch/i386/kernel/head.s流程因為在setup.s最後的是一條轉跳指令,跳到內核第一條指令並開始執行。指令中指向的是記憶體中的絕對地址,無法依此判斷轉跳到了head.s。但是可以通過Makefile簡單的確定head.s位於內核的前端。在arch/i386的Makefile中定義了HEAD:=arch/i386/kernel/head.o
在Linux總的Makefile中的語句includearch/$(ARCH)/Makefile說明HEAD定義在該檔中有效。由如下語句:vmlinux:$(CONFIGURATION)init/main.oinit/version.olinuxsubdirs$(LD)$(LINKFLAGS)$(HEAD)init/main.oinit/version.o\$(ARCHIVES)\$(FILESYSTEMS)\$(DRIVERS)\$(LIBS)-ovmlinux$(NM)vmlinux|grep-v'\(compiled\)\|\(\.o$$\)\|\(a\)'|sort>System.map從這個依賴關係可以獲得大量的資訊:(1)$(HEAD)即head.o的確第一個被連接到核心中;(2)內核支持的所有檔系統全部編譯到$(FILESYSTEMS)即fs/filesystems.a中;(3)內核中支持的所有網路協議全部編譯到net.a中;(4)內核中支持的所有SCSI驅動全部編譯到scsi.a中;
這樣看來,內核就是一堆庫檔和目標檔的集合。如果要對內核減肥的話,可以好好比較一下,看究竟是那個部分佔用了空間。在System.map中包含了所有的內核輸出的函數,這些是在編寫內核模組的時候可以調用的系統函數。2.head.s的分析(1)首先將ds、es、fs和gs指向系統數據段KERNEL_DS(KERNEL_DS在asm/segment.h中定義,表示全局描述符表中中的第三項)。
注意:該此時生效的全局描述符表並不是在head.s中定義的,而仍然是在setup.s中定義的。(2)數據段全部清空。(3)setup_idt為一段子程式,將中斷向量表全部指向ignore_int函數。該函數列印出“unknowninterrupt”,當然這樣的中斷處理函數什麼也幹不了。(4)查看數據線A20是否有效,否則迴圈等待。地址線A20是x86的歷史遺留問題,決定是否能訪問1M以上記憶體。(5)拷貝啟動參數到0x5000頁的前半頁,而將setup.s取出的bios參數放到後半頁。(6)檢查CPU類型。(7)初始化頁表,只初始化最初幾頁。①將swapper_pg_dir(0x2000)和pg0(0x3000)清空,swapper_pg_dir作為整個系統的頁目錄。②將pg0作為第一個頁表,將其地址賦到swapper_pg_dir的第一個32位字中。③同時將該頁表項也賦給swapper_pg_dir的第3072個入口,表示虛擬地址0xc0000000也指向pg0。④將pg0這個頁表填滿指向記憶體前4M。⑤進入分頁方式。(8)裝入新的GDT和ldt表。(9)刷新段寄存器ds、es、fs和gs。(10)使用系統堆疊,即預留的0x6000頁面。(11)執行start_kernel函數,這是第一個C編制的函數,內核又有了一個新的開始。圖2.6/arch/i386/kernel/head.s流程圖一圖2.7/arch/i386/kernel/head.s流程圖二3./arch/i386/boot/compressed/head.s流程圖2.8是/arch/i386/boot/compressed/head.s的流程圖。從圖中可看到,保護模式下的初始化主要做了幾件事:解壓內核到0x100000處、建立頁目錄和pg0頁表並啟動分頁功能(即虛存管理功能)、保存實模式下測到的硬體資訊到empty_zero_page、初始化命令緩存區、檢測cpu類型、檢查協處理器、重新建立GDT全局描述符表和中斷描述附表IDT。
圖2.8/arch/i386/boot/compressed/head.s流程
從頁目錄和pg0頁表可以看出,0;4M物理記憶體被用作系統區,它被映射到系統段線性空間的0;4M和3G;3G+4M;即系統可以通過訪問這兩個段來訪問實際的0;4M物理記憶體,也就是系統所在的區域;本來在實模式下初始化時已經建立了全局描述符表GDT,而此處重新建立全局描述符表GDT則主要是出於兩個原因:一個就是若內核是大內核bzimag,則以前建立的GDT,可能在解壓時已經被覆蓋了。所以在這個源碼檔中均只採用相對轉移指令jxxnf或jxxnb;二是以前建立的GDT是建立在實地址方式下的,而現在則是在啟用保護虛擬地址方式之後建立的,也即現在的GDT是建立在邏輯地址(即線性地址)上的;每次建立新的GDT後和啟用保護虛擬地址方式後都必須重新裝載系統棧和重新初始化各段寄存器:cs,ds,es,fs,gs;
從實模式下的初始化和保護模式下的初始化過程可以看出,Linux系統由實模式進入到保護模式的過程大致如圖2.9所示。由於分頁機制只能在保護模式下啟動,不能在實模式下啟動,所以第一步是必要的;又因為在386保護模式下GDT和IDT是建立在邏輯地址(線性地址)上的,所以第三步也是必要的;位置系統數據大小0x101000頁目錄swapper_pg_dir4K0x102000頁表pg04K0x103000empty_bad_page4K0x104000empty_bad_page_table4K0x105000empty_zero_page4K0x105000系統硬體參數2K0x105800命令緩衝區2K0x106000全局描述附表gdt_table4192B經過實模式和保護模式下的初始化後,主要系統數據的分佈如表2.2所示。
從上面對Linux系統的初始化過程的分析可以看出,以程式執行流程為線索,即按照程式的執行先後順序,弄懂程式執行的各個階段所進行的處理,及其各階段之間的相互聯繫。而流程圖應該是這種分析方法最合適的表達工具。事實上,以程式執行流程為線索,是分析任何源代碼都首選的方法。用這種方法來分析系統的初始化過程或用戶進程的執行流程應該說是很有效的。當然由於操作系統的特殊性,光用這種方法是遠遠不夠的。2.2.4系統初始化
Linux內核裝入之後,Linux內核進行硬體和設備驅動程式的初始化,然後運行init。init是Linux內核啟動的第一個用戶級進程,其進程標識號始終為1,該進程在系統引導和關機過程中扮演重要角色。Linux內核進行的初始化工作可大體描述如下:(1)Linux內核一般是壓縮保存的,因此,它首先要進行自身的解壓縮。內核映象前面的一些代碼完成解壓縮。(2)如果系統中安裝有可支持特殊文本模式的、且Linux可識別的SVGA卡,Linux會提示用戶選擇適當的文本顯示模式。但是,如果在內核的編譯過程中預先設置了文本模式,則不會提示選擇顯示模式。該顯示模式也可通過LILO或rdev設置。(3)內核接下來檢測其他的硬體設備,例如硬碟、軟碟和網卡等,並對相應的設備驅動程式進行配置。這時,內核會輸出一些硬體資訊,類似下麵的輸出:LILOboot:
LoadingLinux.
Memory:sizedbyint13088h
Console:16pointfont,400scans
Console:colourVGA+80x25,1virtualconsole(max63)
pcibios_init:BIOS32ServiceDirectorystructureat0x000f7510
pcibios_init:BIOS32ServiceDirectoryentryat0xfd7d2
pcibios_init:PCIBIOSrevision2.10entryat0xfd9e6
ProbingPCIhardware.
Calibratingdelayloop..ok-231.83BogoMIPS
Memory:30760k/32704kavailable(748kkernelcode,384kreserved,812kdata)
SwanseaUniversityComputerSocietyNET3.035forLinux2.0
NET3:UNIXdomainsockets0.13forLinuxNET3.035.
SwanseaUniversityComputerSocietyTCP/IPforNET3.034
IPProtocols:IGMP,ICMP,UDP,TCP
LinuxIPmulticastrouter0.07.
VFS:Diskquotasversiondquot_5.6.0initialized
Checking386/387coupling...Ok,fpuusingexception16errorreporting.
Checking'hlt'instruction...Ok.
Linuxversion2.0.36(root@porky.RedH)(gccversion)#1TueOct1322:17:11EDT1998
Startingkswapdv
Serialdriverversion4.13withnoserialoptionsenabled
tty00at0x03f8(irq=4)isa16550A
RealTimeClockDriverv1.09
Ramdiskdriverinitialized:16ramdisksof4096Ksize
ide:i82371PIIX(Triton)onPCIbus0function57
ide0:BM-DMAat0xfcd0-0xfcd7
ide1:BM-DMAat0xfcd8-0xfcdf
hda:HITACHI_DK237A-32,3102MBw/512kBCache,CHS=788/128/63,UDMA
hdc:CD-224E,ATAPICDROMdrive
ide0at0x1f0-0x1f7,0x3f6onirq14
ide1at0x170-0x177,0x376onirq15
Floppydrive(s):fd0is1.44M
FDC0isapost-199182077
mddriver0.36.3MAX_MD_DEV=4,MAX_REAL=8
scsi:0hosts.
scsi:detectedtotal.
Partitioncheck:
hda:hda1hda2<hda5>hda3hda4(4)接下來,內核掛裝root檔系統。root檔系統的位置可在編譯內核時指定,也可通過LILO或rdev指定。檔系統的類型可自動檢測。如果由於某些原因掛裝失敗,則內核啟動失敗,最終會終止系統。root檔系統一般掛裝為只讀檔系統。(5)此後,內核啟動init進程(位於/sbin/init)。一般而言,init進程要啟動一些重要的後臺守護進程。(6)然後,init切換到多用戶模式,並為每個虛擬控制臺和串行線路啟動一個getty
進程,getty
進程管理用戶從虛擬控制臺和串行終端上的登錄。根據不同的配置,init
也可以啟動其他進程。(7)至此,系統的引導過程結束。
初始化進程開始執行/sbin/init之後,系統內核就不再對程式進行直接控制了。之後系統內核的作用主要是給進程提供系統調用,以及提供非同步中斷事件的處理。多任務機制已經建立起來,並開始處理多個用戶的登錄和fork()創建的進程。對Linux內核源碼的分析,有幾個很好的入口點:一個就是系統的引導和初始化,即從機器加電到系統核心的運行;對於那些對硬體比較熟悉的讀者,可從系統的引導入手進行分析,根據系統的初始化過程,把系統的初始化過程所涉及到的代碼分析清楚。
Linux的源碼是一個組織得有條有理的蛛網。要把整個結構分析清楚,除了找出線頭,還得理順各個部分之間的關係,有條不紊的一點一點的分析。練習與思考
1.閱讀Linux系統中etc/rc源代碼,畫出流程圖。2.簡述Linux系統一般的開機引導過程。3.分析init/main.c中關於系統啟動的相關代碼得到相關的框圖,寫出設計報告。第3章中斷和中斷處理硬體中斷機制是一個操作系統內核中非常重要的部分。它的設計直接影響到操作系統整體的性能。它與硬體平臺和內核的其他部分,如記憶體管理、進程調度、設備驅動等都有很密切的關係。因此,它也是操作系統中比較複雜的一個模組。Linux的硬體中斷機制的設計有很多獨到之處,本章把kernel2.4和kernel2.2.x的相關機制進行詳細的對比,使讀者能夠更好的領會最新的kernel2.4中的硬體中斷機制。3.1硬體提供的中斷機制和約定硬中斷即和硬體相關的中斷也就是通常意義上的“中斷處理程式”,它是直接處理由硬體發過來的中斷信號的。當某個設備發出中斷請求時,CPU停止正在執行的指令,轉而跳到包括中斷處理代碼或者包括指向中斷處理代碼的轉移指令所在的記憶體區域。這些代碼一般在CPU的中斷方式下運行。就回去自己驅動的設備上去看看設備的狀態寄存器以瞭解發生了什麼事情,並進行相應的操作。當中斷處理完畢以後,CPU將恢復到以前的狀態,繼續執行中斷處理前正在執行的指令。中斷的流程如圖3.1所示。3.1硬體提供的中斷機制和約定圖3.1中斷流程大多數處理器在處理中斷過程方式下將不會再有中斷發生。但有些CPU的中斷有自己的優先權,更高優先權的中斷則可以發生。這意味著第一級的中斷處理程式必須擁有自己的堆疊,以便在處理更高級別的中斷前保存CPU的執行狀態。3.1硬體提供的中斷機制和約定Linux系統是包含內核、系統工具、完整的開發環境和應用的類Unix操作系統。這個系統是由全世界各地的成千上萬的程式員設計和實現的。1984年,RichardStallman創立了GNU工程,其目標是開發一個完全免費的類Unix系統及其應用程式。1991年,芬蘭赫爾辛基大學一位名叫Linus
Torvalds的學生開始了開放源代碼的Linux雛形的設計。其目的是建立不受任何商品化軟體的版權制約的、全世界都能自由使用的Unix相容產品由於Linux是一套具有Unix全部功能的免費操作系統,它在眾多的軟體中佔有很大的優勢,為廣大的電腦愛好者提供了學習、探索以及修改電腦操作系統內核的機會3.1.1中斷產生的過程CPU在一些外部硬體的幫助下處理中斷。中斷處理硬體和具體的系統相關,但一般來說,這些硬體系統和i386處理器的中斷系統在功能上是一致的。圖3.2i386PC可編程中斷控制器8259A級鏈示意圖3.1.1中斷產生的過程對於中斷,CPU只提供兩條外接引線:NMI和INTR;這裏的中斷線是實際存在的電路,它們通過硬體介面連接到CPU外的設備控制器上。NMI只能通過端口操作來遮罩,它通常用於電源掉電和物理記憶體奇偶驗錯;INTR可通過直接設置中斷遮罩位來遮罩,它可用來接受外部中斷信號。INTR只有一條引線,為更好的處理外部設備,x86微機通過外接兩片級連了可編程中斷控制器8259A,以接受更多的外部中斷信號。每個8259A中斷控制器可以管理8條中斷線,當兩個8259級聯的時候共可以控制15條中斷線。在圖3.2表示了兩個級聯的中斷控制器,從屬中斷控制器的輸出連接到了主中斷控制器的第3個中斷信號輸入,這樣,該系統可處理的外部中斷數量最多可達15個。圖的右邊是i386PC中各中斷輸入管腳的一般分配。可通過對8259A的初始化,使這15個外接引腳對應256個中斷向量的任何15個連續的向量。設備通過中斷線向中斷控制器發送高電平告訴操作系統它產生了一個中斷,而操作系統會從中斷控制器的狀態位知道是哪條中斷線上產生了中斷。3.1.1中斷產生的過程8259A主要完成中斷優先順序排隊管理、接受外部中斷請求和向CPU提供中斷類型號這樣一些任務。由於Intel公司保留0-31號中斷向量用來處理異常事件,所以,硬中斷必須設在31以後,Linux則在實模式下初始化時把硬中斷設在0x20-0x2F。外部設備產生的中斷實際是電平的變化信號,外部設備產生的中斷信號在IRQ(中斷請求)管腳上,這一信號首先由中斷控制器處理。中斷控制器可以回應多個中斷輸入,它的輸出連接到CPU的INT管腳,CPU在該管腳上的電平變化可通知處理器產生了中斷。如果CPU這時可以處理中斷,CPU會通過INTA(中斷確認)管腳上的信號通知中斷控制器已接受中斷,這時,中斷控制器可將一個8位數據放置在數據匯流排上,這一8位數據也稱為中斷向量號,CPU依據中斷向量號和中斷描述符表(IDT)中的資訊自動調用相應的中斷服務程式。3.1.1中斷產生的過程中斷控制器中的控制寄存器實際映射到了CPU的I/O地址空間中,通過對寄存器的設置,可設定中斷控制器遮罩某些中斷,也可以指定中斷控制器的特殊回應方式,因此,中斷控制器也稱為可編程中斷控制器。在Linux中,兩個中斷控制器初始設置為固定優先順序的中斷回應方式。有關可編程控制器的詳細資訊可參閱有關的資料。中斷處理程式得知設備發生了一個中斷,但並不知道設備發生了什麼事情,只有在訪問了設備上的一些狀態寄存器以後,才能知道具體發生了什麼,要怎麼去處理。3.1.2中斷請求設備只有對某一條確定的中斷線擁有了控制權,才可以向這條中斷線上發送信號。由於電腦的外部設備越來越多,所以15條中斷線已經不夠用了。要使用中斷線,就得進行中斷線的申請,就是IRQ(InterruptRequirement),也常把申請一條中斷線稱為申請一個IRQ或者是申請一個中斷號。IRQ是非常寶貴的,所以建議只有當設備需要中斷的時候才申請佔用一個IRQ,或者是在申請IRQ時採用共用中斷的方式,這樣可以讓更多的設備使用中斷。無論對IRQ的使用方式是獨佔還是共用,申請IRQ的過程都分為3步:(1)將所有的中斷線探測一遍,看看哪些中斷還沒有被佔用。從這些還沒有被佔用的中斷中選一個作為該設備的IRQ。(2)通過中斷申請函數申請選定的IRQ,這是要指定申請的方式是獨佔還是共用。(3)根據中斷申請函數的返回值決定怎麼做:如果成功了則執行中斷,如果沒成功則或者重新申請或者放棄申請並返回錯誤。223.1.3置中斷標誌位在處理中斷的時候,中斷控制器會遮罩掉原先發送中斷的那個設備,直到它發送的上一個中斷被處理完了為止。因此如果發送中斷的那個設備載中斷處理期間又發送了一個中斷,那麼這個中斷就被永遠的丟失了。這種情況之所以發生,是因為中斷控制器並不能緩衝中斷資訊。當前面的一個中斷沒有處理完之前又有新的中斷到達,中斷控制器就會丟掉新的中斷。這個問題可以通過設置主處理器(CPU)上的“置中斷標誌位”(sti)來解決,因為主處理器具有緩衝中斷的功能。如果使用了“置中斷標誌位”,在處理完中斷以後使用sti函數就可以使先前被遮罩的中斷得到服務。3.1.3中斷處理程式的不可重入性有時候需要遮罩中斷,是出於管理上的考慮。因為中斷處理程式是不可重入的,所以不能並行執行同一個中斷處理程式,因此在中斷處理的過程中要遮罩由同一個IRQ來的新中斷。由於設備驅動程式要和設備的寄存器打交道,設備寄存器就是全局變數。如果一個中斷處理程式可以並行,很有可能會發生驅動程式鎖死的情況。當驅動程式鎖死的時候,操作系統並不一定會崩潰,但是鎖死的驅動程式所支持的那個設備就不能再使用了。因此,最簡單的辦法就是禁止同一設備的中斷處理程式並行,即設備的中斷處理程式是不可重入的。由於中斷處理程式要求不可重入,編寫可重入的中斷處理程式則幾乎是不可能的。所以通常不必編寫可重入的中斷處理程式。但可編寫可重入的設備驅動程式。一旦中斷的競爭條件出現,有可能會發生死鎖的情況,嚴重時可能會將整個系統鎖死。所以一定要避免競爭條件的出現。3.1.4時鐘和定時器中斷操作系統應該能夠在將來某個時刻準時調度某個任務。所以需要一種能保證準時調度某個任務運行的機制。希望支持每種操作系統的微處理器必須包含一個可週期性中斷它的可編程間隔定時器。該定時器可以在指定的時間週期性地中斷處理器。這個週期性中斷被稱為系統時鐘週期,它像音樂中的節拍器一樣來協調著系統中所有的活動。除此之外,操作系統還必須具備一定的介面記錄系統的時間,並為程式提供時間服務。一般來說,操作系統和電腦硬體一起維護著系統中的時間。Linux的時鐘觀念很簡單:它表示系統啟動後的以時鐘週期計數的時間。在PC機中,Linux利用BIOSCMOS中記錄的時間(稱為“硬體時鐘”)作為系統啟動時的時間基準,而在系統運行時,利用時鐘週期測量系統的時間(稱為“軟體時鐘”)。3.1.4時鐘和定時器中斷Linux利用全局變數jiffies(暫態)作為系統時間的測量基準,所有的時間都從1970.1.10:00:00開始計算,系統啟動時,將CMOS中記錄的時間轉化為從1970.1.10:00:00算起的jiffies值。Linux內核中沒有任何時區的概念,Linux內核中的時間以格林尼治時間記錄,將格林尼治時間轉換為本地時間的任務則由應用程式負責。Linux的jiffies值由兩部分組成,分別用32位無符號整數記錄自1970.1.100:00:00開始的秒數以及秒數千分值。這樣,Linux可正確處理的時間值最大到1970年後的138年,即2108年,而時間的計量也可精確到千分之一秒。在到達2108年之前,人們會想出更好的辦法來計時。Linux包含兩種類型的系統定時器,它們都可以在某個系統時間上被佇列例程使用,但是它們的實現稍有區別。圖3.3說明了這兩種定時器機制。3.1.4時鐘和定時器中斷圖3.3Linux中的兩種系統定時器3.1.4時鐘和定時器中斷第一種是老的定時器機制,它包含指向timer_struct結構的32位指針的靜態數組以及當前活動定時器的掩碼:time_active。此定時器表中的位置是靜態定義的(類似底層部分的數據結構bh_base)。數組中的元素通常是靜態定義的,在系統初始化過程中填充這些元素。其入口在系統初始化時被加入到表中。第二種是相對較新的定時器機制,它使用以定時器到期時間的昇冪排列的timer_list鏈表結構組織。3.1.4時鐘和定時器中斷這兩種方法都使用jiffies作為時間週期的終結。如果某個定時器要在5秒之後到期,則必須將5秒時間轉換成對應的jiffies值,並且將它和以jiffies計數的當前系統時間相加從而得到該定時器到期的系統時間。在每個系統時鐘週期裏,定時器的底層部分處理過程被標記成活動狀態,當調度程式下次運行時能進行定時器佇列的處理。定時器底層部分處理過程要處理上述兩種類型的系統定時器。對老的系統定時器來說,就是檢查timer_active位是否置位。如果活動定時器已經到期(到期時間大於或等於當前系統的jiffies),則調用對應的定時器例程,並清除timer_active中的相應活動位。對於新定時器,則檢查鏈表中的timer_list數據結構,每個到期的定時器從鏈表中移出,而對應的定時器例程被調用。新的定時器機制的優點之一是能傳遞一個參數給定時器例程。Linux提供了兩種定時器服務。一種早期的由timer_struct等結構描述,由run_old_times函數處理。另一種“新”的服務由timer_list等結構描述,由add_timer、del_timer、cascade_time和run_timer_list等函數處理。3.1.4時鐘和定時器中斷早期的定時器服務利用如下數據結構:structtimer_struct{unsignedlongexpires;
//本定時器被喚醒的時刻void(*fn)(void);
//定時器喚醒後的處理函數}structtimer_structtimer_table[32];
//最多可同時啟用32個定時器unsignedlongtimer_active;//每位對應一定時器,置1表示啟用新的定時器服務依靠鏈表結構突破了32個的限制,利用如下的數據結構:structtimer_list{structtimer_list*next;structtimer_list*prev;unsignedlongexpires;unsignedlongdata;
//用來存放當前進程的PCB塊的指針,可作為參數傳void(*function)(unsignedlong);
給function}3.1.4時鐘和定時器中斷系統啟動核心時,調用start_kernal()開始各方面的初始化,在這之前,各種中斷都被禁止,只有在完成必要的初始化後,直到執行完Kmalloc_init()後,才允許中斷(init\main.c)。在CPU調度時、系統調用返回前和中斷處理返回前都會作判斷調用do_bottom_half函數。Do_bottom_half函數依次掃描32個佇列,找出需要服務的佇列,執行服務後把對應該隊列的bh_active的相應位置0。由於bh_active標誌中TIMER_BH對應的bit為1,因而系統根據服務函數入口地址數組bh_base找到函數timer_bh()的入口地址,並馬上執行該函數,在函數timer_bh中,調用函數run_timer_list()和函數run_old_timers()函數,定時執行服務。3.2Linux的中斷處理3.2.1Linux中斷處理程式的特色考慮到中斷處理的效率,Linux的中斷處理程式分為兩個部分:上半部(tophalf)和下半部(bottomhalf)。上半部的功能是“登記中斷”。當一個中斷發生時,就把設備驅動程式中中斷例程的下半部掛到該設備的下半部執行佇列中去,然後就等待新的中斷的到來。這樣,上半部執行的速度就會很快,就可以接受所負責設備產生的更多中斷。上半部之所以要快,是因為它是完全遮罩中斷的,其他的中斷只能等到這個中斷處理程式執行完畢以後才能申請,不能得到及時的處理。快速的中斷處理程式就可以對設備產生的中斷盡可能多地進行服務。3.2Linux的中斷處理有些中斷事件的處理比較複雜,中斷處理程式必須多花一點時間才能夠把事情做完。為了化解在短時間內完成複雜處理的矛盾,Linux引入了下半部的概念。下半部和上半部最大的不同是下半部是可中斷的,而上半部是不可中斷的。上半部只是將下半部放入了它們所負責的設備的中斷處理佇列中去,然後就什麼都不管了。因此,下半部幾乎做了中斷處理程式所有的工作,包括查看設備上的寄存器以獲得產生中斷的事件資訊,並根據這些資訊進行相應的處理。如果下半部不知道怎麼去做,它就使用鴕鳥演算法來解決問題,即忽略這個事件。由於下半部是可中斷的,所以在它運行期間,如果其他的設備產生了中斷,這個下半部可以暫時的中斷掉,等到那個設備的上半部運行完了,再回頭來運行它。但是要注意,如果一個設備中斷處理程式正在運行,無論它是運行上半部還是運行下半部,只要中斷處理程式還沒有處理完畢,在這期間設備產生的新的中斷都將被忽略掉。因為中斷處理程式是不可重入的,同一個中斷處理程式是不能並行的。3.2Linux的中斷處理Linux將中斷處理程式劃分成兩個部分的一個原因,是要把中斷的總延遲時間最小化。Linux內核定義了兩種類型的中斷,快速的和慢速的,這兩者之間的一個區別是慢速中斷自身還可以被中斷,而快速中斷則不能。因此,當處理快速中斷時,如果有其他中斷到達;不管是快速中斷還是慢速中斷,它們都必須等待。為了盡可能快地處理這些其他的中斷,內核就需要盡可能地將處理延遲到下半部分執行。其次,當內核執行上半部分時,正在服務的這個特殊IRQ將會被可編程中斷控制器禁止,於是,連接在同一個IRQ上的其他設備就只有等到該該中斷處理被處理完畢後果才能發出IRQ請求。而採用Bottom_half機制後,不需要立即處理的部分就可以放在下半部分處理,從而,加快了處理機對外部設備的中斷請求的回應速度。3.2Linux的中斷處理還有一個原因是,處理程式的下半部分還可以包含一些並非每次中斷都必須處理的操作;對這些操作,內核可以在一系列設備中斷之後集中處理一次就可以了。即在這種情況下,每次都執行並非必要的操作完全是一種浪費,而採用Bottom_half機制後,可以稍稍延遲並在後來只執行一次就行了。在下半部中也可以進行中斷遮罩。如果某一段代碼不能被中斷的話。可以使用cti、sti或者是save_flag、restore_flag來實現。3.2.2中斷的相關數據結構從數據結構入手,應該說是分析操作系統源碼最常用的和最主要的方法。因為操作系統的幾大功能部件,如進程管理、設備管理、記憶體管理等,都可以通過對其相應的數據結構的分析來弄懂其實現機制。很好的掌握這種方法,對分析Linux內核大有幫助。中斷向量在保護模式下的實現機制是中斷描述符表(InterruptDescriptorTable,IDT),中斷描述符表的結構如圖3.4所示。中斷描述符表即中斷向量表相當於一個數組,包含256個中斷描述符,每個中斷描述符8位,對應硬體提供的256個中斷服務例程的入口,即256個中斷向量。IDT的位置由idtr確定,idtr是個48位的寄存器,高32位是IDT的基址,低16位為IDT的界限(通常為2k=256*8)。3.2.2中斷的相關數據結構圖3.4Linux的中斷處理數據結構3.2.2中斷的相關數據結構在i386系統中,Linux啟動時要設置系統的中斷描述符表IDT。IDT中包含各個中斷(以及異常,諸如浮點運算溢出)的服務程式地址,中斷服務程式地址由Linux提供。每個設備驅動程式可以在圖3.4所示的結構(irq_action)中註冊自己的中斷及中斷處理程式地址。Linux的中斷服務程式根據irq_action中的註冊資訊調用相應的設備驅動程式的中斷處理程式。和硬體相關的中斷處理代碼隱藏在中斷服務程式中,這樣,設備驅動程式的中斷處理程式可在不同平臺之間方便移植。一般來說,CPU在處理中斷時,首先要在堆疊中保存與CPU指令執行相關的寄存器(例如指令計數寄存器),然後調用中斷服務程式,中斷服務程式結束時再恢復這些寄存器。3.2.2中斷的相關數據結構圖3.5與硬中斷相關的幾個數據結構的關係3.2.2中斷的相關數據結構irq_action實際是一個數組,其中包含指向irqaction的指針,每個數組元素分別定義一個IRQ。Linux內核提供相應的操作函數,設備驅動程式可調用這些操作函數設置相應的中斷處理函數。一般在系統啟動時,由各個設備驅動程式通過如下途徑獲取相關的設備IRQ並設置對應的irq_action數組元素所指向的irqaction結構。由於0-31號中斷向量已被Intel保留,就剩下32-255共224個中斷向量可用。在Linux中,這224個中斷向量除了0x80(SYSCALL_VECTOR)用作系統調用總入口之外,其他都用在外部硬體中斷源(包括可編程中斷控制器8259A的15個irq)上。實際上,當沒有定義CONFIG_X86_IO_APIC時,其他223(除0x80外)個中斷向量,只利用了從32號開始的15個,其他208個空著未用。這些中斷服務程式入口的設置將在下面詳細說明。與硬中斷相關數據結構主要有三個,三者關係如圖3.5所示。3.2.2中斷的相關數據結構(1)定義在/arch/i386/Kernel/irq.h中的structhw_interrupt_type數據結構,它是一個抽象的中斷控制器。這包含一系列的指向函數的指針,這些函數處理控制器特有的操作:typename:控制器的名字。startup:允許從給定的控制器的IRQ所產生的事件。shutdown:禁止從給定的控制器的IRQ所產生的事件。handle:根據提供給該函數的IRQ,處理唯一的中斷。enable和disable:這兩個函數基本上和startup和shutdown相同;structhw_interrupt_type{constchar*typename;void(*startup)(unsignedintirq);void(*shutdown)(unsignedintirq);void(*handle)(unsignedintirq,structpt_regs*regs);void(*enable)(unsignedintirq);void(*disable)(unsignedintirq);};3.2.2中斷的相關數據結構(2)定義在/arch/i386/Kernel/irq.h中的另外一個數據結構是irq_desc_t,它具有如下成員:status:一個整數。代表IRQ的狀態:IRQ是否被禁止了,有關IRQ的設備當前是否正被自動檢測,等等。handler:指向hw_interrupt_type的指針。action:指向irqaction結構組成的佇列的頭。正常情況下每個IRQ只有一個操作,因此鏈接列表的正常長度是1(或者0)。但是,如果IRQ被兩個或者多個設備所共用,那麼這個佇列中就有多個操作。depth:irq_desc_t的當前用戶的個數。主要是用來保證在中斷處理過程中IRQ不會被禁止。3.2.2中斷的相關數據結構irq_desc是irq_desc_t類型的數組。對於每一個IRQ都有一個數組入口,即數組把每一個IRQ映射到和它相關的處理程式和irq_desc_t中的其他資訊。typedefstruct{unsignedintstatus;//IRQstatus-IRQ_INPROGRESS,IRQ_DISABLEDstructhw_interrup
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 食堂聘用合同(标准版)
- 质借款合同(标准版)
- 青岛市人民医院髋部骨折手术治疗方案选择考核
- 南京市人民医院睡眠脑电图专项考核
- 漳州市人民医院科室学术道德监督考核
- 高中历史文化传播科普试题及答案
- 鹰潭市中医院心力衰竭的社区早期识别与稳定期管理考核
- 大兴安岭中医院压力治疗技术考核
- 南通市中医院输血科主治医师晋升考核
- 2025-2030碳中和目标下绿色金融产品创新与碳交易市场发展预测报告
- 汽车文化考试题及答案
- 2025年新员工入职协议书
- 长春中考直播解读课件
- 运动康复放松培训课件
- 2025下半年四川成都东部新区教育卫健和文旅体局教育系统所属事业单位考试招聘31人考试参考试题及答案解析
- 职高思政考试试题及答案
- 信息化项目合同5篇
- 算力:新质生产力的基石
- 工伤基础培训课件
- 做事先做人培训课件
- 定格动画教学课件
 
            
评论
0/150
提交评论