一個(gè)完整可用的操作系統(tǒng)主要由 4 部分組成:硬件、操作系統(tǒng)內(nèi)核、操作系統(tǒng)服務(wù)和用戶應(yīng)用程序,如下圖所示:
用戶應(yīng)用程序是指那些字處理程序、 Internet 瀏覽器程序或用戶自行編制的各種應(yīng)用程序;
操作系統(tǒng)服務(wù)程序是指那些向用戶所提供的服務(wù)被看作是操作系統(tǒng)的部分功能的程序。
在 Linux 操作系統(tǒng)上,這些程序包括 X 窗口系統(tǒng)、 shell 命令解釋系統(tǒng)以及那些內(nèi)核編程接口等系統(tǒng)程序;操作系統(tǒng)內(nèi)核程序即是本書所感興趣的部分,它主要用于對(duì)硬件資源的抽象和訪問調(diào)度。
Linux 內(nèi)核的主要用途就是為了與計(jì)算機(jī)硬件進(jìn)行交互,實(shí)現(xiàn)對(duì)硬件部件的編程控制和接口操作,調(diào)度對(duì)硬件資源的訪問,并為計(jì)算機(jī)上的用戶程序提供一個(gè)高級(jí)的執(zhí)行環(huán)境和對(duì)硬件的虛擬接口。在本文內(nèi)容中,我們首先基于 Linux 0.11 版的內(nèi)核源代碼,簡(jiǎn)明地描述 Linux 內(nèi)核的基本體系結(jié)構(gòu)、主要構(gòu)成模塊。然后對(duì)源代碼中出現(xiàn)的幾個(gè)重要數(shù)據(jù)結(jié)構(gòu)進(jìn)行說明。最后描述了構(gòu)建 Linux 0.11 內(nèi)核編譯實(shí)驗(yàn)環(huán)境的方法。
本文地址:http://www.CUOXin.com/archimedes/p/linux011-architecture.html,轉(zhuǎn)載請(qǐng)注明源地址。
1、Linux 內(nèi)核模式目前,操作系統(tǒng)內(nèi)核的結(jié)構(gòu)模式主要可分為整體式的單內(nèi)核模式和層次式的微內(nèi)核模式。而Linux 0.11 內(nèi)核,則是采用了單內(nèi)核模式。
單內(nèi)核模式的主要優(yōu)點(diǎn)是內(nèi)核代碼結(jié)構(gòu)緊湊、執(zhí)行速度快,不足之處主要是層次結(jié)構(gòu)性不強(qiáng)。在單內(nèi)核模式的系統(tǒng)中,操作系統(tǒng)所提供服務(wù)的流程為:應(yīng)用主程序使用指定的參數(shù)值執(zhí)行系統(tǒng)調(diào)用指令(int x80),使 CPU 從用戶態(tài)( User Mode)切換到核心態(tài)( Kernel Model),然后操作系統(tǒng)根據(jù)具體的參數(shù)值調(diào)用特定的系統(tǒng)調(diào)用服務(wù)程序,而這些服務(wù)程序則根據(jù)需要再底層的一些支持函數(shù)以完成特定的功能。在完成了應(yīng)用程序所要求的服務(wù)后,操作系統(tǒng)又從核心態(tài)切換回用戶態(tài),返回到應(yīng)用程序中繼續(xù)執(zhí)行后面的指令。
因此概要地講,單內(nèi)核模式的內(nèi)核也可粗略地分為三個(gè)層次:調(diào)用服務(wù)的主程序?qū)印?zhí)行系統(tǒng)調(diào)用的服務(wù)層和支持系統(tǒng)調(diào)用的底層函數(shù)。如下圖所示:?jiǎn)蝺?nèi)核模式的簡(jiǎn)單結(jié)構(gòu)模型
Linux 內(nèi)核主要由 5 個(gè)模塊構(gòu)成,它們分別是:進(jìn)程調(diào)度模塊、內(nèi)存管理模塊、文件系統(tǒng)模塊、進(jìn)程間通信模塊和網(wǎng)絡(luò)接口模塊。
進(jìn)程調(diào)度模塊用來(lái)負(fù)責(zé)控制進(jìn)程對(duì) CPU 資源的使用。所采取的調(diào)度策略是各進(jìn)程能夠公平合理地訪問 CPU,同時(shí)保證內(nèi)核能及時(shí)地執(zhí)行硬件操作。
內(nèi)存管理模塊用于確保所有進(jìn)程能夠安全地共享機(jī)器主內(nèi)存區(qū),同時(shí),內(nèi)存管理模塊還支持虛擬內(nèi)存管理方式,使得 Linux 支持進(jìn)程使用比實(shí)際內(nèi)存空間更多大的內(nèi)存容量。并可以利用文件系統(tǒng)把暫時(shí)不用的內(nèi)存數(shù)據(jù)塊會(huì)被交換到外部存儲(chǔ)設(shè)備上去,當(dāng)需要時(shí)再交換回來(lái)。
文件系統(tǒng)模塊用于支持對(duì)外部設(shè)備的驅(qū)動(dòng)和存儲(chǔ)。虛擬文件系統(tǒng)模塊通過向所有的外部存儲(chǔ)設(shè)備提供一個(gè)通用的文件接口,隱藏了各種硬件設(shè)備的不同細(xì)節(jié)。從而提供并支持與其它操作系統(tǒng)兼容的多種文件系統(tǒng)格式。
進(jìn)程間通信模塊子系統(tǒng)用于支持多種進(jìn)程間的信息交換方式。
網(wǎng)絡(luò)接口模塊提供對(duì)多種網(wǎng)絡(luò)通信標(biāo)準(zhǔn)的訪問并支持許多網(wǎng)絡(luò)硬件。這幾個(gè)模塊之間的依賴關(guān)系見圖 所示。其中的連線代表它們之間的依賴關(guān)系,虛線和虛框部分表示 Linux 0.11 中還未實(shí)現(xiàn)的部分(從 Linux 0.95 版才開始逐步實(shí)現(xiàn)虛擬文件系統(tǒng),而網(wǎng)絡(luò)接口的支持到 0.96版才有)。Linux 內(nèi)核系統(tǒng)模塊結(jié)構(gòu)及相互依賴關(guān)系:
若從單內(nèi)核模式結(jié)構(gòu)模型出發(fā),我們還可以根據(jù) linux 0.11 內(nèi)核源代碼的結(jié)構(gòu)將內(nèi)核主要模塊繪制成下圖所示的框圖結(jié)構(gòu):
對(duì)于 linux 0.11 內(nèi)核來(lái)講, 系統(tǒng)最多可有 64 個(gè)進(jìn)程同時(shí)存在。 系統(tǒng)除了第一個(gè)進(jìn)程是“手工”建立以外, 其余的都是進(jìn)程使用系統(tǒng)調(diào)用 fork 創(chuàng)建的新進(jìn)程。內(nèi)核程序使用進(jìn)程標(biāo)識(shí)號(hào)(PRocess ID, pid)來(lái)標(biāo)識(shí)每個(gè)進(jìn)程。進(jìn)程由可執(zhí)行的指令代碼、數(shù)據(jù)和堆棧區(qū)組成。進(jìn)程中的代碼和數(shù)據(jù)部分分別對(duì)應(yīng)一個(gè)執(zhí)行文件中的代碼段、數(shù)據(jù)段。每個(gè)進(jìn)程只能執(zhí)行自己的代碼和訪問自己的數(shù)據(jù)及堆棧區(qū)。進(jìn)程之間相互之間的通信需要通過系統(tǒng)調(diào)用了進(jìn)行。對(duì)于只有一個(gè) CPU 的系統(tǒng),在某一時(shí)刻只能有一個(gè)進(jìn)程正在運(yùn)行。內(nèi)核通過調(diào)度程序分時(shí)調(diào)度各個(gè)進(jìn)程運(yùn)行。Linux 系統(tǒng)中,一個(gè)進(jìn)程可以在內(nèi)核態(tài)( kernel mode)或用戶態(tài)( user mode)下執(zhí)行,因此, linux 內(nèi)核棧和用戶棧是分開的。用戶棧用于進(jìn)程在用戶態(tài)下臨時(shí)保存調(diào)用函數(shù)的參數(shù)、局部變量等數(shù)據(jù)。內(nèi)核棧則含有內(nèi)核程序執(zhí)行函數(shù)調(diào)用時(shí)的信息。內(nèi)核程序是通過進(jìn)程表對(duì)進(jìn)程進(jìn)行管理的,每個(gè)進(jìn)程在進(jìn)程表中占有一項(xiàng)。在 linux 系統(tǒng)中,進(jìn)程表項(xiàng)是一個(gè) task 結(jié)構(gòu)。當(dāng)一個(gè)進(jìn)程在執(zhí)行時(shí), CPU 的所有寄存器中的值、進(jìn)程的狀態(tài)以及堆棧中的內(nèi)容被稱為該進(jìn)程的上下文。當(dāng)內(nèi)核需要切換( switch)至另一個(gè)進(jìn)程時(shí),它就需要保存當(dāng)前進(jìn)程的所有狀態(tài),也即保存當(dāng)前進(jìn)程的上下文,以便在再次執(zhí)行該進(jìn)程時(shí),能夠恢復(fù)到切換時(shí)的狀態(tài)執(zhí)行下去。在發(fā)生中斷時(shí),內(nèi)核就在被中斷進(jìn)程的上下文中,在內(nèi)核態(tài)下執(zhí)行中斷服務(wù)例程。但同時(shí)會(huì)保留所有需要用到的資源,以便中斷服務(wù)結(jié)束時(shí)能恢復(fù)被中斷進(jìn)程的執(zhí)行。一個(gè)進(jìn)程在其生存期內(nèi),可處于一組不同的狀態(tài)下,稱為進(jìn)程狀態(tài)。見下圖所示:
當(dāng)進(jìn)程正在被 CPU 執(zhí)行時(shí),被稱為處于執(zhí)行狀態(tài)( running)。當(dāng)進(jìn)程正在等待系統(tǒng)中的資源而處于等待狀態(tài)時(shí),則稱其處于睡眠等待狀態(tài)。在 linux 系統(tǒng)中,還分為可中斷的和不可中斷的等待狀態(tài)。當(dāng)系統(tǒng)資源已經(jīng)可用時(shí),進(jìn)程就被喚醒而進(jìn)入準(zhǔn)備運(yùn)行狀態(tài),該狀態(tài)稱為就緒態(tài)。當(dāng)進(jìn)程已停止運(yùn)行,但其父進(jìn)程還沒有詢問其狀態(tài)時(shí),則稱該進(jìn)程處于僵死狀態(tài)。當(dāng)進(jìn)程被終止時(shí),稱其處于停止?fàn)顟B(tài)。只有當(dāng)進(jìn)程從“內(nèi)核運(yùn)行態(tài)”轉(zhuǎn)移到“睡眠狀態(tài)”時(shí),內(nèi)核才會(huì)進(jìn)行進(jìn)程切換操作。在內(nèi)核態(tài)下運(yùn)行的進(jìn)程不能被其它進(jìn)程搶占,而且一個(gè)進(jìn)程不能改變另一個(gè)進(jìn)程的狀態(tài)。為了避免進(jìn)程切換時(shí)造成內(nèi)核數(shù)據(jù)錯(cuò)誤,內(nèi)核在執(zhí)行臨界區(qū)代碼時(shí)會(huì)禁止一切中斷。
在 linux 0.11 內(nèi)核中,為了有效地使用系統(tǒng)的物理內(nèi)存,內(nèi)存被劃分成幾個(gè)功能區(qū)域,見下圖所示:
其中,linux 內(nèi)核程序占據(jù)在物理內(nèi)存的開始部分,接下來(lái)是用于供硬盤或軟盤等塊設(shè)備使用的高速緩沖區(qū)部分。當(dāng)一個(gè)進(jìn)程需要讀取塊設(shè)備中的數(shù)據(jù)時(shí),系統(tǒng)會(huì)首先將數(shù)據(jù)讀到高速緩沖區(qū)中;當(dāng)有數(shù)據(jù)需要寫到塊設(shè)備上去時(shí),系統(tǒng)也是先將數(shù)據(jù)放到高速緩沖區(qū)中,然后由塊設(shè)備驅(qū)動(dòng)程序?qū)懙皆O(shè)備上。最后部分是供所有程序可以隨時(shí)申請(qǐng)使用的主內(nèi)存區(qū)部分。內(nèi)核程序在使用主內(nèi)存區(qū)時(shí),也同樣要首先向內(nèi)核的內(nèi)存管理模塊提出申請(qǐng),在申請(qǐng)成功后方能使用。對(duì)于含有 RAM 虛擬盤的系統(tǒng),主內(nèi)存區(qū)頭部還要?jiǎng)澣ヒ徊糠郑蔡摂M盤存放數(shù)據(jù)。由于計(jì)算機(jī)系統(tǒng)中所含的實(shí)際物理內(nèi)存容量是有限制的。為了能有效地使用這些物理內(nèi)存,Linux 采用了 Intel CPU 的內(nèi)存分頁(yè)管理機(jī)制,使用虛擬線性地址與實(shí)際物理內(nèi)存地址映射的方法讓所有同時(shí)執(zhí)行的程序共同使用有限的內(nèi)存。
內(nèi)存分頁(yè)管理的基本原理是將整個(gè)主內(nèi)存區(qū)域劃分成 4096 字節(jié)為一頁(yè)的內(nèi)存頁(yè)面。程序申請(qǐng)使用內(nèi)存時(shí),就以內(nèi)存頁(yè)為單位進(jìn)行分配。在使用這種內(nèi)存分頁(yè)管理方法時(shí),每個(gè)執(zhí)行中的進(jìn)程(任務(wù))可以使用比實(shí)際內(nèi)存容量大得多的線性地址空間。對(duì)于 Intel 80386 系統(tǒng),其 CPU 可以提供多達(dá) 4G 的線性地址空間。對(duì)于 linux 0.11 內(nèi)核,系統(tǒng)設(shè)置全局描述符表 GDT 中的段描述符項(xiàng)數(shù)最大為 256,其中 2 項(xiàng)空閑、 2 項(xiàng)系統(tǒng)使用,每個(gè)進(jìn)程使用兩項(xiàng)。因此,此時(shí)系統(tǒng)可以最多容納(256-4)/2 + 1=127 個(gè)任務(wù),并且虛擬地址范圍是 ((256-4)/2)* 64MB 約等于8G。 但 0.11 內(nèi)核中人工定義最大任務(wù)數(shù) NR_TASKS = 64 個(gè), 每個(gè)進(jìn)程虛擬地址(或線性地址)范圍是 64M,并且各個(gè)進(jìn)程的虛擬地址起始位置是(任務(wù)號(hào)-1)*64MB。 因此所使用的虛擬地址空間范圍是 64MB*64 =4G,見下圖所示。 4G 正好與 CPU 的線性地址空間范圍或物理地址空間范圍相同,因此在 0.11 內(nèi)核中比較容易混淆三種地址概念。
linux 0.11 中,在進(jìn)行地址映射時(shí),我們需要分清 3 種地址之間的變換: a. 進(jìn)程虛擬地址,是從虛擬地址 0 開始計(jì),最大 64M; b. CPU 的線性地址空間( 0--4G); c. 實(shí)際物理內(nèi)存地址。進(jìn)程的虛擬地址需要首先通過其局部段描述符變換為 CPU 整個(gè)線性地址空間中的地址,然后再使用頁(yè)目錄表 PDT(一級(jí)頁(yè)表)和頁(yè)表 PT(二級(jí)頁(yè)表)映射到實(shí)際物理地址頁(yè)上。因此兩種變換不能混淆。為了使用實(shí)際物理內(nèi)存,每個(gè)進(jìn)程的線性地址通過二級(jí)內(nèi)存頁(yè)表動(dòng)態(tài)地映射到主內(nèi)存區(qū)域的不同內(nèi)存頁(yè)上。因此每個(gè)進(jìn)程最大可用的虛擬內(nèi)存空間是 64MB。每個(gè)進(jìn)程的邏輯地址通過加上任務(wù)號(hào)*64M,即可轉(zhuǎn)換為線性地址。不過在注釋中,我們通常將進(jìn)程中的地址簡(jiǎn)單地稱為線性地址。
5、Linux 內(nèi)核源代碼的目錄結(jié)構(gòu)由于 Linux 內(nèi)核是一種單內(nèi)核模式的系統(tǒng),因此,內(nèi)核中所有的程序幾乎都有緊密的聯(lián)系,它們之間的依賴和調(diào)用關(guān)系非常密切。所以在閱讀一個(gè)源代碼文件時(shí)往往需要參閱其它相關(guān)的文件。因此有必要在開始閱讀內(nèi)核源代碼之前,先熟悉一下源代碼文件的目錄結(jié)構(gòu)和安排。
這里我們首先列出 Linux 內(nèi)核完整的源代碼目錄,包括其中的子目錄。然后逐一介紹各個(gè)目錄中所包含程序的主要功能,使得整個(gè)內(nèi)核源代碼的安排形式能在我們的頭腦中建立起一個(gè)大概的框架,以便于后面開始的源代碼閱讀工作。當(dāng)我們使用 tar 命令將 linux-0.11.tar.gz 解開時(shí),內(nèi)核源代碼文件被放到了 linux 目錄中。其中的目錄結(jié)構(gòu)為:
該內(nèi)核版本的源代碼目錄中含有 14 個(gè)子目錄,總共包括 102 個(gè)代碼文件。下面逐個(gè)對(duì)這些子目錄中的內(nèi)容進(jìn)行描述。1、內(nèi)核主目錄 linuxlinux 目錄是源代碼的主目錄,在該主目錄中除了包括所有的 14 個(gè)子目錄以外,還含有唯一的一個(gè)makefile 文件。該文件是編譯輔助工具軟件 make 的參數(shù)配置文件。 make 工具軟件的主要用途是通過識(shí)別哪些文件已被修改過,從而自動(dòng)地決定在一個(gè)含有多個(gè)源程序文件的程序系統(tǒng)中哪些文件需要被重新編譯。因此, make 工具軟件是程序項(xiàng)目的管理軟件。linux 目錄下的這個(gè) makefile 文件還嵌套地調(diào)用了所有子目錄中包含的 makefile 文件,這樣,當(dāng) linux 目錄(包括子目錄)下的任何文件被修改過時(shí), make 都會(huì)對(duì)其進(jìn)行重新編譯。因此為了編譯整個(gè)內(nèi)核所有的源代碼文件,只要在 linux 目錄下運(yùn)行一次 make 軟件即可。
2、引導(dǎo)啟動(dòng)程序目錄 bootboot 目錄中含有 3 個(gè)匯編語(yǔ)言文件,是內(nèi)核源代碼文件中最先被編譯的程序。這 3 個(gè)程序完成的主要功能是當(dāng)計(jì)算機(jī)加電時(shí)引導(dǎo)內(nèi)核啟動(dòng),將內(nèi)核代碼加載到內(nèi)存中,并做一些進(jìn)入 32 位保護(hù)運(yùn)行方式前的系統(tǒng)初始化工作。 其中 bootsect.s 和 setup.s 程序需要使用 as86 軟件來(lái)編譯, 使用的是 as86 的匯編語(yǔ)言格式(與微軟的類似),而 head.s 需要用 GNU as 來(lái)編譯,使用的是 AT&T 格式的匯編語(yǔ)言。這兩種匯編語(yǔ)言在下一章的代碼注釋里以及代碼列表后面的說明中會(huì)有簡(jiǎn)單的介紹。bootsect.s 程序是磁盤引導(dǎo)塊程序,編譯后會(huì)駐留在磁盤的第一個(gè)扇區(qū)中(引導(dǎo)扇區(qū), 0 磁道(柱面),0 磁頭,第 1 個(gè)扇區(qū))。在 PC 機(jī)加電 ROM BIOS 自檢后,將被 BIOS 加載到內(nèi)存 0x7C00 處進(jìn)行執(zhí)行。setup.s 程序主要用于讀取機(jī)器的硬件配置參數(shù),并把內(nèi)核模塊 system 移動(dòng)到適當(dāng)?shù)膬?nèi)存位置處。head.s 程序會(huì)被編譯連接在 system 模塊的最前部分,主要進(jìn)行硬件設(shè)備的探測(cè)設(shè)置和內(nèi)存管理頁(yè)面的初始設(shè)置工作。
3、文件系統(tǒng)目錄 fs是文件系統(tǒng)實(shí)現(xiàn)程序的目錄,共包含 17 個(gè) C 語(yǔ)言程序。這些程序之間的主要引用關(guān)系所示圖中每個(gè)方框代表一個(gè)文件,從上到下按基本按引用關(guān)系放置。其中各文件名均略去了后綴.c,虛框中是的程序文件不屬于文件系統(tǒng),帶箭頭的線條表示引用關(guān)系,粗線條表示有相互引用關(guān)系。
由圖可以看出,該目錄中的程序可以劃分成四個(gè)部分:高速緩沖區(qū)管理、低層文件操作、文件數(shù)據(jù)訪問和文件高層函數(shù),對(duì)于文件系統(tǒng),我們可以將它看成是內(nèi)存高速緩沖區(qū)的擴(kuò)展部分。所有對(duì)文件系統(tǒng)中數(shù)據(jù)的訪問,都需要首先讀取到高速緩沖區(qū)中。本目錄中的程序主要用來(lái)管理高速緩沖區(qū)中緩沖塊的使用分配和塊設(shè)備上的文件系統(tǒng)。4、頭文件主目錄 include頭文件目錄中總共有 32 個(gè).h 頭文件。其中主目錄下有 13 個(gè), asm 子目錄中有 4 個(gè), linux 子目錄中有10 個(gè), sys 子目錄中有 5 個(gè):
<a.out.h> //a.out 頭文件,定義了 a.out 執(zhí)行文件格式和一些宏。<const.h> //常數(shù)符號(hào)頭文件,目前僅定義了 i 節(jié)點(diǎn)中 i_mode 字段的各標(biāo)志位。<ctype.h> //字符類型頭文件。定義了一些有關(guān)字符類型判斷和轉(zhuǎn)換的宏。<errno.h> //錯(cuò)誤號(hào)頭文件。包含系統(tǒng)中各種出錯(cuò)號(hào)。 (Linus 從 minix 中引進(jìn)的)。<fcntl.h> //文件控制頭文件。用于文件及其描述符的操作控制常數(shù)符號(hào)的定義。<signal.h> //信號(hào)頭文件。定義信號(hào)符號(hào)常量,信號(hào)結(jié)構(gòu)以及信號(hào)操作函數(shù)原型。<stdarg.h> //標(biāo)準(zhǔn)參數(shù)頭文件。以宏的形式定義變量參數(shù)列表。主要說明了 -個(gè)類型(va_list)和三個(gè)宏(va_start, va_arg 和 va_end),用于vsprintf、vprintf、vfprintf 函數(shù)<stddef.h> //標(biāo)準(zhǔn)定義頭文件。定義了 NULL, offsetof(TYPE, MEMBER)。<string.h> //字符串頭文件。主要定義了一些有關(guān)字符串操作的嵌入函數(shù)。<termios.h> // 終端輸入輸出函數(shù)頭文件。主要定義控制異步通信口的終端接口。<time.h> //時(shí)間類型頭文件。其中最主要定義了 tm 結(jié)構(gòu)和一些有關(guān)時(shí)間的函數(shù)原形。<unistd.h> //Linux 標(biāo)準(zhǔn)頭文件。定義了各種符號(hào)常數(shù)和類型,并申明了各種函數(shù)。如定義了__LIBRARY__,則還包括系統(tǒng)調(diào)用號(hào)和內(nèi)嵌匯編_syscall0()等。<utime.h> //用戶時(shí)間頭文件。定義了訪問和修改時(shí)間結(jié)構(gòu)以及 utime()原型
體系結(jié)構(gòu)相關(guān)頭文件子目錄 include/asm這些頭文件主要定義了一些與 CPU 體系結(jié)構(gòu)密切相關(guān)的數(shù)據(jù)結(jié)構(gòu)、宏函數(shù)和變量。共 4 個(gè)文件。
<asm/io.h> //io 頭文件。以宏的嵌入?yún)R編程序形式定義對(duì) io 端口操作的函數(shù)。<asm/memory.h> //內(nèi)存拷貝頭文件。含有 memcpy()嵌入式匯編宏函數(shù)。<asm/segment.h> // 段操作頭文件。定義了有關(guān)段寄存器操作的嵌入式匯編函數(shù)。<asm/system.h> // 系統(tǒng)頭文件。定義了設(shè)置或修改描述符/中斷門等的嵌入式匯編宏。
Linux 內(nèi)核專用頭文件子目錄 include/linux
<linux/config.h> //內(nèi)核配置頭文件。定義鍵盤語(yǔ)言和硬盤類型( HD_TYPE)可選項(xiàng)。<linux/fdreg.h> //軟驅(qū)頭文件。含有軟盤控制器參數(shù)的一些定義。<linux/fs.h> //文件系統(tǒng)頭文件。定義文件表結(jié)構(gòu)( file,buffer_head,m_inode 等)。<linux/hdreg.h> //硬盤參數(shù)頭文件。定義訪問硬盤寄存器端口,狀態(tài)碼,分區(qū)表等信息。<linux/head.h> //head 頭文件,定義了段描述符的簡(jiǎn)單結(jié)構(gòu),和幾個(gè)選擇符常量。<linux/kernel.h> //內(nèi)核頭文件。含有一些內(nèi)核常用函數(shù)的原形定義。<linux/mm.h> //內(nèi)存管理頭文件。含有頁(yè)面大小定義和一些頁(yè)面釋放函數(shù)原型。<linux/sched.h> //調(diào)度程序頭文件,定義了任務(wù)結(jié)構(gòu) task_struct、初始任務(wù) 0 的數(shù)據(jù), //還有一些有關(guān)描述符參數(shù)設(shè)置和獲取的嵌入式匯編函數(shù)宏語(yǔ)句。<linux/sys.h> //系統(tǒng)調(diào)用頭文件。含有 72 個(gè)系統(tǒng)調(diào)用 C 函數(shù)處理程序,以'sys_'開頭。<linux/tty.h> //tty 頭文件,定義了有關(guān) tty_io,串行通信方面的參數(shù)、常數(shù)。
系統(tǒng)專用數(shù)據(jù)結(jié)構(gòu)子目錄 include/sys
<sys/stat.h> //文件狀態(tài)頭文件。含有文件或文件系統(tǒng)狀態(tài)結(jié)構(gòu) stat{}和常量。<sys/times.h> //定義了進(jìn)程中運(yùn)行時(shí)間結(jié)構(gòu) tms 以及 times()函數(shù)原型。<sys/types.h> //類型頭文件。定義了基本的系統(tǒng)數(shù)據(jù)類型。<sys/utsname.h> //系統(tǒng)名稱結(jié)構(gòu)頭文件。<sys/wait.h> //等待調(diào)用頭文件。定義系統(tǒng)調(diào)用 wait()核 waitpid()及相關(guān)常數(shù)符號(hào)。5、內(nèi)核初始化程序目錄 init該目錄中僅包含一個(gè)文件 main.c。用于執(zhí)行內(nèi)核所有的初始化工作,然后移到用戶模式創(chuàng)建新進(jìn)程,并在控制臺(tái)設(shè)備上運(yùn)行 shell 程序。程序首先根據(jù)機(jī)器內(nèi)存的多少對(duì)緩沖區(qū)內(nèi)存容量進(jìn)行分配,如果還設(shè)置了要使用虛擬盤,則在緩沖區(qū)內(nèi)存后面也為它留下空間。之后就進(jìn)行所有硬件的初始化工作,包括人工創(chuàng)建第一個(gè)任務(wù)( task 0),并設(shè)置了中斷允許標(biāo)志。在執(zhí)行從核心態(tài)移到用戶態(tài)之后,系統(tǒng)第一次調(diào)用創(chuàng)建進(jìn)程函數(shù) fork(),創(chuàng)建出一個(gè)用于運(yùn)行 init()的進(jìn)程,在該子進(jìn)程中,系統(tǒng)將進(jìn)行控制臺(tái)環(huán)境設(shè)置,并且在生成一個(gè)子進(jìn)程用來(lái)運(yùn)行 shell程序。
6、內(nèi)核程序主目錄 kernellinux/kernel 目錄中共包含 12 個(gè)代碼文件和一個(gè) Makefile 文件,另外還有 3 個(gè)子目錄。由于這些文件中代碼之間調(diào)用關(guān)系復(fù)雜,因此這里就不詳細(xì)列出各文件之間的引用關(guān)系圖,但仍然可以進(jìn)行大概分類,如圖所示:
asm.s //程序是用于處理系統(tǒng)硬件異常所引起的中斷,對(duì)各硬件異常的實(shí)際處理程序則是在 traps.c 文件中,在各個(gè)中斷處理過程中,將分別調(diào)用traps.c 中相應(yīng)的 C 語(yǔ)言處理函數(shù)exit.c //程序主要包括用于處理進(jìn)程終止的系統(tǒng)調(diào)用。包含進(jìn)程釋放、會(huì)話(進(jìn)程組)終止和程序退出處理函數(shù)以及殺死進(jìn)程、終止進(jìn)程、掛起進(jìn)程等系統(tǒng)調(diào)用函數(shù)。fork.c //程序給出了 sys_fork()系統(tǒng)調(diào)用中使用了兩個(gè) C 語(yǔ)言函數(shù): find_empty_process()和 copy_process()。mktime.c //程序包含一個(gè)內(nèi)核使用的時(shí)間函數(shù) mktime(),用于計(jì)算從 1970 年 1 月 1 日 0 時(shí)起到開機(jī)當(dāng)日的秒數(shù),作為開機(jī)秒時(shí)間。僅在 init/main.c 中被調(diào)用一次。panic. //程序包含一個(gè)顯示內(nèi)核出錯(cuò)信息并停機(jī)的函數(shù) panic()。printk.c //程序包含一個(gè)內(nèi)核專用信息顯示函數(shù) printk()。sched.c //程序中包括有關(guān)調(diào)度的基本函數(shù)(sleep_on、 wakeup、 schedule 等)以及一些簡(jiǎn)單的系統(tǒng)調(diào)用函數(shù)。另外還有幾個(gè)與定時(shí)相關(guān)的軟盤操作函數(shù)。signal.c //程序中包括了有關(guān)信號(hào)處理的 4 個(gè)系統(tǒng)調(diào)用以及一個(gè)在對(duì)應(yīng)的中斷處理程序中處理信號(hào)的函數(shù) do_signal()。sys.c //程序包括很多系統(tǒng)調(diào)用函數(shù),其中有些還沒有實(shí)現(xiàn)。system_call.s //程序?qū)崿F(xiàn)了 linux 系統(tǒng)調(diào)用(int 0x80)的接口處理過程,實(shí)際的處理過程則包含在各系統(tǒng)調(diào)用相應(yīng)的 C 語(yǔ)言處理函數(shù)中,這些處理函數(shù)分布在整個(gè) linux 內(nèi)核代碼中vsprintf.c //程序?qū)崿F(xiàn)了現(xiàn)在已經(jīng)歸入標(biāo)準(zhǔn)庫(kù)函數(shù)中的字符串格式化函數(shù)。
塊設(shè)備驅(qū)動(dòng)程序子目錄 kernel/blk_dev通常情況下,用戶是通過文件系統(tǒng)來(lái)訪問設(shè)備的,因此設(shè)備驅(qū)動(dòng)程序?yàn)槲募到y(tǒng)實(shí)現(xiàn)了調(diào)用接口。在使用塊設(shè)備時(shí),由于其數(shù)據(jù)吞吐量大,為了能夠高效率地使用塊設(shè)備上的數(shù)據(jù),在用戶進(jìn)程與塊設(shè)備之間使用了高速緩沖機(jī)制。在訪問塊設(shè)備上的數(shù)據(jù)時(shí),系統(tǒng)首先以數(shù)據(jù)塊的形式把塊設(shè)備上的數(shù)據(jù)讀入到高速緩沖區(qū)中,然后再提供給用戶。 blk_dev 子目錄共包含 4 個(gè) c 文件和 1 個(gè)頭文件。頭文件 blk.h 由于是塊設(shè)備程序?qū)S玫模耘c C 文件放在一起。這幾個(gè)文件之間的大致關(guān)系,如圖所示:
blk.h //中定義了 3 個(gè) C 程序中共用的塊設(shè)備結(jié)構(gòu)和數(shù)據(jù)塊請(qǐng)求結(jié)構(gòu)。 hd.c //程序主要實(shí)現(xiàn)對(duì)硬盤數(shù)據(jù)塊進(jìn)行讀/寫的底層驅(qū)動(dòng)函數(shù),主要是 do_hd__request()函數(shù); floppy.c //程序中主要實(shí)現(xiàn)了對(duì)軟盤數(shù)據(jù)塊的讀/寫驅(qū)動(dòng)函數(shù),主要是 do_fd_request()函數(shù)。 ll_rw_blk.c //中程序?qū)崿F(xiàn)了低層塊設(shè)備數(shù)據(jù)讀/寫函數(shù) ll_rw_block(),內(nèi)核中所有其它程序都是通過該函數(shù)對(duì)塊設(shè)備進(jìn)行數(shù)據(jù)讀寫操作。
你將看到該函數(shù)在許多訪問塊設(shè)備數(shù)據(jù)的地方被調(diào)用,尤其是在高速緩沖區(qū)處理文件 fs/buffer.c 中。
字符設(shè)備驅(qū)動(dòng)程序子目錄 kernel/chr_dev字符設(shè)備程序子目錄共含有 4 個(gè) C 語(yǔ)言程序和 2 個(gè)匯編程序文件。 這些文件實(shí)現(xiàn)了對(duì)串行端口 rs-232、串行終端、鍵盤和控制臺(tái)終端設(shè)備的驅(qū)動(dòng)。下圖(圖 2.12)是這些文件之間的大致調(diào)用層次關(guān)系:
tty_io.c //程序中包含 tty 字符設(shè)備讀函數(shù) tty_read()和寫函數(shù) tty_write(),為文件系統(tǒng)提供了上層訪問接 //口。另外還包括在串行中斷處理過程中調(diào)用的 C 函數(shù) do_tty_interrupt(),該函數(shù)將會(huì)在中斷類型為讀字符 //的處理中被調(diào)用。console.c //文件主要包含控制臺(tái)初始化程序和控制臺(tái)寫函數(shù) con_write(),用于被 tty 設(shè)備調(diào)用。還包含 //對(duì)顯示器和鍵盤中斷的初始化設(shè)置程序 con_init()。 rs_io.s //匯編程序用于實(shí)現(xiàn)兩個(gè)串行接口的中斷處理程序。 該中斷處理程序會(huì)根據(jù)從中斷標(biāo)識(shí)寄存器(端 //口 0x3fa 或 0x2fa)中取得的 4 種中斷類型分別進(jìn)行處理,并在處理中斷類型為讀字符的代碼中調(diào)用do_tty_interrupt()。 serial.c //用于對(duì)異步串行通信芯片 UART 進(jìn)行初始化操作,并設(shè)置兩個(gè)通信端口的中斷向量。 //另外還包括 tty 用于往串口輸出的 rs_write()函數(shù)。 tty_ioctl.c //程序?qū)崿F(xiàn)了 tty 的 io 控制接口函數(shù) tty_ioctl()以及對(duì) termio(s)終端 io 結(jié)構(gòu)的讀寫函數(shù),并會(huì) //在實(shí)現(xiàn)系統(tǒng)調(diào)用 sys_ioctl()的 fs/ioctl.c 程序中被調(diào)用。 keyboard.S //程序主要實(shí)現(xiàn)了鍵盤中斷處理過程 keyboard_interrupt。
協(xié)處理器仿真和操作程序子目錄 kernel/math該子目錄中目前僅有一個(gè) C 程序 math_emulate.c。其中的 math_emulate()函數(shù)是中斷 int7 的中斷處理程序調(diào)用的 C 函數(shù)。當(dāng)機(jī)器中沒有數(shù)學(xué)協(xié)處理器,而 CPU 卻又執(zhí)行了協(xié)處理器的指令時(shí),就會(huì)引發(fā)該中斷。因此,使用該中斷就可以用軟件來(lái)仿真協(xié)處理器的功能。本書所討論的內(nèi)核版本還沒有包含有關(guān)協(xié)處理器的仿真代碼。本程序中只是打印一條出錯(cuò)信息,并向用戶程序發(fā)送一個(gè)協(xié)處理器錯(cuò)誤信號(hào) SIGFPE。7、內(nèi)核庫(kù)函數(shù)目錄 lib內(nèi)核庫(kù)函數(shù)主要用于用戶編程調(diào)用,是編譯系統(tǒng)標(biāo)準(zhǔn)庫(kù)的接口函數(shù)之一。其中共有 12 個(gè) C 語(yǔ)言文件,除了一個(gè)由 tytso 編制的 malloc.c 程序較長(zhǎng)以外,其它的程序很短,有的只有一二行代碼。8、內(nèi)存管理程序目錄 mm該目錄包括 2 個(gè)代碼文件。主要用于管理程序?qū)χ鲀?nèi)存區(qū)的使用,實(shí)現(xiàn)了進(jìn)程邏輯地址到線性地址以及線性地址到主內(nèi)存區(qū)中物理內(nèi)存地址的映射,通過內(nèi)存的分頁(yè)管理機(jī)制,在進(jìn)程的虛擬內(nèi)存頁(yè)與主內(nèi)存區(qū)的物理內(nèi)存頁(yè)之間建立了對(duì)應(yīng)關(guān)系。page.s 文件包括內(nèi)存頁(yè)面異常中斷( int 14)處理程序,主要用于處理程序由于缺頁(yè)而引起的頁(yè)異常中斷和訪問非法地址而引起的頁(yè)保護(hù)。memory.c 程序包括對(duì)內(nèi)存進(jìn)行初始化的函數(shù) mem_init(),由 page.s 的內(nèi)存處理中斷過程調(diào)用的do_no_page()和 do_wp_page()函數(shù)。在創(chuàng)建新進(jìn)程而執(zhí)行復(fù)制進(jìn)程操作時(shí),即使用該文件中的內(nèi)存處理函數(shù)來(lái)分配管理內(nèi)存空間。9、編譯內(nèi)核工具程序目錄 tools該目錄下的 build.c 程序用于將 Linux 各個(gè)目錄中被分別編譯生成的目標(biāo)代碼連接合并成一個(gè)可運(yùn)行的內(nèi)核映象文件 image。具體的功能后面詳細(xì)介紹
6、內(nèi)核系統(tǒng)與用戶程序的關(guān)系在 Linux 系統(tǒng)中,內(nèi)核為應(yīng)用程序提供了兩方面的接口。其一是系統(tǒng)調(diào)用接口,也即中斷調(diào)用 int 0x80;另一方面是通過內(nèi)核庫(kù)函數(shù),與內(nèi)核進(jìn)行信息交流。內(nèi)核庫(kù)函數(shù)是基本 C 函數(shù)庫(kù) libc 的組成部分。許多的系統(tǒng)調(diào)用是作為基本 C 語(yǔ)言函數(shù)庫(kù)的一部分實(shí)現(xiàn)的。系統(tǒng)調(diào)用主要是提供給系統(tǒng)軟件直接使用或用于庫(kù)函數(shù)的實(shí)現(xiàn)。而一般用戶開發(fā)的程序則是通過調(diào)用象 libc 等庫(kù)中的函數(shù)來(lái)訪問內(nèi)核資源。通過調(diào)用這些庫(kù)中的程序,應(yīng)用程序代碼能夠完成各種常用工作,例如,打開和關(guān)閉對(duì)文件或設(shè)備的訪問、進(jìn)行科學(xué)計(jì)算、出錯(cuò)處理以及訪問組和用戶標(biāo)識(shí)號(hào) ID 等系統(tǒng)信息。系統(tǒng)調(diào)用是內(nèi)核與外界接口的最高層。在內(nèi)核中,每個(gè)系統(tǒng)調(diào)用都有一個(gè)序列號(hào)(在include/linux/unistd.h 頭文件中定義),并常以宏的形式實(shí)現(xiàn)。
參考資料Linux內(nèi)核完全注釋(趙炯)
新聞熱點(diǎn)
疑難解答
圖片精選