Linux內(nèi)核模塊間通訊方法非常的多,最便捷的方法莫過(guò)于函數(shù)或變量符號(hào)導(dǎo)出,然后直接調(diào)用。默認(rèn)情況下,模塊與模塊之間、模塊與內(nèi)核之間的全局變量是相互獨(dú)立的,只有通過(guò)EXPORT_SYMBOL
將模塊導(dǎo)出才能對(duì)其他模塊或內(nèi)核可見(jiàn)。
符號(hào)導(dǎo)出函數(shù)
(資料圖片僅供參考)
EXPORT_SYMBOL()
:括號(hào)中定義的函數(shù)或變量對(duì)全部?jī)?nèi)核代碼公開(kāi)EXPORT_SYMBOL_GPL()
和EXPORT_SYMBOL
類(lèi)似,但范圍只適合GPL許可的模塊進(jìn)行調(diào)用
Linux kallsyms,即內(nèi)核符號(hào)表,其中會(huì)列出所有的Linux內(nèi)核中的導(dǎo)出符號(hào),在用戶(hù)態(tài)下可以通過(guò)/proc/kallsyms
訪(fǎng)問(wèn),此時(shí)由于內(nèi)核保護(hù),看到的地址為0x0000000000000000
,在root模式下可以看到真實(shí)地址。啟用kallsyms需要編譯內(nèi)核時(shí)設(shè)置CONFIG_KALLSYMS
為y。
/proc/kallsyms
會(huì)顯示內(nèi)核中所有的符號(hào),但是這些符號(hào)不是都能被其他模塊引用的(絕大多數(shù)都不能),能被引用的符號(hào)是被EXPORT_SYMBOL
或EXPORT_SYMBOL_GPL
導(dǎo)出的
內(nèi)核模塊在編譯時(shí)符號(hào)的查找順序:
在本模塊中符號(hào)表中,尋找符號(hào)(函數(shù)或變量實(shí)現(xiàn))在內(nèi)核全局符號(hào)表中尋找在模塊目錄下的Module.symvers文件中尋找內(nèi)核符號(hào)表就是在內(nèi)核內(nèi)部函數(shù)或變量中可供外部引用的函數(shù)和變量的符號(hào)表(/proc/kallsyms),表格如下:
符號(hào)類(lèi)型 | 名稱(chēng) | 說(shuō)明 |
---|---|---|
A | Absolute | 符號(hào)的值是絕對(duì)值,并且在進(jìn)一步鏈接過(guò)程中不會(huì)被改變 |
B | BSS | 符號(hào)在未初始化數(shù)據(jù)區(qū)或區(qū)(section)中,即在BSS段中 |
C | Common | 符號(hào)是公共的。公共符號(hào)是未初始化的數(shù)據(jù)。在鏈接時(shí),多個(gè)公共符號(hào)可能具有同一名稱(chēng)。如果該符號(hào)定義在其他地方,則公共符號(hào)被看作是未定義的引用 |
D | Data | 符號(hào)在已初始化數(shù)據(jù)區(qū)中 |
G | Global | 符號(hào)是在小對(duì)象已初始化數(shù)據(jù)區(qū)中的符號(hào)。某些目標(biāo)文件的格式允許對(duì)小數(shù)據(jù)對(duì)象(例如一個(gè)全局整型變量)可進(jìn)行更有效的訪(fǎng)問(wèn) |
I | Inderect | 符號(hào)是對(duì)另一個(gè)符號(hào)的間接引用 |
N | Debugging | 符號(hào)是一個(gè)調(diào)試符號(hào) |
R | Readonly | 符號(hào)在一個(gè)只讀數(shù)據(jù)區(qū)中 |
S | Small | 符號(hào)是小對(duì)象未初始化數(shù)據(jù)區(qū)中的符號(hào) |
T | Text | 符號(hào)是代碼區(qū)中的符號(hào) |
U | Undefined | 符號(hào)是外部的,并且其值為0(未定義) |
V | Weaksymbol | 弱符號(hào) |
W | Weaksymbol | 弱符號(hào) |
- | Stabs | 符號(hào)是a.out目標(biāo)文件中的一個(gè)stab符號(hào),用于保存調(diào)試信息 |
? | Unknown | 符號(hào)的類(lèi)型未知,或者與具體文件格式有關(guān) |
注:符號(hào)屬性,小寫(xiě)表示局部符號(hào),大寫(xiě)表示全局符號(hào)
這里我們定義兩個(gè)源文件myexportfunc.c
和myusefunc.c
,分別放置在不同目錄;在myexportfunc.c
文件中導(dǎo)出publicFunc
函數(shù)和變量myOwnVar
以供myusefunc.c
文件中的函數(shù)調(diào)用。myusefunc.c
文件中要想成功調(diào)用publicFunc
函數(shù)和myOwnVar
變量,必須進(jìn)行extern
聲明,否則編譯時(shí)會(huì)報(bào)錯(cuò)。源碼如下:
myexportfunc.c
文件:
/* myexportfunc.c */#include < linux/module.h >#include < linux/kernel.h >#include < linux/init.h >char myOwnVar[30]="Linux kernel communication.";static int __init myfunc_init(void){ printk("Hello,this is my own module!\\n"); return 0;}static void __exit myfunc_exit(void){ printk("Goodbye,this is my own clean module!\\n");}void publicFunc(void){ printk(KERN_INFO "This is public module and used for another modules.\\n");}module_init(myfunc_init);module_exit(myfunc_exit);EXPORT_SYMBOL(publicFunc);EXPORT_SYMBOL(myOwnVar);MODULE_DESCRIPTION("First Personel Module");MODULE_AUTHOR("Lebron James");MODULE_LICENSE("GPL");
myexportfunc.c
文件的Makefile
文件:
ifneq ($(KERNELRELEASE),)$(info "2nd")obj-m:=myexportfunc.oelseKDIR :=/lib/modules/$(shell uname -r)/buildPWD :=$(shell pwd)all: $(info "1st") make -C $(KDIR) M=$(PWD) modulesclean: rm -f *.ko *.o *.mod.o *.symvers *.cmd *.mod.c *.order *.modendif
myusefunc.c
文件:
/* myusefunc.c */#include < linux/init.h >#include < linux/module.h >MODULE_LICENSE("GPL");extern void publicFunc(void);extern char myOwnVar[30];void showVar(void);static int __init hello_init(void){ printk(KERN_INFO "Hello,this is myusefunc module.\\n"); publicFunc(); showVar(); return 0;}static void __exit hello_exit(void){ printk(KERN_INFO "Goodbye this is myusefunc module.\\n");}void showVar(void){ printk(KERN_INFO "%s\\n", myOwnVar);}module_init(hello_init);module_exit(hello_exit);
myusefunc.c
文件的Makefile
文件:
KBUILD_EXTRA_SYMBOLS += /tmp/tmp/Module.symversifneq ($(KERNELRELEASE),)$(info "2nd")obj-m:=myusefunc.oelseKDIR :=/lib/modules/$(shell uname -r)/buildPWD :=$(shell pwd)all: $(info "1st") make -C $(KDIR) M=$(PWD) modulesclean: rm -f *.ko *.o *.mod.o *.symvers *.cmd *.mod.c *.order *.modendif
注:
KBUILD_EXTRA_SYMBOLS
用來(lái)告訴內(nèi)核當(dāng)前module
需要引用另外一個(gè)module
導(dǎo)出的符號(hào)。KBUILD_EXTRA_SYMBOLS
后需要寫(xiě)絕對(duì)路徑,相對(duì)路徑會(huì)出錯(cuò),因?yàn)?code>scripts/mod/modpost執(zhí)行時(shí), 以其在內(nèi)核目錄的路徑為起始點(diǎn)進(jìn)行解析。
分別通過(guò)make
命令編譯myexportfunc.c
和myusefunc.c
文件:
[root@localhost tmp]# make"1st"make -C /lib/modules/4.18.0-394.el8.x86_64/build M=/tmp/tmp modulesmake[1]: Entering directory "/usr/src/kernels/4.18.0-394.el8.x86_64""2nd" CC [M] /tmp/tmp/myexportfunc.o Building modules, stage 2."2nd" MODPOST 1 modules CC /tmp/tmp/myexportfunc.mod.o LD [M] /tmp/tmp/myexportfunc.komake[1]: Leaving directory "/usr/src/kernels/4.18.0-394.el8.x86_64"[root@localhost tmp]# ls -ltotal 488drwxr-xr-x 3 root root 139 May 25 04:40 24-rw-r--r-- 1 root root 260 May 24 13:34 Makefile-rw-r--r-- 1 root root 32 May 25 04:40 modules.order-rw-r--r-- 1 root root 114 May 25 04:40 Module.symvers-rw-r--r-- 1 root root 655 May 25 04:40 myexportfunc.c-rw-r--r-- 1 root root 237256 May 25 04:40 myexportfunc.ko-rw-r--r-- 1 root root 826 May 25 04:40 myexportfunc.mod.c-rw-r--r-- 1 root root 117856 May 25 04:40 myexportfunc.mod.o-rw-r--r-- 1 root root 121336 May 25 04:40 myexportfunc.o[root@localhost tmp]# cd 24/[root@localhost 24]# lsMakefile myusefunc.c[root@localhost 24]# make"1st"make -C /lib/modules/4.18.0-394.el8.x86_64/build M=/tmp/tmp/24 modulesmake[1]: Entering directory "/usr/src/kernels/4.18.0-394.el8.x86_64""2nd" CC [M] /tmp/tmp/24/myusefunc.o Building modules, stage 2."2nd" MODPOST 1 modules CC /tmp/tmp/24/myusefunc.mod.o LD [M] /tmp/tmp/24/myusefunc.komake[1]: Leaving directory "/usr/src/kernels/4.18.0-394.el8.x86_64"[root@localhost tmp]# cd ..[root@localhost tmp]# insmod ./myexportfunc.ko[root@localhost tmp]# cd 24/[root@localhost 24]# ls -ltotal 488-rw-r--r-- 1 root root 305 May 24 13:34 Makefile-rw-r--r-- 1 root root 32 May 25 04:42 modules.order-rw-r--r-- 1 root root 114 May 25 04:42 Module.symvers-rw-r--r-- 1 root root 557 May 24 13:33 myusefunc.c-rw-r--r-- 1 root root 235832 May 25 04:42 myusefunc.ko-rw-r--r-- 1 root root 898 May 25 04:42 myusefunc.mod.c-rw-r--r-- 1 root root 117984 May 25 04:42 myusefunc.mod.o-rw-r--r-- 1 root root 119392 May 25 04:42 myusefunc.o[root@localhost 24]# insmod ./myusefunc.ko[root@localhost 24]#[root@localhost 24]# lsmod | grep -E "myexportfunc|myusefunc"myusefunc 16384 0myexportfunc 16384 1 myusefunc
注:這里必須先
insmod myexportfunc
模塊,再insmod myusefunc
模塊。假如先insmod myusefunc
模塊,則會(huì)發(fā)生如下錯(cuò)誤:[root@localhost 24]# insmod ./myusefunc.koinsmod: ERROR: could not insert module ./myusefunc.ko: Unknown symbol in module[root@localhost 24]# lsmod | grep myusefunc[root@localhost 24]#
且通過(guò)
lsmod
查看可知,模塊未能加載成功。
Linux內(nèi)核知道的所有符號(hào)都列在/proc/kallsyms
中。讓我們?cè)谶@個(gè)文件中搜索我們的符號(hào)。
[root@localhost 24]# cat /proc/kallsyms | grep -E "publicFunc|myOwnVar"ffffffffc0480030 r __ksymtab_myOwnVar [myexportfunc]ffffffffc04800e4 r __kstrtab_myOwnVar [myexportfunc]ffffffffc0480040 r __ksymtab_publicFunc [myexportfunc]ffffffffc04800ed r __kstrtab_publicFunc [myexportfunc]ffffffffc047f000 T publicFunc [myexportfunc]ffffffffc0481000 D myOwnVar [myexportfunc]
我們可以看到,我們導(dǎo)出的符號(hào)列在內(nèi)核識(shí)別的符號(hào)中。上述列表信息具體含義解釋?zhuān)?/p>
第一列,是該符號(hào)在內(nèi)核地址空間中的地址
第二列,是符號(hào)屬性,小寫(xiě)表示局部符號(hào),大寫(xiě)表示全局符號(hào)(具體含義參考man nm)
第三列,表示符號(hào)字符串(即函數(shù)名或變量等)
第四列,表示加載的驅(qū)動(dòng)名稱(chēng)
如果您查看編譯模塊所在目錄中的Module.symvers
文件,將看到一行類(lèi)似于以下內(nèi)容
[root@localhost tmp]# cat Module.symvers0x4db1caee publicFunc /tmp/tmp/myexportfunc EXPORT_SYMBOL0x519e6cfa myOwnVar /tmp/tmp/myexportfunc EXPORT_SYMBOL
Module.symvers
包含所有導(dǎo)出的列表符號(hào),Module.symvers
file 的語(yǔ)法格式:;當(dāng)內(nèi)核編譯選項(xiàng)
CONFIG_MODVERSIONS
關(guān)閉時(shí),所有的CRC
值都為0x00000000
上述編譯并且加載模塊完畢后, 通過(guò)dmesg
可以看到打印信息如下:
[root@localhost 24]# dmesg[1028204.777932] Hello,this is my own module![1028215.008381] Hello,this is myusefunc module.[1028215.008385] This is public module and used for another modules.[1028215.008386] Linux kernel communication.
通過(guò)打印信息可知,publicFunc
函數(shù)以成功實(shí)現(xiàn)在其他內(nèi)核模塊內(nèi)調(diào)用。接下來(lái),我們卸載內(nèi)核模塊,再看下會(huì)發(fā)生什么?
[root@localhost tmp]# rmmod myexportfuncrmmod: ERROR: Module myexportfunc is in use by: myusefunc
報(bào)錯(cuò)了,很詫異吧,為什么會(huì)無(wú)法卸載呢,我們從報(bào)錯(cuò)信息中就可以看出端倪, myexportfunc
模塊正在被myusefunc
模塊使用,所以無(wú)法卸載。通過(guò)modinfo
命令,我們也可以看出myusefunc
模塊的依賴(lài)關(guān)系:
[root@localhost 24]# modinfo myusefunc.kofilename: /tmp/tmp/24/myusefunc.kolicense: GPLrhelversion: 8.7srcversion: 092199D11396603B6377902depends: myexportfuncname: myusefuncvermagic: 4.18.0-394.el8.x86_64 SMP mod_unload modversions
通過(guò)上述depends
行的結(jié)果可以看出,myusefunc
模塊依賴(lài)myexportfunc
模塊。
因此卸載也是需要按照順序進(jìn)行,先卸載調(diào)用模塊,再卸載被調(diào)用模塊,方可保證卸載成功。
[root@localhost tmp]# rmmod myusefunc[root@localhost tmp]# rmmod myexportfunc
按照如上所說(shuō)進(jìn)行卸載,果然成功了,再通過(guò)dmesg
查看打印的信息是什么,如下:
[root@localhost ~]# dmesg[ 635.296204] Hello,this is my export module![ 646.274636] Hello,this is myusefunc module.[ 646.274655] This is public function and used for another modules.[ 646.274657] Linux kernel communication.[ 676.093397] Goodbye this is myusefunc module.[ 683.385341] Goodbye,this is my export clean module![root@localhost ~]#
同樣定義兩個(gè)源文件myexportfunc.c
和myusefunc.c
,分別放置在不同目錄;在myexportfunc.c
文件中使用EXPORT_SYMBOL_GPL
導(dǎo)出publicFunc
函數(shù)以供myusefunc.c
文件中的函數(shù)調(diào)用。myusefunc.c
文件中要想成功調(diào)用publicFunc
函數(shù),必須進(jìn)行extern
聲明,否則編譯時(shí)會(huì)報(bào)錯(cuò)。源碼如下:
myexportfunc.c
文件:
/* myexportfunc.c */#include < linux/module.h >#include < linux/kernel.h >#include < linux/init.h >char myOwnVar[30]="Linux kernel communication.";static int __init myfunc_init(void){ printk("Hello,this is my own module!\\n"); return 0;}static void __exit myfunc_exit(void){ printk("Goodbye,this is my own clean module!\\n");}void publicFunc(void){ printk(KERN_INFO "This is public module and used for another modules.\\n");}module_init(myfunc_init);module_exit(myfunc_exit);EXPORT_SYMBOL_GPL(publicFunc);EXPORT_SYMBOL(myOwnVar);MODULE_DESCRIPTION("First Personel Module");MODULE_AUTHOR("Lebron James");MODULE_LICENSE("GPL");
myexportfunc.c
文件的Makefile
文件:
ifneq ($(KERNELRELEASE),)$(info "2nd")obj-m:=myexportfunc.oelseKDIR :=/lib/modules/$(shell uname -r)/buildPWD :=$(shell pwd)all: $(info "1st") make -C $(KDIR) M=$(PWD) modulesclean: rm -f *.ko *.o *.mod.o *.symvers *.cmd *.mod.c *.order *.modendif
myusefunc.c
文件:
/* myusefunc.c */#include < linux/init.h >#include < linux/module.h >extern void publicFunc(void);extern char myOwnVar[30];void showVar(void);static int __init hello_init(void){ printk(KERN_INFO "Hello,this is myusefunc module.\\n"); publicFunc(); showVar(); return 0;}static void __exit hello_exit(void){ printk(KERN_INFO "Goodbye this is myusefunc module.\\n");}void showVar(void){ printk(KERN_INFO "%s\\n", myOwnVar);}module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("GPL");
myusefunc.c
文件的Makefile
文件:
KBUILD_EXTRA_SYMBOLS += /tmp/tmp/Module.symversifneq ($(KERNELRELEASE),)$(info "2nd")obj-m:=myusefunc.oelseKDIR :=/lib/modules/$(shell uname -r)/buildPWD :=$(shell pwd)all: $(info "1st") make -C $(KDIR) M=$(PWD) modulesclean: rm -f *.ko *.o *.mod.o *.symvers *.cmd *.mod.c *.order *.modendif
分別通過(guò)make
命令編譯myexportfunc.c
和myusefunc.c
文件:
[root@localhost tmp]# ls24 Makefile myexportfunc.c[root@localhost tmp]# make"1st"make -C /lib/modules/4.18.0-394.el8.x86_64/build M=/tmp/tmp modulesmake[1]: Entering directory "/usr/src/kernels/4.18.0-394.el8.x86_64""2nd" CC [M] /tmp/tmp/myexportfunc.o Building modules, stage 2."2nd" MODPOST 1 modules CC /tmp/tmp/myexportfunc.mod.o LD [M] /tmp/tmp/myexportfunc.komake[1]: Leaving directory "/usr/src/kernels/4.18.0-394.el8.x86_64"[root@localhost tmp]# cd 24[root@localhost 24]# lsMakefile myusefunc.c[root@localhost 24]# vi myusefunc.c[root@localhost 24]# make"1st"make -C /lib/modules/4.18.0-394.el8.x86_64/build M=/tmp/tmp/24 modulesmake[1]: Entering directory "/usr/src/kernels/4.18.0-394.el8.x86_64""2nd" CC [M] /tmp/tmp/24/myusefunc.o Building modules, stage 2."2nd" MODPOST 1 modules CC /tmp/tmp/24/myusefunc.mod.o LD [M] /tmp/tmp/24/myusefunc.komake[1]: Leaving directory "/usr/src/kernels/4.18.0-394.el8.x86_64"[root@localhost 24]#[root@localhost 24]# cd ..[root@localhost tmp]# ls24 Makefile modules.order Module.symvers myexportfunc.c myexportfunc.ko myexportfunc.mod.c myexportfunc.mod.o myexportfunc.o[root@localhost tmp]# insmod ./myexportfunc.ko[root@localhost tmp]#[root@localhost tmp]# cd 24[root@localhost 24]# lsMakefile modules.order Module.symvers myusefunc.c myusefunc.ko myusefunc.mod.c myusefunc.mod.o myusefunc.o[root@localhost 24]# insmod ./myusefunc.ko[root@localhost 24]#[root@localhost 24]# cat /proc/kallsyms | grep publicFuncffffffffc0480040 r __ksymtab_publicFunc [myexportfunc]ffffffffc04800ed r __kstrtab_publicFunc [myexportfunc]ffffffffc047f000 t publicFunc [myexportfunc] [root@localhost 24]# cat /proc/kallsyms | grep myOwnVarffffffffc0480030 r __ksymtab_myOwnVar [myexportfunc]ffffffffc04800e4 r __kstrtab_myOwnVar [myexportfunc]ffffffffc0481000 D myOwnVar [myexportfunc][root@localhost 24]# dmesg[1039412.015206] Hello,this is my own module![1039426.559478] Hello,this is myusefunc module.[1039426.559482] This is public module and used for another modules.[1039426.559482] Linux kernel communication.
注:
使用EXPORT_SYMBOL_GPL
導(dǎo)出的符號(hào)的模塊類(lèi)型為小寫(xiě)字母t,表示局部引用使用EXPORT_SYMBOL
導(dǎo)出的符號(hào)的模塊類(lèi)型為大寫(xiě)字母D,表示全局引用
上述內(nèi)容顯示了成功在內(nèi)核模塊調(diào)用其他內(nèi)核模塊的函數(shù)或變量的過(guò)程,接下來(lái),讓我們一起看一下,哪些情況下會(huì)導(dǎo)致無(wú)法調(diào)用其他內(nèi)核模塊函數(shù)或變量,并且會(huì)報(bào)Unknown symbol
錯(cuò)誤。
Unknown symbol
說(shuō)明 有些函數(shù)不認(rèn)識(shí)(未定義)。
使用EXPORT_SYMBOL
宏導(dǎo)出函數(shù)及變量的情形下,不插入模塊myexportfunc
,直接插入模塊myusefunc
,會(huì)出現(xiàn)Unknown symbol in module
,原因在于此時(shí)內(nèi)核全局符號(hào)表中不存在publicFunc
和myOwnVar
符號(hào):
[root@localhost tmp]# lsmod | grep myexportfunc[root@localhost tmp]#[root@localhost tmp]# cd 24[root@localhost 24]# insmod myusefunc.koinsmod: ERROR: could not insert module myusefunc.ko: Unknown symbol in module #dmesg查看報(bào)錯(cuò)原因[root@localhost 24]# dmesg[1025403.614123] myusefunc: Unknown symbol publicFunc (err 0)[1025403.614144] myusefunc: Unknown symbol myOwnVar (err 0)
先插入模塊myexportfunc
,再插入模塊myusefunc
,通過(guò)dmesg
查看,此時(shí)模塊myexportfunc
中定義的publicFunc
和myOwnVar
符號(hào)成功被模塊myusefunc
使用:
[root@localhost tmp]# insmod ./myexportfunc.ko[root@localhost tmp]# cd 24[root@localhost 24]# lsMakefile modules.order Module.symvers myusefunc.c myusefunc.ko myusefunc.mod.c myusefunc.mod.o myusefunc.o[root@localhost 24]# insmod ./myusefunc.ko [root@localhost 24]# cat /proc/kallsyms | grep myOwnVarffffffffc0480030 r __ksymtab_myOwnVar [myexportfunc]ffffffffc04800e4 r __kstrtab_myOwnVar [myexportfunc]ffffffffc0481000 D myOwnVar [myexportfunc][root@localhost 24]#[root@localhost 24]# cat /proc/kallsyms | grep publicFuncffffffffc0480040 r __ksymtab_publicFunc [myexportfunc]ffffffffc04800ed r __kstrtab_publicFunc [myexportfunc]ffffffffc047f000 T publicFunc [myexportfunc][root@localhost 24]# dmesg[1025729.139236] Hello,this is my own module![1025739.579713] Hello,this is myusefunc module.[1025739.579717] This is public module and used for another modules.[1025739.579718] Linux kernel communication.
將myexportfunc.c
中的EXPORT_SYMBOL
注釋掉,myexportfunc
源碼文件修改如下:
/* myexportfunc.c */#include < linux/module.h >#include < linux/kernel.h >#include < linux/init.h >char myOwnVar[30]="Linux kernel communication.";static int __init myfunc_init(void){ printk("Hello,this is my own module!\\n"); return 0;}static void __exit myfunc_exit(void){ printk("Goodbye,this is my own clean module!\\n");}void publicFunc(void){ printk(KERN_INFO "This is public module and used for another modules.\\n");}module_init(myfunc_init);module_exit(myfunc_exit);//EXPORT_SYMBOL(publicFunc);//EXPORT_SYMBOL(myOwnVar);MODULE_DESCRIPTION("First Personel Module");MODULE_AUTHOR("Lebron James");MODULE_LICENSE("GPL");
重新通過(guò)make
命令編譯myexportfunc
和myusefunc
模塊,myusefunc
源文件、Makefile
文件及編譯方法同上,首先插入模塊myexportfunc
,此時(shí)全局符號(hào)表中的符號(hào)類(lèi)型為d和t,再插入模塊myusefunc
報(bào)錯(cuò),無(wú)法訪(fǎng)問(wèn)模塊myexportfunc
中的符號(hào)。
[root@localhost tmp]# insmod ./myexportfunc.ko[root@localhost tmp]# cat /proc/kallsyms | grep myOwnVarffffffffc0481000 d myOwnVar [myexportfunc] [root@localhost tmp]# cat /proc/kallsyms | grep publicFuncffffffffc047f00c t publicFunc [myexportfunc][root@localhost tmp]# cd 24 [root@localhost 24]# make"1st"make -C /lib/modules/4.18.0-394.el8.x86_64/build M=/tmp/tmp/24 modulesmake[1]: Entering directory "/usr/src/kernels/4.18.0-394.el8.x86_64""2nd" CC [M] /tmp/tmp/24/myusefunc.o Building modules, stage 2."2nd" MODPOST 1 modulesWARNING: "publicFunc" [/tmp/tmp/24/myusefunc.ko] undefined!WARNING: "myOwnVar" [/tmp/tmp/24/myusefunc.ko] undefined! CC /tmp/tmp/24/myusefunc.mod.o LD [M] /tmp/tmp/24/myusefunc.komake[1]: Leaving directory "/usr/src/kernels/4.18.0-394.el8.x86_64"[root@localhost 24]# insmod ./myusefunc.koinsmod: ERROR: could not insert module ./myusefunc.ko: Unknown symbol in module [root@localhost 24]# dmesg[1027498.802299] Goodbye this is myusefunc module.[1027502.210478] Goodbye,this is my own clean module![1027507.431371] Hello,this is my own module![1027519.232574] myusefunc: Unknown symbol publicFunc (err 0)[1027519.232594] myusefunc: Unknown symbol myOwnVar (err 0)
原因在于,模塊類(lèi)型為小寫(xiě)字母d和t表示局部引用,定義在Data
和Text
,只能在模塊內(nèi)訪(fǎng)問(wèn)。模塊類(lèi)型為大寫(xiě)字母D和T表示全局引用,可以在模塊外訪(fǎng)問(wèn),其他類(lèi)型類(lèi)似。
使用EXPORT_SYMBOL_GPL
導(dǎo)出myexportfunc.c
文件中的publicFunc
函數(shù),修改myexportfunc.c
源文件如下:
/* myexportfunc.c */#include < linux/module.h >#include < linux/kernel.h >#include < linux/init.h >char myOwnVar[30]="Linux kernel communication.";static int __init myfunc_init(void){ printk("Hello,this is my own module!\\n"); return 0;}static void __exit myfunc_exit(void){ printk("Goodbye,this is my own clean module!\\n");}void publicFunc(void){ printk(KERN_INFO "This is public module and used for another modules.\\n");}module_init(myfunc_init);module_exit(myfunc_exit);EXPORT_SYMBOL_GPL(publicFunc);EXPORT_SYMBOL(myOwnVar);MODULE_DESCRIPTION("First Personel Module");MODULE_AUTHOR("Lebron James");MODULE_LICENSE("GPL");
將myusefunc.c
中的MODULE_LICENSE("GPL")
注釋掉,myusefunc.c
源碼文件修改如下:
/* myusefunc.c */#include < linux/init.h >#include < linux/module.h >extern void publicFunc(void);extern char myOwnVar[30];void showVar(void);static int __init hello_init(void){ printk(KERN_INFO "Hello,this is myusefunc module.\\n"); publicFunc(); showVar(); return 0;}static void __exit hello_exit(void){ printk(KERN_INFO "Goodbye this is myusefunc module.\\n");}void showVar(void){ printk(KERN_INFO "%s\\n", myOwnVar);}module_init(hello_init);module_exit(hello_exit);//MODULE_LICENSE("GPL");
兩個(gè)模塊的Makefile
文件均同上,分別通過(guò)make命令編譯myexportfunc.c
和myusefunc.c
文件,之后使用insmod
命令先后插入兩個(gè)模塊,如下:
[root@localhost tmp]# insmod ./myexportfunc.ko[root@localhost tmp]# cd 24[root@localhost 24]# make"1st"make -C /lib/modules/4.18.0-394.el8.x86_64/build M=/tmp/tmp/24 modulesmake[1]: Entering directory "/usr/src/kernels/4.18.0-394.el8.x86_64""2nd" CC [M] /tmp/tmp/24/myusefunc.o Building modules, stage 2."2nd" MODPOST 1 modulesWARNING: modpost: missing MODULE_LICENSE() in /tmp/tmp/24/myusefunc.osee include/linux/module.h for more information CC /tmp/tmp/24/myusefunc.mod.o LD [M] /tmp/tmp/24/myusefunc.komake[1]: Leaving directory "/usr/src/kernels/4.18.0-394.el8.x86_64"[root@localhost 24]#[root@localhost 24]# insmod ./myusefunc.koinsmod: ERROR: could not insert module ./myusefunc.ko: Unknown symbol in module[root@localhost 24]# dmesg[1030402.772230] Hello,this is my own module![1030428.231104] myusefunc: Unknown symbol publicFunc (err 0)
通過(guò)上面結(jié)果可知,在被調(diào)用模塊使用EXPORT_SYMBOL_GPL
宏導(dǎo)出函數(shù)或變量后,調(diào)用該函數(shù)或變量的調(diào)用模塊必須包含MODULE_LICENSE("GPL")
宏,否則,在加載該模塊時(shí),將會(huì)報(bào)Unknown symbol
錯(cuò)誤。即只有包含GPL許可權(quán)的模塊才能調(diào)用EXPORT_SYMBOL_GPL
宏導(dǎo)出的符號(hào)。
License(許可證):
Linux是一款免費(fèi)的操作系統(tǒng),采用了GPL協(xié)議,允許用戶(hù)可以任意修改其源代碼。 GPL協(xié)議的主要內(nèi)容是軟件產(chǎn)品中即使使用了某個(gè)GPL協(xié)議產(chǎn)品提供的庫(kù), 衍生出一個(gè)新產(chǎn)品,該軟件產(chǎn)品都必須采用GPL協(xié)議,即必須是開(kāi)源和免費(fèi)使用的, 可見(jiàn)GPL協(xié)議具有傳染性。因此,我們可以在Linux使用各種各樣的免費(fèi)軟件。 在以后學(xué)習(xí)Linux的過(guò)程中,可能會(huì)發(fā)現(xiàn)我們安裝任何一款軟件,從來(lái)沒(méi)有30天試用期或者是要求輸入激活碼的。
內(nèi)核模塊許可證有
“GPL”,“GPL v2”,“GPL and additional rights”,“Dual SD/GPL”,“Dual MPL/GPL”,“Proprietary”
。
如果你的模塊需要輸出符號(hào)給其他模塊使用,符號(hào)必須在模塊文件的全局部分輸出, 在任何函數(shù)之外, 因?yàn)楹甓x擴(kuò)展成一個(gè)特殊用途的并被期望是全局存取的變量的聲明。 這個(gè)變量存儲(chǔ)于模塊的一個(gè)特殊的可執(zhí)行部分( 一個(gè) "ELF 段" ), 內(nèi)核通過(guò)這個(gè)部分在加載時(shí)找到模塊輸出的變量。編譯生成ko模塊之后,用insmod
命令加載此模塊到內(nèi)核。這個(gè)程序加載模塊的代碼段和數(shù)據(jù)段到內(nèi)核。
使用你的模塊輸出符號(hào)的其他模塊同樣通過(guò)insmod加載到內(nèi)核,insmod
在加載的過(guò)程中使用公共內(nèi)核符號(hào)表來(lái)解析模塊中未定義的符號(hào)(即通過(guò)extern聲明的符號(hào)),公共內(nèi)核符號(hào)表中包含了所有的全局內(nèi)核項(xiàng)(即函數(shù)和變量)的地址,這是實(shí)現(xiàn)模塊化驅(qū)動(dòng)程序所必需的。
同時(shí)也可以導(dǎo)出自身模塊中的任何內(nèi)核符號(hào)到公共內(nèi)核符號(hào)表,如圖:
通常情況下,模塊只需實(shí)現(xiàn)自己的功能,而無(wú)需導(dǎo)出任何符號(hào)。但是,如果其他模塊需要從某個(gè)模塊中獲得好處時(shí),我們也可以導(dǎo)出符號(hào)。
驅(qū)動(dòng)也是存在于內(nèi)核空間的,它的每一個(gè)函數(shù)每一個(gè)變量都會(huì)有對(duì)應(yīng)的符號(hào),這部分符號(hào)也可以稱(chēng)作內(nèi)核符號(hào),它們不導(dǎo)出(EXPORT_SYMBOL
)就只能為自身所用,導(dǎo)出后就可以成為公用,對(duì)于導(dǎo)出的那部分內(nèi)核符號(hào)就是我們常說(shuō)的內(nèi)核符號(hào)表。
EXPORT_SYMBOL使用方法:
在模塊函數(shù)定義之后使用EXPORT_SYMBOL
(函數(shù)名);在調(diào)用該函數(shù)的模塊中使用extern
對(duì)之聲明;首先加載定義該函數(shù)的模塊,再加載調(diào)用該函數(shù)的模塊?!灸K加載順序的前后要求,一般就是依賴(lài)于符號(hào)調(diào)用】
insmod
的時(shí)候并不是所有的函數(shù)都得到內(nèi)核符號(hào)表去尋找對(duì)應(yīng)的符號(hào),每一個(gè)驅(qū)動(dòng)在自己分配的空間里也會(huì)存儲(chǔ)一份符號(hào)表,里面有關(guān)于這個(gè)驅(qū)動(dòng)里使用到的變量以及函數(shù)的一些符號(hào),首先驅(qū)動(dòng)會(huì)在這里面找,如果發(fā)現(xiàn)找不到就會(huì)去公共內(nèi)核符號(hào)表中搜索,搜索到了則該模塊加載成功,搜索不到則該模塊加載失敗。
內(nèi)核默認(rèn)情況下,是不會(huì)在模塊加載后把模塊中的非靜態(tài)全局變量以及非靜態(tài)函數(shù)自動(dòng)導(dǎo)出到內(nèi)核符號(hào)表中的,需要顯式調(diào)用宏EXPORT_SYMBOL
才能導(dǎo)出。對(duì)于一個(gè)模塊來(lái)講,如果僅依靠自身就可以實(shí)現(xiàn)自已的功能,那么可以不需要要導(dǎo)出任何符號(hào),只有其他模塊中需要使用到該模塊提供的函數(shù)時(shí),就必須要進(jìn)行導(dǎo)出操作。
標(biāo)簽: