1. 程式人生 > >Linux 模組程式設計指南

Linux 模組程式設計指南

Linux模組程式設計

1.1 模組學習什麼

1.認識什麼是模組?跟我學習的驅動有什麼關係?

2.熟悉模組的安裝,解除安裝,檢視

3.熟悉模組的基本框架

4.熟悉模組的程式設計方法

1.2 核心模組概述

Linux 核心的整體結構非常龐大,其包含的元件也非常多。我們怎樣把需要的部分都包含在核心中呢?一種方法是把所有需要的功能都編譯到 Linux 核心。這會導致兩個問題,一是生成的核心會很大,二是如果我們要在現有的核心中新增或刪除功能,將不得不重新編譯核心。

有沒有一種機制使得編譯出的核心本身並不需要包含所有功能,而在這些功能需要被使用的時候,其對應的程式碼可被動態地載入到核心中呢?

Linux 提供了這樣的一種機制,這種機制被稱為模組(Module,可以實現以上效果。模組具有以下特點。

1.模組本身不被編譯入核心映像,從而控制了核心的大小。

2.模組一旦被載入,它就和核心中的其他部分完全一樣。

1.3 認識模組

我們在配置核心的時候,在配置選單看到有一種選項可以選擇為<M> <*> < > 。

其中,<M>表示編譯成模組,即modules,這個便於選項編譯的程式碼不編譯進核心zImage,而是編譯成一個單獨的檔案,通常為字尾.ko(2.6以上核心版本是.ko,2.6核心之前是.o)檔案,那麼我們可以像軟體一樣選擇安裝和解除安裝這些.ko模組檔案。

<*>表示編譯進核心,就是將這一段模組程式碼編譯到了zImage映象檔案去了,在核心啟動的時候自動安裝執行我們的模組程式碼,這樣做類似於一些安裝系統的時候自帶的驅動,在安裝系統的時候就已經安裝好了。

< > 表示沒有選擇,不做任何事情。

所以,我們所說的模組就是linux下的那些.ko檔案

在我們學習的Tiny4412提供的 Linux3.5 原始碼中有一項模組測試選項,如下:

         Tiny4412module sample.

         Symbol:TINY4412_HELLO_MODULE [=m]

         Type: tristate

         Prompt:Tiny4412 module sample

         Definedat drivers/char/Kconfig:49

         Dependson: ARCH_EXYNOS4 [=y]

Location:

          -> DeviceDrivers

                   ->Character devices      

注意:因為有依賴項,所以它的依賴必須被選上,即ARCH_EXYNOS4 [=y]

 CONFIG_ARCH_EXYNOS4:                                                                                

 Samsung EXYNOS4 SoCs based systems                                                                   

 Symbol: ARCH_EXYNOS4[=y]                            

 Type  :boolean                                      

 Prompt: SAMSUNGEXYNOS4                              

  Defined at arch/arm/mach-exynos/Kconfig:14         

  Depends on: ARCH_EXYNOS [=y]                       

  Location:                                          

     -> SystemType                                    

       -> SAMSUNGEXYNOS SoCs Support                 

  Selects: HAVE_SMP [=y] && MIGHT_HAVE_CACHE_L2X0 [=y]

這個測試檔案在drivers/char/目錄下的tiny4412_hello_moduls.c當把它選擇為<M>之後,編譯就會在同級目錄下生成.ko檔案。

編譯命令:make modules

在 Linux 系統中,幾乎所有驅動都可以編譯模組的形式。

總之,模組程式碼我們可以在系統啟動後再安裝(zImage檔案中並不包含該選項對應的C程式碼)。也可以編譯到核心,像核心配置選單中選擇為y的選項,對應的C程式碼會被編譯到zImage檔案中。

這裡記住一句話:linux下模組不一定是驅動,但是驅動肯是一種模組

示例:編譯成模組

1)在linux核心核心配置選單makememuconfig上把模組測試選項為M,然後退出儲存。

2)編譯成模組,如下:

[[email protected] linux-3.5]# make modules

scripts/kconfig/conf--silentoldconfig Kconfig

 CHK     include/linux/version.h

 CHK    include/generated/utsrelease.h

make[1]: “include/generated/mach-types.h”是最新的。

 CALL    scripts/checksyscalls.sh

 CC [M]  crypto/ansi_cprng.o

 CC [M] drivers/char/tiny4412_hello_module.o

 CC [M] drivers/scsi/scsi_wait_scan.o

 Building modules, stage 2.

 MODPOST 3 modules

 CC      crypto/ansi_cprng.mod.o

 LD [M]  crypto/ansi_cprng.ko

 CC     drivers/char/tiny4412_hello_module.mod.o

 LD [M] drivers/char/tiny4412_hello_module.ko

 CC     drivers/scsi/scsi_wait_scan.mod.o

 LD [M] drivers/scsi/scsi_wait_scan.ko

[[email protected] linux-3.5]#

以上就會在drivers/char/生成了tiny4412_hello_module.ko檔案。

然後我們可以檢視一下它的存在:

[[email protected] linux-3.5]# cd drivers/char/

[[email protected] char]# ls tiny4412_hello_module.ko -l

-rw-r--r--. 1root root 29062 925 11:26 tiny4412_hello_module.ko

[[email protected] char]#

有了.ko檔案,那麼我們可在以外部獨立的來載入到核心,怎麼載入呢?又怎麼解除安裝呢?

Linux系統提供專業的命令來完成這樣的事情,下面來介紹這幾個命令。

Linux下模組提供有安裝,解除安裝,檢視安裝了那個模組以及檢視模組資訊等命令。

1.3.1、模組相關命令

insmod <file name.ko>

安裝模組進核心

rmmod <file name>

解除安裝指定的模組

lsmod

檢視當前系統安裝了哪些模組

modinfo <file name>

檢視模組資訊

1.insmod   -- 安裝模組

示例

[[email protected] /home]# insmod tiny4412_hello_module.ko

[ 140.030000] Hello, Tiny4412 module is installed !

[[email protected] /home]#

2.lsmod    -- 列出當前系統已經安裝的模組及模組間的依賴關係(不包含被編譯的核心的模組程式碼)

示例

[[email protected] /home]# lsmod

tiny4412_hello_module 773 0 - Live 0xbf000000

第1列:模組名

第2列:模組大小

第3列:被引用多少次(本模組的程式碼被多少個模組呼叫)

第4列:被哪個模組引用

說明

lsmod 命令實際上讀取並分析/proc/modules檔案。

比如我修改了編譯模組的核心版本號,然後編譯檢視模組資訊:

         [[email protected]]# modinfo drivers/char/tiny4412_hello_module.ko

         filename:       drivers/char/tiny4412_hello_module.ko

         license:        GPL

         depends:       

         intree:         Y

         vermagic:       3.5.0-FriendlyARM_JUNJIASMP preempt mod_unload ARMv7 p2v8

         [[email protected]]#

安裝會出現以下問題:

         [[email protected]/home]# insmod tiny4412_hello_module.ko

         [3212.730000] tiny4412_hello_module: version magic '3.5.0-FriendlyARM_JUNJIASMP preempt mod_unload ARMv7 p2v8 ' should be '3.5.0-FriendlyARMSMP preempt mod_unload ARMv7 p2v8 '

         insmod:can't insert 'tiny4412_hello_module.ko': invalid module format

         [[email protected]/home]#

這個錯誤是說模組 版本號是3.5.0-FriendlyARM_JUNJIA,而核心版本號是3.5.0-FriendlyARM,不匹配不能進行載入。那麼我就只能

結論:模組版本和核心版本不同是不能安裝模組的。

3.rmmod    -- 解除安裝模組(編譯在核心的模組程式碼不可以移除)

示例:

[[email protected] /home]# rmmodtiny4412_hello_module.ko

[[email protected] /home]# lsmod

tiny4412_hello_module 773 0 - Live 0xbf000000 (O)    //發現沒有解除安裝掉

[[email protected] /home]# rmmodtiny4412_hello_module  //不加字尾

[  231.660000] Good-bye, Tiny4412 module was removed! //列印解除安裝函式裡的資訊

rmmod: module 'tiny4412_hello_module' not found//這個提示是busybos存在的BUG

[[email protected] /home]#

注意:使用就一些的busybox製作的根檔案系統的rmmod命令不能帶副檔名,帶了不能解除安裝;但是X86是可以帶的,也可以不帶。現在的新版本根檔案系統製作工具busybox修復了這個功能,帶.ko也能解除安裝。

如果busybox下makemenuconfig配置如下:

                   Symbol:MODPROBE_SMALL [=y] 

                   Prompt:Simplified modutils  

                   Definedat modutils/Config.in:15                    

                 Location:                                                                        

                            ->Linux Module Utilities 

模組安裝後如果需要解除安裝,需要在lib下建立modules目錄,然後再建立一個以核心版本名命名的子目錄。

格式:mkdir/lib/modules/核心版本號

示例:

mkdir/lib/modules/3.5.0-FriendlyARM

核心版本號:(1)可以通過 uname -r 得到;(2)也可以通過檢視核心配置,子版本是我們在配置核心的時候寫上去的。如下:

         Symbol:LOCALVERSION [=-FriendlyARM]                                           

           │ Type  : string                                                            

           │ Prompt: Local version - append to kernel release                          

           │   Defined at init/Kconfig:91                                              

           │   Location:                 

           │     -> Generalsetup 

那麼核心版本號是核心主版本號(3.5.0)接以上的本地版本號(-FriendlyARM)構成。

我們可以有根治的方法,把Simplified modutils選項取消,然後選擇出現的lsmodinsmodrmmod命令,重新編譯安裝。

4.modinfo  -- 檢視模組的基本資訊

示例:            

[[email protected] 桌面]# modinfo dm_mod

         filename:     /lib/modules/2.6.32-279.el6.i686/kernel/drivers/md/dm-mod.ko

         license:        GPL

         author:         Joe Thornber<[email protected]>

         description:    device-mapper driver

         srcversion:     55E98DC47312D5D1A682B77

         depends:       

         vermagic:       2.6.32-279.el6.i686 SMP mod_unloadmodversions 686

         parm:           major:The major number of the devicemapper (uint)

說明:

filename:                          模組路徑(相對的)

license:                             模組釋出許可協議

author:                             模組作者

description:                     模組是功能描述

srcversion:                       原始碼版本 --- 不是由程式設計者決定,不可以控制

depends:                          依賴

vermagic:                        核心的版本魔數,簡單就是一版本的ID,由核心原始碼決定,不可控制

parm:                                模組可以傳遞的引數介紹

使用這個命令可能遇到問題

[[email protected] /home]# modinfo tiny4412_hello_module.ko

modinfo: can't open'/lib/modules/3.5.0-FriendlyARM/modules.dep': No such file or directory

解決辦法

cd /lib/

mkdir modules

cd modules/

mkdir <linux version number>

cd <linux version number>

cp *.ko ./

depmod

mv modules.dep.bb modules.dep

modinfo *.ko

1.4模組程式框架

我們看一下tiny4412_hello_modules.c,內容如下:

         #include<linux/kernel.h>

         #include<linux/module.h>

         staticint __init tiny4412_hello_module_init(void)

         {

             printk("Hello, Tiny4412 module isinstalled !\n");

             return 0;

         }

         staticvoid __exit tiny4412_hello_module_cleanup(void)

         {

             printk("Good-bye, Tiny4412 module wasremoved!\n");

         }

         module_init(tiny4412_hello_module_init);

         module_exit(tiny4412_hello_module_cleanup);

         MODULE_LICENSE("GPL");

以上是一個模組最基本的框架,我們來說明這個框架的組成都有哪些?

1.必須的標頭檔案

#include <linux/kernel.h>

#include <linux/module.h>

包含了我們這個基本模組所用到的函式宣告。

2.模組的初始化(載入)函式(必須)

以下模組初始化函式,insmod 模組時會執行,如果是編譯到核心,在系統啟動階段會自動執行。典型的模組初始化函式形式入下:

static int __init tiny4412_hello_module_init(void)

         {

                 /* 初始化程式碼 */

                   printk("Hello,Tiny4412 module is installed !\n");

                   return0;

         }

module_init(tiny4412_hello_module_init);

說明:

(1)宣告static,限定它的作用域,防止與其他檔案下的同名函式起衝突。

(2)__init 宣告它是一個初始化的函式,在insmod或當編譯到核心,在系統啟動階段會自動執行的函式。         在 Linux 核心中,所有標識為__init的函式在連線的時候都放在.init.text 這個區段內,此外,所有的_ _init 函式在區段.initcall.init中還儲存了一份函式指標,在初始化時核心會通過這些函式指標呼叫這些__init 函式,並在初始化完成後釋放init區段(包括.init.text,.initcall.init 等)。

(3)引數是void型別,沒有形參。

(4)返回值是int型別,通常正常執行返回0,出現錯誤返回錯誤碼。在Linux核心裡,錯誤編碼是一個負值,在<linux/errno.h>中定義,包含-ENODEV、-ENOMEM 之類的符號值。返回相應的錯誤編碼是種非常好的習慣,因為只有這樣,使用者程式才可以利用 perror等方法把它們轉換成有意義的錯誤資訊字串。

(5)做為外部模組的時候,模組必須以module_init(函式名的形式被指定)。

(6)在Linux 2.6核心中,可以使用 request_module(const char *fmt,)函式載入核心模組,驅動開發人員可以通過呼叫:

request_module(module_name);

request_module("char-major-%d-%d",MAJOR(dev), MINOR(dev));

來載入其他核心模組。

3.模組解除安裝函式(必須)

當rmmod時,或則通過其他方式解除安裝模組時候會執行。

static void __exittiny4412_hello_module_cleanup(void)

{

                 /* 釋放程式碼 */

          printk("Good-bye, Tiny4412 module wasremoved!\n");

}

module_exit(tiny4412_hello_module_cleanup);

說明:

(1)宣告static,限定它的作用域,防止與其他檔案下的同名函式起衝突。

(2)__exit宣告它是一個解除安裝型別函式,在執行rmmod命令的時候執行。和__init 一樣,__exit 也可以使對應函式在執行完成後自動回收記憶體。實際上,__init 和__exit 都是巨集,其定義分別為:

#define __init __attribute__ ((__section__(".init.text")))

#ifdef MODULE

#define __exit __attribute__((__section__(".exit.text")))

#else

#define __exit __attribute_used____attribute__((__section__(".exit.text")))

#endif

對於資料也可以被定義為__initdata __exitdata,這兩個巨集分別為:

#define __initdata__attribute__ ((__section__(".init.data")))

#define __exitdata__attribute__((__section__(".exit.data")))

(3)沒有形參,沒有返回值。

(4)做為外部模組的時候,必須以“module_exit(函式名)”的形式來指定。

(5)通常來說,模組解除安裝函式要完成與模組載入函式相反的功能,如下所示:

1.若模組載入函式註冊了 XXX,則模組解除安裝函式應該登出 XXX。

2.若模組載入函式動態申請了記憶體,則模組解除安裝函式應釋放該記憶體。

3.若模組載入函式申請了硬體資源(中斷、DMA 通道、I/O 埠和 I/O 記憶體等)的佔用,則模組解除安裝函式應釋放這些硬體資源。

4.若模組載入函式開啟了硬體,則解除安裝函式中一般要關閉硬體。

(1)module_init 宣告tiny4412_hello_module_init是一個初始化函式。

(2)module_exit 宣告tiny4412_hello_module_cleanup是一個解除安裝函式。

        module_init(tiny4412_hello_module_init);

module_exit(tiny4412_hello_module_cleanup);

這兩句話的作用跟__init,__exit作用相同,這裡是為了雙重保險。通常__init,__exit這個編譯到核心的時候使用,而module_init,module_exit這兩編譯成模組使用。

宣告該模組是基於GPL協議建立的,如果沒有宣告,當安裝的時候提示該程式會汙染我的核心。在Linux 2.6核心中,可接受的LICENSE包括“GPL”、“GPL v2”、“GPL and additional rights”、“Dual BSD/GPL”、“DualMPL/GPL”和“Proprietary”。

大多數情況下,核心模組應遵循 GPL 相容許可權。

6.模組引數(可選)

模組引數是模組被載入的時候可以被傳遞給它的值,它本身對應模組內部的全域性變數。

7.模組匯出符號(可選)

核心模組可以匯出符號(symbol,對應於函式或變數),這樣其他模組可以使用本模組中的變數或函式。

8.模組作者等資訊宣告(可選)

在 Linux 核心模組中, 我們可以用 MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_ VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS 分別宣告模組的作者、描述、版本、裝置表和別名,例如:

MODULE_AUTHOR(author);

MODULE_DESCRIPTION(description);

MODULE_VERSION(version_string);

MODULE_DEVICE_TABLE(table_info);

MODULE_ALIAS(alternate_name);

對於 USB、PCI 等裝置驅動,通常會建立一個MODULE_DEVICE_TABLE驅動所支援的裝置列表,如下所示:

 /* 對應此驅動的裝置表 */

 staticstruct usb_device_id skel_table [] = {

        { USB_DEVICE(USB_SKEL_VENDOR_ID,

         USB_SKEL_PRODUCT_ID)},

         { }/* 表結束 */

};

MODULE_DEVICE_TABLE (usb, skel_table);

此時,並不需要讀者理解MODULE_DEVICE_TABLE 的作用,後續相關章節會有詳細介紹

小知識:

核心模組中用於輸出的函式是核心空間的 printk()而非使用者空間的 printf(), printk()的用法和 printf()相似,但前者可定義輸出級別。printk()可作為一種最基本的核心除錯手段。

1.5模組的編譯方式

模組的編譯通常有兩種,一種是修改核心配置選單,利用核心配置選單編譯;另一種是自己編寫makefike檔案,讀取核心的makefile來編譯。

第一種:修改核心配置選單

1)把模組新增到核心原始碼目錄;

2)然後做一個選單,再把選單配置為M選項;

3)再使用 make modules命令編譯成.ko檔案。

這種方式比較麻煩,繁瑣。

第二種:自己編寫makefile

獨立編寫一個Makefile來編譯模組;這樣模組原始碼可以放在任何地方,這種方法在開發中最常用。其實可以不需要Makefile,只需要一條命令就能搞定的。這條命令如下:

make -C 核心原始碼絕對路徑M=模組原始碼檔案所在的絕對路徑 modules 

但是寫成makefile是為了方便我們執行多步操作。

示例:編寫一個編譯模組的makefile檔案通常包含以下內容。

指定編譯目標:obj-m:=xxx.o

ARM板的核心原始碼路徑:/works/linux-3.5/

模組原始碼路徑 :/linux_share/hello_model_single

命令

make -C /root/work/linux-3.5/ M=/linux_share/hello_model_singlemodules 

這條命令完成的工作是進入到核心原始碼目錄,讀取核心原始碼目錄的Makefile。(-C dir 是讀入指定目錄下的makefile),執行核心原始碼Makefile中的modules目標(這裡我們自己知道了目標obj-m:=xxx.o),根據 modules 目標編譯M所指向的檔案路徑下的 C檔案。

為了更方便,一般我們寫成Makefile檔案。

單個檔案單模組Makefile的模板

# Makefile 2.6

#hello最終的模組名,單檔案單模組時,這個名字就是原始碼檔名,hello.o對應於hello.c

obj-m :=hello.o

#KDIR是核心原始碼路徑,當編譯用於X86平臺模組的時候使用X86上的核心原始碼,當編譯ARM的模組時候使用自己配套的Linux核心原始碼

#KDIR  :=/lib/modules/$(shell uname-r)/build

KDIR   := /works/linux-3.5/

all:

         make -C $(KDIR) M=$(PWD) modules  #$(PWD) 是代表當前路徑,也就是模組原始碼路徑

clean:

         rm -f *.ko *.o *.mod.o *.mod.c*.symvers *.markers *.unsigned *.order *~

一般只需根據自己的情況修改KDIR 和 obj-m := ? 內容就可以了。

         注意:KDIR所指向的核心原始碼一定要被成功編譯過,沒有清除工程才能編譯模組。

1.6模組幾種常見模型示例

1.6.1 單檔案單模組示例

1.模組程式碼清單

/* hello.c */

#include<linux/module.h>       /* Needed byall modules */

#include <linux/init.h>         /* Needed for the module-macros */

static int __init hello_init(void)

{

         printk(KERN_DEBUG"Hello world, priority = 7\n");

         printk(KERN_INFO"Hello world, priority = 6\n");

         printk("Helloworld, priority = DEFAULT_MESSAGE_LOGLEVEL\n");

         printk(KERN_NOTICE"Hello world, priority = 5\n");

         printk(KERN_WARNING"Hello world, priority = 4\n");

         printk(KERN_ERR"Hello world, priority = 3\n");

         printk(KERN_CRIT"Hello world, priority = 2\n");

         printk(KERN_ALERT"Hello world, priority = 1\n");

         printk(KERN_EMERG"Hello world, priority = 0\n");

  return 0;

}

static void  __exit hello_exit(void) 

{

         printk(KERN_DEBUG"Goodbye,cruel world!, priority = 7\n");

         printk(KERN_INFO"Goodbye,cruel world!, priority = 6\n");

         printk("Goodbye,cruelworld!, priority = DEFAULT_MESSAGE_LOGLEVEL\n");

         printk(KERN_NOTICE"Goodbye,cruel world!, priority = 5\n");

         printk(KERN_WARNING"Goodbye,cruel world!, priority = 4\n");

         printk(KERN_ERR"Goodbye,cruel world!, priority = 3\n");

         printk(KERN_CRIT"Goodbye,cruel world!, priority = 2\n");

         printk(KERN_ALERT"Goodbye,cruel world!, priority = 1\n");

         printk(KERN_EMERG"Goodbye,cruel world!, priority = 0\n");

}

module_init(hello_init);

module_exit(hello_exit);

MODULE_LICENSE("DualBSD/GPL"); 

MODULE_AUTHOR("BENSON");

MODULE_DESCRIPTION("STUDY_MODULE");

# Makefile 2.6

obj-m := hello.o

KDIR  :=/lib/modules/$(shelluname -r)/build

#KDIR   :=/root/work/linux-3.5/

all:

         make -C $(KDIR)M=$(PWD) modules  

         @rm -f *.o *.mod.o*.mod.c *.symvers *.markers *.unsigned *.order *~

clean:

         rm -f *.ko *.o *.mod.o*.mod.c *.symvers *.markers *.unsigned *.order *~

3.編譯,安裝模組

如果編譯給X86用的,因為printk的列印優先順序問題,不會都列印到終端。

我們可以在終端輸入tail/var/log/messages可以檢視到日誌檔案最後10行,注意檢視動作快點,要不然日誌更新之後就看不到了。

如果編譯給開發版使用,因為沒有設定優先順序,可以全部從串列埠列印輸出。

1.6.2多模組之間有依賴關係示例

1.示例程式碼清單

首先我編寫兩個模組,它們之間是有依賴的。程式碼如下:

Calculate.c檔案程式碼清單:

#include <linux/init.h>

#include <linux/module.h>

static int add_integar(int a,intb)

{

         returna+b;

}

static int sub_integar(int a,intb)

{

         returna-b;

}

static int __init sym_init()

{

         return0;

}

static void __exit sym_exit() {}

module_init(sym_init);

module_exit(sym_exit);

MODULE_LICENSE("GPL");

        //提供介面

EXPORT_SYMBOL(add_integar);

EXPORT_SYMBOL(sub_integar);

Hello.c程式碼清單:

#include <linux/module.h>

#include <linux/init.h>

//在其他地方定義,在這裡宣告之後可以在本檔案使用

extern int add_integar(int a,int b);

extern int sub_integar(int a,int b);

static int __init hello_init(void)

{

         intres=add_integar(1,2);

         printk(KERN_EMERG"helloinit , res=%d\n",res);

         return0;

}

static void __exit hello_exit()

{

         intres=sub_integar(2,1);

         printk(KERN_EMERG"helloexit,res=%d\n",res);

}

module_init(hello_init);

module_exit(hello_exit);

MODULE_LICENSE("GPL");

依賴關係的Makefile可以寫成以下形式:

# hello.o對應hello.c ,calculate.o對應calculate.c,編譯後會生成兩個ko檔案。

obj-m := hello.ocalculate.o

#KDIR := /lib/modules/$(shell uname -r)/build

KDIR :=/root/work/linux-3.5/

all:

         @make -C $(KDIR) M=$(PWD) modules  

         @rm -f *.o *.mod.o *.mod.c *.symvers*.markers *.unsigned *.order *~ *.bak

clean:

         rm -f *.ko *.o *.mod.o *.mod.c*.symvers *.markers *.unsigned *.order *~ *.bak

注意:hello.c,calculate.c 必須是以模組程式碼框架格式編寫,不能普通c程式碼格式

3.程式碼安裝解除安裝操作分析

操作示例

[[email protected] /home]# ls

calculate.ko            hello.ko                  //把以上的兩個.C檔案編譯成模組        

[[email protected] /home]# lsmod                        

[[email protected] /home]# insmod hello.ko      //先安裝hello.c

[ 7103.030000] hello: Unknown symboladd_integar (err 0)

[ 7103.030000] hello: Unknown symbolsub_integar (err 0)

insmod: can't insert 'hello.ko': unknown symbolin module or invalid parameter

提示錯誤,找不到add_integar,sub_integar符號。原因這兩個函式具體實現程式碼不存在 hello.ko中,也不在當前的核心中,而是在calculate.c中。所以安裝不成功。

那麼怎麼辦呢?先安裝 calculate.ko,這樣add_integar,sub_integar這兩個函式就在核心中了。

[[email protected] /home]# insmod calculate.ko

再來安裝hello.ko,在核心中就可以找到