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 9月25 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]/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選項取消,然後選擇出現的lsmod,insmod,rmmod命令,重新編譯安裝。
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,在核心中就可以找到