![]() Linux內(nèi)核的作用是將應(yīng)用程序的請(qǐng)求傳遞給硬件,并充當(dāng)?shù)讓域?qū)動(dòng)程序,對(duì)系統(tǒng)中的各種設(shè)備和組件進(jìn)行尋址。目前支持模塊的動(dòng)態(tài)裝卸(裁剪)。Linux內(nèi)核就是基于這個(gè)策略實(shí)現(xiàn)的。 Linux進(jìn)程采用層次結(jié)構(gòu),每個(gè)進(jìn)程都依賴于一個(gè)父進(jìn)程。內(nèi)核啟動(dòng)init程序作為第一個(gè)進(jìn)程。該進(jìn)程負(fù)責(zé)進(jìn)一步的系統(tǒng)初始化操作。init進(jìn)程是進(jìn)程樹的根,所有的進(jìn)程都直接或者間接起源于該進(jìn)程。 1. 理解Linux內(nèi)核最好預(yù)備的知識(shí)點(diǎn):
結(jié)合了unix操作系統(tǒng)的一些基礎(chǔ)概念: 3. Linux內(nèi)核的任務(wù):
4. 內(nèi)核實(shí)現(xiàn)策略:
5. 哪些地方用到了內(nèi)核機(jī)制?
6. Linux進(jìn)程
include/ ---- 內(nèi)核頭文件,需要提供給外部模塊(例如用戶空間代碼)使用。 ▲ Linux系統(tǒng)層次結(jié)構(gòu) 最上面是用戶(或應(yīng)用程序)空間。這是用戶應(yīng)用程序執(zhí)行的地方。用戶空間之下是內(nèi)核空間,Linux 內(nèi)核正是位于這里。GNU C Library (glibc)也在這里。它提供了連接內(nèi)核的系統(tǒng)調(diào)用接口,還提供了在用戶空間應(yīng)用程序和內(nèi)核之間進(jìn)行轉(zhuǎn)換的機(jī)制。這點(diǎn)非常重要,因?yàn)閮?nèi)核和用戶空間的應(yīng)用程序使用的是不同的保護(hù)地址空間。每個(gè)用戶空間的進(jìn)程都使用自己的虛擬地址空間,而內(nèi)核則占用單獨(dú)的地址空間。 Linux 內(nèi)核可以進(jìn)一步劃分成 3 層。最上面是系統(tǒng)調(diào)用接口,它實(shí)現(xiàn)了一些基本的功能,例如 read 和 write。系統(tǒng)調(diào)用接口之下是內(nèi)核代碼,可以更精確地定義為獨(dú)立于體系結(jié)構(gòu)的內(nèi)核代碼。這些代碼是 Linux 所支持的所有處理器體系結(jié)構(gòu)所通用的。在這些代碼之下是依賴于體系結(jié)構(gòu)的代碼,構(gòu)成了通常稱為 BSP(Board Support Package)的部分。這些代碼用作給定體系結(jié)構(gòu)的處理器和特定于平臺(tái)的代碼。 Linux 內(nèi)核實(shí)現(xiàn)了很多重要的體系結(jié)構(gòu)屬性。在或高或低的層次上,內(nèi)核被劃分為多個(gè)子系統(tǒng)。Linux 也可以看作是一個(gè)整體,因?yàn)樗鼤?huì)將所有這些基本服務(wù)都集成到內(nèi)核中。這與微內(nèi)核的體系結(jié)構(gòu)不同,后者會(huì)提供一些基本的服務(wù),例如通信、I/O、內(nèi)存和進(jìn)程管理,更具體的服務(wù)都是插入到微內(nèi)核層中的。每種內(nèi)核都有自己的優(yōu)點(diǎn),不過這里并不對(duì)此進(jìn)行討論。 隨著時(shí)間的流逝,Linux 內(nèi)核在內(nèi)存和 CPU 使用方面具有較高的效率,并且非常穩(wěn)定。但是對(duì)于 Linux 來說,最為有趣的是在這種大小和復(fù)雜性的前提下,依然具有良好的可移植性。Linux 編譯后可在大量處理器和具有不同體系結(jié)構(gòu)約束和需求的平臺(tái)上運(yùn)行。一個(gè)例子是 Linux 可以在一個(gè)具有內(nèi)存管理單元(MMU)的處理器上運(yùn)行,也可以在那些不提供 MMU 的處理器上運(yùn)行。 ▲ Linux內(nèi)核體系結(jié)構(gòu) Linux內(nèi)核的主要組件有:系統(tǒng)調(diào)用接口、進(jìn)程管理、內(nèi)存管理、虛擬文件系統(tǒng)、網(wǎng)絡(luò)堆棧、設(shè)備驅(qū)動(dòng)程序、硬件架構(gòu)的相關(guān)代碼。 SCI 層提供了某些機(jī)制執(zhí)行從用戶空間到內(nèi)核的函數(shù)調(diào)用。正如前面討論的一樣,這個(gè)接口依賴于體系結(jié)構(gòu),甚至在相同的處理器家族內(nèi)也是如此。SCI 實(shí)際上是一個(gè)非常有用的函數(shù)調(diào)用多路復(fù)用和多路分解服務(wù)。在 ./linux/kernel 中您可以找到 SCI 的實(shí)現(xiàn),并在 ./linux/arch 中找到依賴于體系結(jié)構(gòu)的部分。 進(jìn)程管理的重點(diǎn)是進(jìn)程的執(zhí)行。在內(nèi)核中,這些進(jìn)程稱為線程,代表了單獨(dú)的處理器虛擬化(線程代碼、數(shù)據(jù)、堆棧和 CPU 寄存器)。在用戶空間,通常使用進(jìn)程 這個(gè)術(shù)語,不過 Linux 實(shí)現(xiàn)并沒有區(qū)分這兩個(gè)概念(進(jìn)程和線程)。內(nèi)核通過 SCI 提供了一個(gè)應(yīng)用程序編程接口(API)來創(chuàng)建一個(gè)新進(jìn)程(fork、exec 或 Portable Operating System Interface [POSIX] 函數(shù)),停止進(jìn)程(kill、exit),并在它們之間進(jìn)行通信和同步(signal 或者 POSIX 機(jī)制)。 進(jìn)程管理還包括處理活動(dòng)進(jìn)程之間共享 CPU 的需求。內(nèi)核實(shí)現(xiàn)了一種新型的調(diào)度算法,不管有多少個(gè)線程在競(jìng)爭(zhēng) CPU,這種算法都可以在固定時(shí)間內(nèi)進(jìn)行操作。這種算法就稱為 O(1) 調(diào)度程序,這個(gè)名字就表示它調(diào)度多個(gè)線程所使用的時(shí)間和調(diào)度一個(gè)線程所使用的時(shí)間是相同的。O(1) 調(diào)度程序也可以支持多處理器(稱為對(duì)稱多處理器或 SMP)。您可以在 ./linux/kernel 中找到進(jìn)程管理的源代碼,在 ./linux/arch 中可以找到依賴于體系結(jié)構(gòu)的源代碼。 內(nèi)核所管理的另外一個(gè)重要資源是內(nèi)存。為了提高效率,如果由硬件管理虛擬內(nèi)存,內(nèi)存是按照所謂的內(nèi)存頁方式進(jìn)行管理的(對(duì)于大部分體系結(jié)構(gòu)來說都是 4KB)。Linux 包括了管理可用內(nèi)存的方式,以及物理和虛擬映射所使用的硬件機(jī)制。不過內(nèi)存管理要管理的可不止 4KB 緩沖區(qū)。Linux 提供了對(duì) 4KB 緩沖區(qū)的抽象,例如 slab 分配器。這種內(nèi)存管理模式使用 4KB 緩沖區(qū)為基數(shù),然后從中分配結(jié)構(gòu),并跟蹤內(nèi)存頁使用情況,比如哪些內(nèi)存頁是滿的,哪些頁面沒有完全使用,哪些頁面為空。這樣就允許該模式根據(jù)系統(tǒng)需要來動(dòng)態(tài)調(diào)整內(nèi)存使用。為了支持多個(gè)用戶使用內(nèi)存,有時(shí)會(huì)出現(xiàn)可用內(nèi)存被消耗光的情況。由于這個(gè)原因,頁面可以移出內(nèi)存并放入磁盤中。這個(gè)過程稱為交換,因?yàn)轫撁鏁?huì)被從內(nèi)存交換到硬盤上。內(nèi)存管理的源代碼可以在 ./linux/mm 中找到。 虛擬文件系統(tǒng)(VFS)是 Linux 內(nèi)核中非常有用的一個(gè)方面,因?yàn)樗鼮槲募到y(tǒng)提供了一個(gè)通用的接口抽象。VFS 在 SCI 和內(nèi)核所支持的文件系統(tǒng)之間提供了一個(gè)交換層。 ▲ Linux文件系統(tǒng)層次結(jié)構(gòu) 網(wǎng)絡(luò)堆棧在設(shè)計(jì)上遵循模擬協(xié)議本身的分層體系結(jié)構(gòu)?;叵胍幌?,Internet Protocol (IP) 是傳輸協(xié)議(通常稱為傳輸控制協(xié)議或 TCP)下面的核心網(wǎng)絡(luò)層協(xié)議。TCP 上面是 socket 層,它是通過 SCI 進(jìn)行調(diào)用的。socket 層是網(wǎng)絡(luò)子系統(tǒng)的標(biāo)準(zhǔn) API,它為各種網(wǎng)絡(luò)協(xié)議提供了一個(gè)用戶接口。從原始幀訪問到 IP 協(xié)議數(shù)據(jù)單元(PDU),再到 TCP 和 User Datagram Protocol (UDP),socket 層提供了一種標(biāo)準(zhǔn)化的方法來管理連接,并在各個(gè)終點(diǎn)之間移動(dòng)數(shù)據(jù)。內(nèi)核中網(wǎng)絡(luò)源代碼可以在 ./linux/net 中找到。 Linux 內(nèi)核中有大量代碼都在設(shè)備驅(qū)動(dòng)程序中,它們能夠運(yùn)轉(zhuǎn)特定的硬件設(shè)備。Linux 源碼樹提供了一個(gè)驅(qū)動(dòng)程序子目錄,這個(gè)目錄又進(jìn)一步劃分為各種支持設(shè)備,例如 Bluetooth、I2C、serial 等。設(shè)備驅(qū)動(dòng)程序的代碼可以在 ./linux/drivers 中找到。 盡管 Linux 很大程度上獨(dú)立于所運(yùn)行的體系結(jié)構(gòu),但是有些元素則必須考慮體系結(jié)構(gòu)才能正常操作并實(shí)現(xiàn)更高效率。./linux/arch 子目錄定義了內(nèi)核源代碼中依賴于體系結(jié)構(gòu)的部分,其中包含了各種特定于體系結(jié)構(gòu)的子目錄(共同組成了 BSP)。對(duì)于一個(gè)典型的桌面系統(tǒng)來說,使用的是 x86 目錄。每個(gè)體系結(jié)構(gòu)子目錄都包含了很多其他子目錄,每個(gè)子目錄都關(guān)注內(nèi)核中的一個(gè)特定方面,例如引導(dǎo)、內(nèi)核、內(nèi)存管理等。這些依賴體系結(jié)構(gòu)的代碼可以在 ./linux/arch 中找到。 如果 Linux 內(nèi)核的可移植性和效率還不夠好,Linux 還提供了其他一些特性,它們無法劃分到上面的分類中。作為一個(gè)生產(chǎn)操作系統(tǒng)和開源軟件,Linux 是測(cè)試新協(xié)議及其增強(qiáng)的良好平臺(tái)。Linux 支持大量網(wǎng)絡(luò)協(xié)議,包括典型的 TCP/IP,以及高速網(wǎng)絡(luò)的擴(kuò)展(大于1 Gigabit Ethernet [GbE] 和 10 GbE)。Linux 也可以支持諸如流控制傳輸協(xié)議(SCTP)之類的協(xié)議,它提供了很多比 TCP 更高級(jí)的特性(是傳輸層協(xié)議的接替者)。 Linux 還是一個(gè)動(dòng)態(tài)內(nèi)核,支持動(dòng)態(tài)添加或刪除軟件組件。被稱為動(dòng)態(tài)可加載內(nèi)核模塊,它們可以在引導(dǎo)時(shí)根據(jù)需要(當(dāng)前特定設(shè)備需要這個(gè)模塊)或在任何時(shí)候由用戶插入。
1)現(xiàn)代CPU通常都實(shí)現(xiàn)了不同的工作模式,以ARM為例:ARM實(shí)現(xiàn)了7種工作模式,不同模式下CPU可以執(zhí)行的指令或者訪問的寄存器不同:
以(2)X86為例:X86實(shí)現(xiàn)了4個(gè)不同級(jí)別的權(quán)限,Ring0—Ring3 ;Ring0下可以執(zhí)行特權(quán)指令,可以訪問IO設(shè)備;Ring3則有很多的限制。 2)所以,Linux從CPU的角度出發(fā),為了保護(hù)內(nèi)核的安全,把系統(tǒng)分成了2部分; 3.用戶空間和內(nèi)核空間是程序執(zhí)行的兩種不同狀態(tài),我們可以通過“系統(tǒng)調(diào)用”和“硬件中斷“來完成用戶空間到內(nèi)核空間的轉(zhuǎn)移。 4.Linux的內(nèi)核結(jié)構(gòu)(注意區(qū)分Linux體系結(jié)構(gòu)和Linux內(nèi)核結(jié)構(gòu)) Linux的這種platform driver機(jī)制和傳統(tǒng)的device_driver機(jī)制相比,一個(gè)十分明顯的優(yōu)勢(shì)在于platform機(jī)制將本身的資源注冊(cè)進(jìn)內(nèi)核,由內(nèi)核統(tǒng)一管理,在驅(qū)動(dòng)程序中使用這些資源時(shí)通過platform_device提供的標(biāo)準(zhǔn)接口進(jìn)行申請(qǐng)并使用。這樣提高了驅(qū)動(dòng)和資源管理的獨(dú)立性,并且擁有較好的可移植性和安全性。下面是SPI驅(qū)動(dòng)層次示意圖,Linux中的SPI總線可理解為SPI控制器引出的總線: 內(nèi)核啟動(dòng)初始化時(shí)的main.c文件中的 kernel_init()→do_basic_setup()→driver_init()→platform_bus_init()→bus_register(&platform_bus_type),注冊(cè)了一條platform總線(虛擬總線,platform_bus)。 設(shè)備注冊(cè)的時(shí)候 Platform_device_register()→platform_device_add()→(pdev→dev.bus = &platform_bus_type)→device_add(),就這樣把設(shè)備給掛到虛擬的總線上。 Platform_driver_register()→driver_register()→bus_add_driver()→driver_attach()→bus_for_each_dev(), 對(duì)在每個(gè)掛在虛擬的platform bus的設(shè)備作__driver_attach()→driver_probe_device(),判斷drv→bus→match()是否執(zhí)行成功,此時(shí)通過指針執(zhí)行platform_match→strncmp(pdev→name , drv→name , BUS_ID_SIZE),如果相符就調(diào)用really_probe(實(shí)際就是執(zhí)行相應(yīng)設(shè)備的platform_driver→probe(platform_device)。)開始真正的探測(cè),如果probe成功,則綁定設(shè)備到該驅(qū)動(dòng)。 從上面可以看出,platform機(jī)制最后還是調(diào)用了bus_register() , device_add() , driver_register()這三個(gè)關(guān)鍵的函數(shù)。 下面看幾個(gè)結(jié)構(gòu)體:
Platform_device結(jié)構(gòu)體描述了一個(gè)platform結(jié)構(gòu)的設(shè)備,在其中包含了一般設(shè)備的結(jié)構(gòu)體struct device dev;設(shè)備的資源結(jié)構(gòu)體struct resource *resource;還有設(shè)備的名字const char *name。(注意,這個(gè)名字一定要和后面platform_driver.driver àname相同,原因會(huì)在后面說明。) 該結(jié)構(gòu)體中最重要的就是resource結(jié)構(gòu),這也是之所以引入platform機(jī)制的原因。 struct resource 其中 flags位表示該資源的類型,start和end分別表示該資源的起始地址和結(jié)束地址(/include/linux/Platform_device.h):
Platform_driver結(jié)構(gòu)體描述了一個(gè)platform結(jié)構(gòu)的驅(qū)動(dòng)。其中除了一些函數(shù)指針外,還有一個(gè)一般驅(qū)動(dòng)的device_driver結(jié)構(gòu)。 上面說的驅(qū)動(dòng)在注冊(cè)的時(shí)候會(huì)調(diào)用函數(shù)bus_for_each_dev(), 對(duì)在每個(gè)掛在虛擬的platform bus的設(shè)備作__driver_attach()→driver_probe_device(),在此函數(shù)中會(huì)對(duì)dev和drv做初步的匹配,調(diào)用的是drv->bus->match所指向的函數(shù)。platform_driver_register函數(shù)中drv->driver.bus = &platform_bus_type,所以drv->bus->match就為platform_bus_type→match,為platform_match函數(shù),該函數(shù)如下: static int platform_match(struct device * dev, struct device_driver * drv) 是比較dev和drv的name,相同則會(huì)進(jìn)入really_probe()函數(shù),從而進(jìn)入自己寫的probe函數(shù)做進(jìn)一步的匹配。所以dev→name和driver→drv→name在初始化時(shí)一定要填一樣的。 不同類型的驅(qū)動(dòng),其match函數(shù)是不一樣的,這個(gè)platform的驅(qū)動(dòng),比較的是dev和drv的名字,還記得usb類驅(qū)動(dòng)里的match嗎?它比較的是Product ID和Vendor ID。 個(gè)人總結(jié)Platform機(jī)制的好處:
因?yàn)長(zhǎng)inux內(nèi)核是單片的,所以它比其他類型的內(nèi)核占用空間最大,復(fù)雜度也最高。這是一個(gè)設(shè)計(jì)特性,在Linux早期引起了相當(dāng)多的爭(zhēng)論,并且仍然帶有一些與單內(nèi)核固有的相同的設(shè)計(jì)缺陷。 為了解決這些缺陷,Linux內(nèi)核開發(fā)人員所做的一件事就是使內(nèi)核模塊可以在運(yùn)行時(shí)加載和卸載,這意味著您可以動(dòng)態(tài)地添加或刪除內(nèi)核的特性。這不僅可以向內(nèi)核添加硬件功能,還可以包括運(yùn)行服務(wù)器進(jìn)程的模塊,比如低級(jí)別虛擬化,但也可以替換整個(gè)內(nèi)核,而不需要在某些情況下重啟計(jì)算機(jī)。 如果Windows已經(jīng)安裝了所有可用的驅(qū)動(dòng)程序,而您只需要打開所需的驅(qū)動(dòng)程序怎么辦?這本質(zhì)上就是內(nèi)核模塊為L(zhǎng)inux所做的。內(nèi)核模塊,也稱為可加載內(nèi)核模塊(LKM),對(duì)于保持內(nèi)核在不消耗所有可用內(nèi)存的情況下與所有硬件一起工作是必不可少的。 模塊通常向基本內(nèi)核添加設(shè)備、文件系統(tǒng)和系統(tǒng)調(diào)用等功能。lkm的文件擴(kuò)展名是.ko,通常存儲(chǔ)在/lib/modules目錄中。由于模塊的特性,您可以通過在啟動(dòng)時(shí)使用menuconfig命令將模塊設(shè)置為load或not load,或者通過編輯/boot/config文件,或者使用modprobe命令動(dòng)態(tài)地加載和卸載模塊,輕松定制內(nèi)核。 第三方和封閉源碼模塊在一些發(fā)行版中是可用的,比如Ubuntu,默認(rèn)情況下可能無法安裝,因?yàn)檫@些模塊的源代碼是不可用的。該軟件的開發(fā)人員(即nVidia、ATI等)不提供源代碼,而是構(gòu)建自己的模塊并編譯所需的.ko文件以便分發(fā)。雖然這些模塊像beer一樣是免費(fèi)的,但它們不像speech那樣是免費(fèi)的,因此不包括在一些發(fā)行版中,因?yàn)榫S護(hù)人員認(rèn)為它通過提供非免費(fèi)軟件“污染”了內(nèi)核。 內(nèi)核并不神奇,但對(duì)于任何正常運(yùn)行的計(jì)算機(jī)來說,它都是必不可少的。Linux內(nèi)核不同于OS X和Windows,因?yàn)樗瑑?nèi)核級(jí)別的驅(qū)動(dòng)程序,并使許多東西“開箱即用”。希望您能對(duì)軟件和硬件如何協(xié)同工作以及啟動(dòng)計(jì)算機(jī)所需的文件有更多的了解。 ![]() |
|