天天看點

Linux 驅動開發之核心子產品開發 (一)—— 核心子產品機制基礎

一、核心子產品的概念

1、什麼是子產品?

       核心子產品是一些可以讓作業系統核心在需要時載入和執行的代碼,同時在不需要的時候可以解除安裝。這是一個好的功能,擴充了作業系統的核心功能,卻不需要重新啟動系統,是一種動态加載的技術。

特點:動态加載,随時載入,随時解除安裝,擴充功能

2、核心子產品的加載作用

      核心子產品隻是向linux核心預先注冊自己,以便于将來的請求使用;由目标代碼組成,沒有形成完整的可執行程式。隻是告訴核心,它有了新增的功能,而并不馬上使用(執行),隻是等待應用程式的調用;而應用程式在加載後就開始執行。

3、核心子產品所用函數

      核心子產品代碼編寫沒有外部的函數庫可以用,隻能使用核心導出的函數。而應用程式習慣于使用外部的庫函數,在編譯時将程式與庫函數連結在一起。例如對比printf( ) and printk( )。

      是以驅動所用頭檔案均來自核心源代碼,應用程式所用頭檔案來自庫函數。

4、核心子產品代碼運作空間

      核心代碼運作在核心空間,而應用程式在使用者空間。應用程式的運作會形成新的程序,而核心子產品一般不會。每當應用程式執行系統調用時,linux執行模式從使用者空間切換到核心空間。

二、linux核心子產品的架構

        最少兩個入口點

*子產品加載函數   module_init()

*子產品解除安裝函數   module_exit()

      module_init() and module_exit()兩個宏定義聲明子產品的加載函數和解除安裝函數,這個定義在linux3.14/include/linux/init.h中。内容為:

#define module_init(x) __initcall(x)

//在核心啟動或子產品加載時執行

#define module_exit(x) __exitcall(x)

//在子產品解除安裝時執行

每一個子產品隻能有一個module_init 和一個module_exit。

下面我們對比一下應用程式,看看應用程式與核心子產品的差別:

#include <stdio.h>

int main() {      printf("Hello World!\n");

      return 0; }

#include <linux/module.h>  //所有核心子產品都必須包含這個頭檔案 #include<linux/kernel.h>    //使用核心資訊優先級時要包含這個

#include<linux/init.h>         //一些初始化的函數如module_init()

static int hello_init(void) {     printk("hello_init"); } static void hello_exit(void) {     printk("hello_exit \n"); }

MODULE_LICENSE("GPL");  //子產品許可聲明

module_init(hello_init); 加載時候調用該函數insmod module_exit(hello_exit);解除安裝時候 rmmod

應用程式和核心子產品對比總結如下

應用程式 子產品
入口函數 main 加載時候調用hello_init
函數的調用 /lib 所有函數可以直接調用
運作空間 使用者空間 核心空間
資源的釋放 系統自動釋放 kill -9 pid 手動釋放 手動釋放

三、Linux 核心子產品的編譯和加載       其實核心的編譯在前面就已經講過了,現在回顧一下: linux3.14核心的Makefile分為5個組成部分: Makefile     最頂層的Makefile  .config        核心的目前配置檔案,編譯時成為定層Makefile的一部分 arch/$(ARCH)/Makefile    與體系結構相關的Makefile

s/ Makefile.*      一些Makefile的通用規則

kbuild Makefile           各級目錄下的大概約500個檔案,編譯時根據上層Makefile傳下來的宏定義和其他編譯規則,将源代碼編譯成子產品或者編入核心

      頂層的Makefile檔案讀取 .config檔案的内容,并總體上負責build核心和子產品。Arch Makefile則提供補充體系結構相關的資訊。 s目錄下的Makefile檔案包含了所有用來根據kbuild Makefile 建構核心所需的定義和規則。(其中.config的内容是在make menuconfig的時候,通過Kconfig檔案配置的結果,上面已經說過)      對于大部分核心子產品或裝置驅動的開發者和使用者來說,最常接觸到的就是各層目錄下 基于kbuild架構的kbuild Makefile檔案。主要部分有:

1、目标定義       目标定義就是用來定義哪些内容要做為子產品編譯,哪些要編譯連結進核心。       最簡單的隻有一行,如             obj-y += foo.o

     表示要由foo.c或者foo.s檔案編譯得到foo.o并連結進核心,而obj-m則表示該檔案要作為子產品編譯。除了y,m以外的obj-x形式的目标都不會被編譯。

由于既可以編譯成子產品,也可以編譯進核心,更常見的做法是根據.config檔案的CONFIG_ 變量來決定檔案的編譯方式,如:

obj-$(CONFIG_HELLO_MODULE) += hello.o 

除了obj-形式的目标以外,還有lib-y library庫,hostprogs-y 主機程式等目标,但是基本都應用在特定的目錄和場合下

2、多目标

     一個核心子產品由多個源檔案編譯而成,這是Makefile有所不同。

     采用子產品名加 –objs字尾或者 –y字尾的形式來定義子產品的組成檔案。

如以下例子: obj-$(CONFIG_EXT2_FS) += ext2.o

ext2-y := balloc.o bitmap.o

ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o 模 塊的名字為ext2,由balloc.o和bitmap.o兩個目标檔案最終連結生成ext2.o 直至ext2.ko檔案,是否包括xattr.o取決于核心配置檔案的配置情況。如果CONFIG_EXT2_FS的值是y也沒有關系,在此過程中生成的 ext2.o将被連結進built-in.o最終連結進核心。這裡需要注意的一點是,該kbuild Makefile所在的目錄中不應該再包含和子產品名相同的源檔案如ext2.c/ext2.s 或者寫成如-objs的形式: obj-$(CONFIG_ISDN) += isdn.o isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o

3、目錄的疊代

obj-$(CONFIG_EXT2_FS) += ext2/ 如果CONFIG_EXT2_FS 的值為y或m,kbuild将會将ext2目錄列入向下疊代的目标中,但是其作用也僅限于此,具體ext2目錄下的檔案是要作為子產品編譯還是鍊入核心,還是有ext2目錄下的Makefile檔案的内容來決定的

4、不同的子產品編譯方式

     編譯子產品的時候,你可以将子產品放在代碼樹中,用Make modules的方式來編譯你的子產品,你也可以将子產品相關檔案目錄放在代碼樹以外的位置,用如下指令來編譯子產品:

       make -C path/to/kernel/src M=$PWD modules   -C指定核心源碼的根目錄,$PWD 或 `PWD` 是目前目錄的環境變量,告訴kbuild回到目前目錄來執行build操作。

5、子產品安裝

      當你需要将子產品安裝到非預設位置的時候,你可以用INSTALL_MOD_PATH 指定一個字首,如:      make INSTALL_MOD_PATH=/foo modules_install

子產品将被安裝到 /foo/lib/modules目錄下

注:核心子產品是.ko字尾,使核心子產品和普通的目标檔案差別開。

繼續閱讀