




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、第一章Linux內(nèi)核概述Linux 內(nèi)核是一個(gè)龐大而復(fù)雜的操作系統(tǒng)的核心,不過盡管龐大,但是卻采用子系統(tǒng)和分層的概念很好地進(jìn)行了組織。在本文中,您將探索 Linux 內(nèi)核的總體結(jié)構(gòu),并學(xué)習(xí)一些主要的子系統(tǒng)和核心接口。您還可以通過其他 IBM 文章的鏈接更深入地進(jìn)行學(xué)習(xí)。由于本章的目標(biāo)是對(duì) Linux 內(nèi)核進(jìn)行介紹并探索其體系結(jié)構(gòu)和主要組件,因此首先回顧一下 Linux 的簡(jiǎn)短歷史,然后從較高的層次審視 Linux 內(nèi)核的體系結(jié)構(gòu),最后介紹它的主要子系統(tǒng)。Linux 內(nèi)核具有超過 600 萬行的代碼,因此本文不可能進(jìn)行完整的介紹。請(qǐng)使用指向其他內(nèi)容的鏈接進(jìn)一步學(xué)習(xí)。1.1 linux簡(jiǎn)短歷史盡管
2、 Linux 絕對(duì)是最流行的開源操作系統(tǒng),但是相對(duì)于其他操作系統(tǒng)的漫長(zhǎng)歷史來說,Linux 的歷史非常短暫。在計(jì)算機(jī)出現(xiàn)早期,程序員是使用硬件語言在裸硬件上進(jìn)行開發(fā)的。缺少操作系統(tǒng)就意味著在某個(gè)時(shí)間只有一個(gè)應(yīng)用程序(和一個(gè)用戶)可以使用這些龐大而又昂貴的設(shè)備。早期的操作系統(tǒng)是在 20 世紀(jì) 50 年代開發(fā)的,用來提供簡(jiǎn)單的開發(fā)體驗(yàn)。二十年后,Andrew Tanenbaum 創(chuàng)建了一個(gè)微內(nèi)核版本的 UNIX®,名為 MINIX(代表 minimal UNIX),它可以在小型的個(gè)人計(jì)算機(jī)上運(yùn)行。這個(gè)開源操作系統(tǒng)在 20 世紀(jì) 90 年代激發(fā)了 Linus Torvalds 開發(fā) Lin
3、ux 的靈感(請(qǐng)參看圖 1 所示)。Linux 快速從一個(gè)個(gè)人項(xiàng)目進(jìn)化成為一個(gè)全球數(shù)千人參與的開發(fā)項(xiàng)目。對(duì)于 Linux 來說,最為重要的決策之一是采用 GPL(GNU General Public License)。在 GPL 保護(hù)之下,Linux 內(nèi)核可以防止商業(yè)使用,并且它還從 GNU 項(xiàng)目(Richard Stallman 開發(fā),其源代碼要比 Linux 內(nèi)核大得多)的用戶空間開發(fā)受益。這允許使用一些非常有用的應(yīng)用程序,例如 GCC(GNU Compiler Collection)和各種 shell 支持。1.2linux內(nèi)核組成1.2.1linux內(nèi)核簡(jiǎn)介現(xiàn)在讓我們從一個(gè)比較高的高度
4、來審視一下 GNU/Linux 操作系統(tǒng)的體系結(jié)構(gòu)。您可以從兩個(gè)層次上來考慮操作系統(tǒng),如圖 2 所示。最上面是用戶(或應(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 和
5、 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)核屬性在討論大型而復(fù)雜的系統(tǒng)的體系結(jié)構(gòu)時(shí),可以從很多角度來審視系統(tǒng)。體系結(jié)構(gòu)分析的一個(gè)目標(biāo)是提供一種方法更好地理解源代碼,這正是本文的目的。Linux 內(nèi)核實(shí)現(xiàn)了很多重要的體系結(jié)構(gòu)屬性。在或高或低的層次上,內(nèi)核被劃分為多個(gè)子系統(tǒng)。Linux 也可以看作是一個(gè)整體,因?yàn)樗鼤?huì)將所
6、有這些基本服務(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)核的 uC
7、linux 移植提供了對(duì)非 MMU 的支持。Linux內(nèi)核子系統(tǒng)現(xiàn)在使用圖 3 中的分類說明 Linux 內(nèi)核的主要組件。系統(tǒng)調(diào)用接口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)程管理進(jìn)程管理的重點(diǎn)是進(jìn)程的執(zhí)行。在內(nèi)核中,這些進(jìn)程稱為線程,代表了單獨(dú)的處理器虛擬化(線程代碼、數(shù)據(jù)、堆棧和 CPU 寄存器)。在用戶空間,通
8、常使用進(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)度
9、一個(gè)線程所使用的時(shí)間是相同的。 O(1) 調(diào)度程序也可以支持多處理器(稱為對(duì)稱多處理器或 SMP)。您可以在 ./linux/kernel 中找到進(jìn)程管理的源代碼,在 ./linux/arch 中可以找到依賴于體系結(jié)構(gòu)的源代碼。內(nèi)存管理內(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)存管理模式使用 4K
10、B 緩沖區(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)虛擬文件系統(tǒng)(VFS)是 Linux 內(nèi)核中非常有用的一個(gè)方面,因?yàn)樗鼮槲募到y(tǒng)提供了一個(gè)通用的接口抽象。VFS 在 SCI 和內(nèi)核所支持的文件系統(tǒng)之間提供了一個(gè)交換層(請(qǐng)參看圖 4)。在 VFS 上面,是對(duì)諸如 o
11、pen、close、read 和 write 之類的函數(shù)的一個(gè)通用 API 抽象。在 VFS 下面是文件系統(tǒng)抽象,它定義了上層函數(shù)的實(shí)現(xiàn)方式。它們是給定文件系統(tǒng)(超過 50 個(gè))的插件。文件系統(tǒng)的源代碼可以在 ./linux/fs 中找到。文件系統(tǒng)層之下是緩沖區(qū)緩存,它為文件系統(tǒng)層提供了一個(gè)通用函數(shù)集(與具體文件系統(tǒng)無關(guān))。這個(gè)緩存層通過將數(shù)據(jù)保留一段時(shí)間(或者隨即預(yù)先讀取數(shù)據(jù)以便在需要是就可用)優(yōu)化了對(duì)物理設(shè)備的訪問。緩沖區(qū)緩存之下是設(shè)備驅(qū)動(dòng)程序,它實(shí)現(xiàn)了特定物理設(shè)備的接口。網(wǎng)絡(luò)堆棧網(wǎng)絡(luò)堆棧在設(shè)計(jì)上遵循模擬協(xié)議本身的分層體系結(jié)構(gòu)?;叵胍幌?,Internet Protocol (IP) 是傳輸
12、協(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 中找到。設(shè)備驅(qū)動(dòng)程序Linux 內(nèi)核中有大量代碼都在設(shè)備驅(qū)動(dòng)程序中,它們能夠運(yùn)轉(zhuǎn)特定的硬件設(shè)備。Linux 源碼樹提供了一個(gè)驅(qū)動(dòng)程序子目錄,這個(gè)目錄又
13、進(jìn)一步劃分為各種支持設(shè)備,例如 Bluetooth、I2C、serial 等。設(shè)備驅(qū)動(dòng)程序的代碼可以在 ./linux/drivers 中找到。依賴體系結(jié)構(gòu)的代碼盡管 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)來說,使用的是 i386 目錄。每個(gè)體系結(jié)構(gòu)子目錄都包含了很多其他子目錄,每個(gè)子目錄都關(guān)注內(nèi)核中的一個(gè)特定方面,例如引導(dǎo)、內(nèi)核、內(nèi)存管理等。這些依賴體系結(jié)構(gòu)的代碼可以在 ./
14、linux/arch 中找到。1.3Linux內(nèi)核的其他特性如果 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)核模
15、塊,它們可以在引導(dǎo)時(shí)根據(jù)需要(當(dāng)前特定設(shè)備需要這個(gè)模塊)或在任何時(shí)候由用戶插入。Linux 最新的一個(gè)增強(qiáng)是可以用作其他操作系統(tǒng)的操作系統(tǒng)(稱為系統(tǒng)管理程序)。最近,對(duì)內(nèi)核進(jìn)行了修改,稱為基于內(nèi)核的虛擬機(jī)(KVM)。這個(gè)修改為用戶空間啟用了一個(gè)新的接口,它可以允許其他操作系統(tǒng)在啟用了 KVM 的內(nèi)核之上運(yùn)行。除了運(yùn)行 Linux 的其他實(shí)例之外, Microsoft® Windows® 也可以進(jìn)行虛擬化。惟一的限制是底層處理器必須支持新的虛擬化指令。第二章 linux內(nèi)核模塊開發(fā)Linux設(shè)備驅(qū)動(dòng)會(huì)以內(nèi)核模塊的形式出現(xiàn),因此學(xué)會(huì)編寫Linux內(nèi)核模塊編程是學(xué)習(xí)linux設(shè)備
16、驅(qū)動(dòng)的先決條件。1.1linux內(nèi)核模塊簡(jiǎn)介L(zhǎng)inux內(nèi)核的整體結(jié)構(gòu)非常龐大,其包含的組件非常多。我們?nèi)绾伟研枰牟糠侄及趦?nèi)核中呢?把需要的功能都編譯到linux內(nèi)核。.以模塊方式擴(kuò)展內(nèi)核功能。為了使學(xué)生對(duì)模塊建立初步的感性認(rèn)識(shí),我們先來看一個(gè)最簡(jiǎn)單的內(nèi)核模塊”hello world”,代碼如下:#include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) printk("hell
17、o worldn”); return 0; static void hello_exit(void) printk(<1> "hello module exitn "); module_init(hello_init); module_exit(hello_exit); MODULE_AUTHOR("zky");MODULE_DESCRIPTION("A simple hello Module ");MODULE_VERSION("V1.0");這個(gè)最簡(jiǎn)單的內(nèi)核模塊只包含內(nèi)核加載函數(shù)、卸載函數(shù)和對(duì)Dua
18、l BSD/GPL許可權(quán)限的聲明以及一些描述信息。編譯會(huì)產(chǎn)生hello.ko目標(biāo)文件,通過”insmod ./hello.ko”命令可以加載它,通過”rmmod hello”命令可以卸載它,加載時(shí)輸出”hello world”, 卸載時(shí)輸出”hello module exit”,查看輸出信息可通過dmesg命令。內(nèi)核模塊中用于輸出的函數(shù)式內(nèi)核空間的printk()而非用戶空間的printf(),printk()的用法和printf()相似,但前者可定義輸出級(jí)別。Printk()可作為一種最基本的內(nèi)核調(diào)試手段。printk有8個(gè)loglevel,定義在<linux/kernel.h>
19、中:#define KERN_EMERG "<0>" /* system is unusable */#define KERN_ALERT "<1>" /* ac
20、tion must be taken immediately*/#define KERN_CRIT "<2>" /* critical conditions */#define KERN_ERR "<3>" /* error
21、 conditions */#define KERN_WARNING "<4>" /* warning conditions */#define KERN_NOTICE &
22、quot;<5>" /* normal but significant condition */#define KERN_INFO "<6>" /* informational */#define
23、 KERN_DEBUG "<7>" /* debug-level messages */未指定優(yōu)先級(jí)的默認(rèn)級(jí)別定義在/kernel/printk.c中:#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */當(dāng)優(yōu)先級(jí)的值小于console_loglevel這個(gè)整數(shù)變量的值,信息才能顯示出來。而console_loglevel的初始值DEFAULT_CONSOLE_LOGLEVE
24、L也定義在/kernel/printk.c中: #define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */在linux系統(tǒng)中,使用lsmod命令可以獲得系統(tǒng)中加載了的所有模塊以及模塊間的依賴關(guān)系,例如:Lsmod命令實(shí)際上讀取并分析/proc/modules文件,與上述lsmod命令結(jié)果對(duì)應(yīng)的/proc/modules文件如下:內(nèi)核中已加載模塊的信息也存在于/sys/module目錄下,加載hello.ko后,內(nèi)核中將包含/sys/module/hello目錄,該目錄下又包含一個(gè)refcnt文件和
25、一個(gè)sections目錄,在/sys/module/hello目錄下運(yùn)行”tree -a”得到如下目錄樹:Modprobe命令比insmod命令要強(qiáng)大,它在加載某模塊時(shí)會(huì)同時(shí)加載該模塊所依賴的其他模塊。使用modprob命令加載的模塊若以”modprobe r filename”的方式卸載將同時(shí)其他依賴的模塊。使用modinfo <模塊名>命令可以獲得模塊的信息,包括模塊的作者、模塊的說明、模塊所支持的參數(shù)以及vermagic,如下所示:1.2模塊的編譯接下來我們來簡(jiǎn)單看看模塊是如何構(gòu)造的,模塊的構(gòu)造和用戶空間應(yīng)用程序的構(gòu)造過程有很大的不同。實(shí)際上對(duì)本章先前給出的”hello wo
26、rld”示例來說,下面一行就足以了:Obj-m := hello.o如果大家熟悉make但對(duì)2.6內(nèi)核構(gòu)造系統(tǒng)還不熟悉的話,則可能會(huì)對(duì)此makefile的工作方式感到疑惑。畢竟上面這行并不是makefile文件的常見格式。問題的答案就是內(nèi)核的構(gòu)造系統(tǒng)處理了其余的問題。上面的賦值語句說明了又一個(gè)模塊需要從目標(biāo)文件hello.o中構(gòu)造,而從目標(biāo)文件中構(gòu)造的模塊名稱為hello.ko。如果我們要構(gòu)造的模塊名稱為module.ko,并有兩個(gè)源文件生成(比如file1.c和file2.c),則正確的makefile可如下編寫:Obj-m := module.oModule-objs :=file1.o
27、file2.o為了讓上面這種類型的makefile文件正常工作,必須在大的內(nèi)核構(gòu)造系統(tǒng)環(huán)境中調(diào)用它們。如果內(nèi)核源碼保存在/kernel-2.6目錄中,則用來構(gòu)造模塊的make命令應(yīng)該為(在包含模塊源代碼和makefile的目錄中鍵入):Make C /kernel-2.6 M=pwd modules上述命令首先改變目錄到-C選項(xiàng)指定的位置(既內(nèi)核源代碼目錄),其中保存有內(nèi)核的頂層makefile文件,M=選項(xiàng)讓該makefile在構(gòu)造modules目標(biāo)之前返回到模塊源代碼目錄。然后modules目標(biāo)指向obj-m變量中設(shè)定的模塊;在上面的例子中,我們將該變量設(shè)置成了module.o。上面這樣的
28、make命令還是有些煩人,因此內(nèi)核開發(fā)者又開發(fā)了一種makefile方法,這種方法將使得內(nèi)核樹之外的模塊構(gòu)造變得更加容易,其技巧就是用下面的方法來編寫makefile:#如果已定義KERNELRELEASE,則說明是從內(nèi)核構(gòu)造系統(tǒng)調(diào)用的,因此可利用其內(nèi)建語句。Ifneq ($(KERNELRELEASE),)obj-m :=hello.o#否則,是直接從命令行調(diào)用的,這時(shí)要調(diào)用內(nèi)核構(gòu)造系統(tǒng)ElseKERNELDIR ?=/KERNEL-2.6PWD := $(shell pwd)Default:$(MAKE) C $(KERNELDIR) M=$(PWD) modulesendif需要注意的是
29、上面的makefile文件并不完整;一個(gè)真正的makefile文件應(yīng)該包含通常用來清除無用文件的目標(biāo),安裝模塊的目標(biāo)等等。1.3linux內(nèi)核模塊的結(jié)構(gòu)一個(gè)linux內(nèi)核模塊主要由以下幾個(gè)部分組成:l 模塊加載函數(shù)(必須)當(dāng)通過insmo或modprobe命令加載內(nèi)核模塊時(shí),模塊的加載函數(shù)會(huì)自動(dòng)被內(nèi)核執(zhí)行,完成本模塊相關(guān)初始化工作。l 模塊卸載函數(shù)(必須)當(dāng)通過rmmod命令卸載模塊時(shí),模塊的卸載函數(shù)會(huì)自動(dòng)被內(nèi)核執(zhí)行,完成與模塊加載函數(shù)相反的功能。l 模塊許可證聲明(必須)模塊許可證(LICENCE)聲明描述內(nèi)核模塊的許可權(quán)限,如果不聲明LICENCE,模塊被加載時(shí)將收到內(nèi)核被污染的警告。在2
30、.6內(nèi)核中,可接受得LICENSE包括“GPL”、“GPL v2”、“GPL and additional right”、“Dual BSD/GPL”、“Dual MPL/GPL”和“Proprietary”。大多數(shù)情況下,內(nèi)核模塊應(yīng)遵循GPL兼容許可權(quán)。Linux2.6內(nèi)核模塊最常見的是以MODULE_LICENSE(“Dual BSD/GPL”)語句聲明模塊采用BSD/GPL雙LICENSE。l 模塊參數(shù)(可選)模塊參數(shù)是模塊被加載的時(shí)候可以被傳遞給他的值,它本身對(duì)應(yīng)模塊內(nèi)部的全局變量。l 模塊導(dǎo)出符號(hào)(可選)內(nèi)核模塊可以導(dǎo)出符號(hào)(symbol,對(duì)應(yīng)于函數(shù)或變量),這樣其他模塊可以使用本模
31、塊中的變量或函數(shù)。l 模塊作者等信息聲明(可選)。1.4模塊加載函數(shù)Linux內(nèi)核模塊加載函數(shù)一般以_init標(biāo)識(shí)聲明,典型的模塊加載函數(shù)的形式如下:Static int _init initialization_function(void) /初始化代碼Module_init(initialization_function);模塊加載函數(shù)必須以“module_init(函數(shù)名)”的形式指定。它返回整形值,若初始化成功,應(yīng)返回0。而在初始化失敗時(shí)。應(yīng)該返回錯(cuò)誤編碼。在linux內(nèi)核里,錯(cuò)誤編碼是一個(gè)負(fù)值,在<linux/errno.h>中定義,包含-ENODEV、-ENOMEM之類
32、的符號(hào)值。返回相應(yīng)的錯(cuò)誤編碼是種非常好的習(xí)慣,因?yàn)橹挥羞@樣,用戶程序才可以利用perror等方法把它們轉(zhuǎn)換成有意義的錯(cuò)誤信息字符串。在linux2.6內(nèi)核中,所有標(biāo)識(shí)為_init的函數(shù)在連接的時(shí)候都會(huì)放在.init.text(這是module_init宏在目標(biāo)代碼中增加的一個(gè)特殊區(qū)段,用于說明內(nèi)核初始化函數(shù)的所在位置)這個(gè)區(qū)段中,此外,所有的_init函數(shù)在區(qū)段.initcall.init中還保存著一份函數(shù)指針,在初始化時(shí)內(nèi)核會(huì)通過這些函數(shù)指針調(diào)用這些_init函數(shù),并在初始化完成后釋放init區(qū)段(包括.init.text和.initcall.init等)。所以大家應(yīng)注意不要在結(jié)束初始化后仍
33、要使用的函數(shù)上使用這個(gè)標(biāo)記。1.5模塊卸載函數(shù)Linux內(nèi)核卸載模塊函數(shù)一般以_exit標(biāo)識(shí)聲明,典型的模塊卸載函數(shù)的形式如下:Satic void _exit cleanup_function(void) /釋放代碼Module_exit(cleanup_function);模塊卸載函數(shù)在模塊卸載時(shí)被調(diào)用,不返回任何值,必須以”module_exit(函數(shù)名)”的形式來指定。一般來說,模塊卸載函數(shù)完成與模塊加載函數(shù)相反的功能:l 如果模塊加載函數(shù)注冊(cè)了 XXX模塊,則模塊卸載函數(shù)應(yīng)注銷XXX。l 若模塊加載函數(shù)動(dòng)體申請(qǐng)了內(nèi)存,則模塊卸載函數(shù)應(yīng)釋放該內(nèi)存。l 若模塊加載函數(shù)申請(qǐng)了硬件資源,則模
34、塊卸載函數(shù)應(yīng)釋放這些硬件資源。l 若模塊加載函數(shù)開啟了硬件,則模塊卸載函數(shù)應(yīng)關(guān)閉硬件。和_init一樣_exit也可以使對(duì)應(yīng)函數(shù)在運(yùn)行完成后自動(dòng)回收內(nèi)存。1.6模塊參數(shù)我們可以用”module_param(參數(shù)名,參數(shù)類型,參數(shù)讀/寫權(quán)限)”為模塊定義一個(gè)參數(shù),例如下列代碼定義了一個(gè)整形參數(shù)和一個(gè)字符指針參數(shù):Static char *book_name = “l(fā)inux 模塊”;Satic int num = 4000;Module_param(num, int, S_IRUGO);Module_param(book_name, charp, S_IRUGO);在裝載內(nèi)核模塊時(shí),用戶可以向模
35、塊傳遞參數(shù),形式為”insmod (或 modprobe) 模塊名 參數(shù)名=參數(shù)值”,如果不傳遞,參數(shù)將使用模塊內(nèi)定義的默認(rèn)值。參數(shù)類型可以是byte、short、ushort、int、uint、long、ulong、charp、bool、或invbool(布爾的反),在模塊被編譯時(shí)會(huì)將module_param中聲明的類型與變量定義的類型進(jìn)行比較,判斷是否一致。模塊被加載后,在/sys/module/目錄下將出現(xiàn)以此模塊命名的目錄。當(dāng)“參數(shù)讀/寫權(quán)限”為0時(shí),表示此參數(shù)不存在sysfs文件系統(tǒng)下對(duì)應(yīng)的文件節(jié)點(diǎn),如果此模塊存在“參數(shù)讀/寫權(quán)限”不為0的命令行參數(shù),在此模塊的目錄下還將出現(xiàn)para
36、meters目錄,包含一系列以參數(shù)名命名的文件節(jié)點(diǎn),這些文件的權(quán)限值就是傳入module_param()的“參數(shù)讀/寫權(quán)限”,而文件的內(nèi)容為參數(shù)的值。現(xiàn)在我們定義一個(gè)包含兩個(gè)參數(shù)的模塊,并觀察模塊加載時(shí)被傳遞參數(shù)和不傳遞參數(shù)時(shí)的輸出。#include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static char *book_name = "dissecting Linux Device Driver" static int
37、num = 4000; static int book_init(void) printk(KERN_INFO " book name:%sn",book_name); printk(KERN_INFO " book num:%dn",num); return 0; static void book_exit(void) printk(KERN_INFO " Book module exitn "); module_init(book_init); module_exit(book_exit); module_param(num, i
38、nt, S_IRUGO); module_param(book_name, charp, S_IRUGO|S_IWUGO); MODULE_AUTHOR("zky");MODULE_DESCRIPTION("A simple Module for testing module params");MODULE_VERSION("V1.0");對(duì)上述模塊運(yùn)新“insmod book.ko”命令加載,相應(yīng)輸出都為模塊內(nèi)的默認(rèn)值,通過查看“/var/log/messages”日志文件可以看到內(nèi)核的輸出,如下所示:當(dāng)用戶運(yùn)行“insmod bo
39、ok.ko book_name=mybook num=3000”命令時(shí),輸出的是用戶傳遞的參數(shù),如下所示:1.7導(dǎo)出符號(hào)Linux2.6的/proc/kallsyms文件對(duì)應(yīng)著內(nèi)核符號(hào)表,它記錄了符號(hào)以及符號(hào)所在的內(nèi)存地址。模塊可使用如下宏導(dǎo)出符號(hào)到內(nèi)核符號(hào)表:EXPORT_SYMBOL(符號(hào)名);EXPORT_SYMBOL_GPL(符號(hào)名);導(dǎo)出的符號(hào)將可以被其他模塊使用,使用前聲明一下即可。EXPORT_SYMBOL_GPL()只適用于包含GPL許可權(quán)的模塊。如下代碼給出了一個(gè)導(dǎo)出整數(shù)加、減運(yùn)算函數(shù)符號(hào)的內(nèi)核模塊的例子。#include <linux/init.h> #incl
40、ude <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); int add_integar(int a,int b) return a+b; int sub_integar(int a,int b) return a-b; EXPORT_SYMBOL(add_integar);EXPORT_SYMBOL(sub_integar);從/proc/kallsyms文件中找出add_integar、sub_integar相關(guān)信息:1.8模塊聲明與描述在linux模塊中,我們可以使用MODULE_AUTHOR、MODULE_
41、DESCRIPTION、MODULE_VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS分別聲明模塊的作者、描述、版本、設(shè)備表和別名,例如:MODULE_AUTHOR(author);MODULE_DESCRIPTION(description);MODULE_VERSION(version);MODULE_DEVICE_TABLE(device table);MODULE_ALIAS(alternate_name);第三章linux字符設(shè)備驅(qū)動(dòng)開發(fā)Linux下的設(shè)備驅(qū)動(dòng)程序被組織為一組完成不同任務(wù)的函數(shù)的集合,通過這些函數(shù)使得linux的設(shè)備操作猶如文件一般。在
42、應(yīng)用程序看來,硬件設(shè)備只是一個(gè)設(shè)備文件,應(yīng)用程序可以象操作普通文件一樣對(duì)硬件設(shè)備進(jìn)行操作,如open()、close()、read()、write() 等。Linux主要將設(shè)備分為二類:字符設(shè)備和塊設(shè)備。字符設(shè)備是指設(shè)備發(fā)送和接收數(shù)據(jù)以字符的形式進(jìn)行;而塊設(shè)備則以整個(gè)數(shù)據(jù)緩沖區(qū)的形式進(jìn)行。字符設(shè)備的驅(qū)動(dòng)相對(duì)比較簡(jiǎn)單。3.1 Linux字符設(shè)備驅(qū)動(dòng)結(jié)構(gòu)3.1.1 cdev結(jié)構(gòu)體在Linux2.6內(nèi)核中一個(gè)字符設(shè)備用cdev結(jié)構(gòu)來描述,其定義如下:struct cdev struct kobject kobj;&
43、#160; struct module *owner; /所屬模塊 const struct file_operations *ops; /文件操作結(jié)構(gòu),在寫驅(qū)動(dòng)時(shí),其結(jié)構(gòu)體內(nèi)的大部分函數(shù)要被實(shí)現(xiàn)
44、 struct list_head list; dev_t dev; /設(shè)備號(hào),int 類型,高12位為主設(shè)備號(hào),低20位為次設(shè)備號(hào) unsigned int count;下面一組函數(shù)用來對(duì)cdev結(jié)構(gòu)體進(jìn)行操作:void cdev_init(struct cdev *, const s
45、truct file_operations *); /初始化,建立cdev和file_operation 之間的連接。 struct cdev *cdev_alloc(void); /動(dòng)態(tài)申請(qǐng)一個(gè)cdev內(nèi)存。void cdev_put(struct cdev *p); /釋放。int cdev_add(struct cdev *, dev_t, unsigned); /注冊(cè)設(shè)備,通常發(fā)生在驅(qū)動(dòng)模塊的加載函數(shù)中。 void cdev_del(struct cdev *);/注銷設(shè)備,通常發(fā)生在驅(qū)動(dòng)模塊的卸載函數(shù)中。3.1.2
46、分配和釋放設(shè)備號(hào)主設(shè)備號(hào)表示設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)程序;次設(shè)備號(hào)由內(nèi)核使用,用于正確確定設(shè)備文件所指的設(shè)備。內(nèi)核用dev_t類型(<linux/types.h>)來保存設(shè)備編號(hào),dev_t是一個(gè)32位的數(shù),12位表示主設(shè)備號(hào),20為表示次設(shè)備號(hào)。在實(shí)際使用中,是通過<linux/kdev_t.h>中定義的宏來轉(zhuǎn)換格式。 (dev_t)->主設(shè)備號(hào)、次設(shè)備號(hào) MAJOR(dev_t dev) MINOR(dev_t dev) 主設(shè)備號(hào)、次設(shè)備號(hào)->(dev_t) MKDEV(int major,int minor)
47、160;建立一個(gè)字符設(shè)備之前,驅(qū)動(dòng)程序首先要做的事情就是獲得設(shè)備編號(hào)。其這主要函數(shù)在<linux/fs.h>中聲明:int register_chrdev_region(dev_t first, unsigned int count,char *name); /指定設(shè)備編號(hào)int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name); /動(dòng)態(tài)生成設(shè)備編號(hào)void unregister_chrdev_region(d
48、ev_t first, unsigned int count); /釋放設(shè)備編號(hào)分配之設(shè)備號(hào)的最佳方式是:默認(rèn)采用動(dòng)態(tài)分配,同時(shí)保留在加載甚至是編譯時(shí)指定主設(shè)備號(hào)的余地。3.1.3 file_operations結(jié)構(gòu)體file_operation就是把系統(tǒng)調(diào)用和驅(qū)動(dòng)程序關(guān)聯(lián)起來的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)。這個(gè)結(jié)構(gòu)的每一個(gè)成員都對(duì)應(yīng)著一個(gè)系統(tǒng)調(diào)用。讀取file_operation中相應(yīng)的函數(shù)指針,接著把控制權(quán)轉(zhuǎn)交給函數(shù),從而完成了Linux設(shè)備驅(qū)動(dòng)程序的工作。 在系統(tǒng)內(nèi)部,I/O設(shè)備的存取操作通過特定的入口點(diǎn)來進(jìn)行,而這組特定的入口點(diǎn)恰恰是由設(shè)備驅(qū)動(dòng)程序提供
49、的。通常這組設(shè)備驅(qū)動(dòng)程序接口是由結(jié)構(gòu)file_operations結(jié)構(gòu)體向系統(tǒng)說明的,它定義在include/linux/fs.h中。struct file_operations struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char _user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, char _user *, size_t, loff_t);ssize_t (*wr
50、ite) (struct file *, const char _user *, size_t, loff_t *);ssize_t (*aio_write) (struct kiocb *, const char _user *, size_t, loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigne
51、d int, unsigned long);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int
52、 (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*readv) (struct file *, const struct iovec *,
53、unsigned long, loff_t *);ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(stru
54、ct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*dir_notify)(struct file *filp, unsigned long arg);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int
55、);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);傳統(tǒng)上, 一個(gè) file_operation 結(jié)構(gòu)或者其一個(gè)指針稱為 fops( 或者它的一些變體). 結(jié)構(gòu)中的每個(gè)成員必須指向驅(qū)動(dòng)中的函數(shù), 這些函數(shù)實(shí)現(xiàn)一個(gè)特別的操作, 或者對(duì)于不支持的操作留置為 NULL.一般情況下,進(jìn)行設(shè)備驅(qū)動(dòng)程序的設(shè)計(jì)只是比較注重下面的幾個(gè)方法:struct file_operations *_ops= .owner = THIS_MODULE,
56、60;.llseek = *_llseek, .read = *_read, .write = *_write, .ioctl = *_ioctl, .open = *_open, .release = *_release, ;3.2 虛擬字符設(shè)備驅(qū)動(dòng)3.2.1 加載與卸載設(shè)備驅(qū)動(dòng)注冊(cè)一個(gè)獨(dú)立的cdev設(shè)備的基本過程如下:1、為struct cdev 分配空間(如果已經(jīng)將struct cdev 嵌入到自己的設(shè)備的特定結(jié)構(gòu)體中,并分配了空間,這步略過!)struct cdev *my_c
57、dev = cdev_alloc();2、初始化struct cdev void cdev_init(struct cdev *cdev, const struct file_operations *fops) 3、初始化cdev.ownercdev.owner = THIS_MODULE;4、注冊(cè)cdev,cdev_add(struct cdev *, dev_t ,unsigned)向內(nèi)核注冊(cè)一個(gè)字符設(shè)備,通知內(nèi)核struct cdev的信息(在執(zhí)行這步之前必須確定你對(duì)struct cdev的以上設(shè)置已經(jīng)完成?。南到y(tǒng)中移除一個(gè)字符設(shè)備:void cdev_del(struct cdev
58、*p)以下是scull中的初始化代碼(之前已經(jīng)為struct scull_dev 分配了空間):/* * Set up the char_dev structure for this device. */static void scull_setup_cdev(struct scull_dev *dev, int index) int err, devno = MKDEV(scull_major, scull_minor + index);
59、60;cdev_init(&dev->cdev, &scull_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &scull_fops; /這句可以省略,在cdev_init中已經(jīng)做過 err = cdev_add (&dev->cdev, devno, 1); /* Fail gracefully if need be 這步值得注意*/ if (err) print
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 姨媽巾自助販賣機(jī)創(chuàng)業(yè)計(jì)劃
- 云南民族大學(xué)附中2025屆高三最后一?;瘜W(xué)試題含解析
- 學(xué)校食堂操作流程培訓(xùn)
- 湖南省邵東市振華中學(xué)2024-2025學(xué)年高一下學(xué)期3月階段性檢測(cè)地理試題(含答案)
- 2025年江西省中考化學(xué)模擬預(yù)測(cè)卷(5)(含答案)
- 北京市師范大學(xué)附屬中學(xué)2025屆高三第三次模擬考試化學(xué)試卷含解析
- 2025年硅-鋁絲材項(xiàng)目發(fā)展計(jì)劃
- 吉林省長(zhǎng)春市外國(guó)語學(xué)校2025屆高考化學(xué)全真模擬密押卷含解析
- 2025年實(shí)驗(yàn)儀器裝置項(xiàng)目建議書
- 2025年茶及飲料原料項(xiàng)目建議書
- (一模)2025年廣東省高三高考模擬測(cè)試 (一) 英語試卷(含官方答案及詳解)
- 退役軍人無人機(jī)培訓(xùn)宣傳
- 退役軍人保密教育
- DB44∕T 370-2006 東風(fēng)螺養(yǎng)殖技術(shù)規(guī)范繁殖與苗種培育技術(shù)
- 7.1我國(guó)法治建設(shè)的歷程 課件高中政治統(tǒng)編版必修三政治與法治
- 2025年仲裁法考試試題及答案
- 2025年電梯修理作業(yè)證理論考試練習(xí)題(100題)含答案
- 交通運(yùn)輸部南海航海保障中心推遲公開招聘筆試高頻重點(diǎn)模擬試卷提升(共500題附帶答案詳解)
- T-ZJWL 001-2024 大宗商品供應(yīng)鏈金融動(dòng)產(chǎn)質(zhì)押監(jiān)管倉儲(chǔ)服務(wù)規(guī)范
- Unit 3 Faster,highter,stronger Understanding Ideas The road to success群文閱讀說課稿 2024-2025學(xué)年高中英語人教版選擇性必修第一冊(cè)
- 交通運(yùn)輸行業(yè)股權(quán)分配方案
評(píng)論
0/150
提交評(píng)論