歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> Linux 驅動開發之內核模塊開發(四)—— 符號表的導出

Linux 驅動開發之內核模塊開發(四)—— 符號表的導出

日期:2017/3/3 11:49:18   编辑:Linux技術
Linux內核頭文件提供了一個方便的方法用來管理符號的對模塊外部的可見性,因此減少了命名空間的污染(命名空間的名稱可能會與內核其他地方定義的名稱沖突),並且適當信息隱藏。 如果你的模塊需要輸出符號給其他模塊使用,應當使用下面的宏定義:
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name); //只適用於包含GPL許可權的模塊;
這兩個宏均用於將給定的符號導出到模塊外. _GPL版本的宏定義只能使符號對GPL許可的模塊可用。 符號必須在模塊文件的全局部分導出,不能在函數中導出,這是因為上述這兩個宏將被擴展成一個特殊用途的聲明,而該變量必須是全局的。這個變量存儲於模塊的一個特殊的可執行部分(一個"ELF段" ),在裝載時,內核通過這個段來尋找模塊導出的變量(感興趣的讀者可以看<linux/module.h>獲知更詳細的信息)。
一、宏定義EXPORT_SYMBOL分析
1、源碼
[cpp] view
plain copy





<include/linux/moudule.h>
…….
#ifndef MODULE_SYMBOL_PREFIX
#define MODULE_SYMBOL_PREFIX ""
#endif
…….
struct kernel_symbol //內核符號結構
{
unsignedlong value; //該符號在內存地址中的地址
constchar *name; //該符號的名稱
};
……
#define __EXPORT_SYMBOL(sym,sec) \
externtypeof(sym) sym; \
__CRC_SYMBOL(sym,sec) \
staticconst char __kstrtab_##sym[] \
__attribute__((section(“__ksymtab_strings”),aligned(1))) \
=MODULE_SYMBOL_PREFIX#sym; \
staticconst struct kernel_symbol __ksymtab_##sym \
__used \
__attribute__((section(“__ksymatab”sec),unused)) \
={(unsignedlong)&sym,_kstrab_#sym}
#define EXPORT_SYMBOL(sym) \
__EXPOTR_SYMBOL(sym,””)
#define EXPORT_SYMBOL_GPL(sym) \
__EXPOTR_SYMBOL(sym,”_gpl”)
#define EXPORT_SYMBOL(sym) \
__EXPOTR_SYMBOL(sym,”_gpl_future”)
在分析前,先了解如下相關知識:
1)#運算符,##運算符
通常在宏定義中使用#來創建字符串 #abc就表示字符串”abc”等。
##運算符稱為預處理器的粘合劑,用來替換粘合兩個不同的符號,
如:#define xName (n) x##n
則xName(4) 則變為x4
2)gcc的 __attribute__ 屬性:
__attribute__((section(“section_name”)))的作用是將指定的函數或變量放入到名為”section_name”的段中
__attribute__屬性添加可以在函數或變量定義的時候直接加入在定義語句中。
如:
int myvar__attribute__((section("mydata"))) = 0;
表示定義了整形變量myvar=0;並且將該變量存放到名為”mydata”的section中
關於gcc_attribute詳解可以參考:http://blog.sina.com.cn/s/blog_661314940100qujt.html
2、EXPORT_SYMBOL的作用是什麼?
EXPORT_SYMBOL標簽內定義的函數或者符號對全部內核代碼公開,不用修改內核代碼就可以在您的內核模塊中直接調用,即使用EXPORT_SYMBOL可以將一個函數以符號的方式導出給其他模塊使用。
這裡要和System.map做一下對比:System.map 中的是連接時的函數地址。連接完成以後,在2.6內核運行過程中,是不知道哪個符號在哪個地址的。
EXPORT_SYMBOL的符號,是把這些符號和對應的地址保存起來,在內核運行的過程中,可以找到這些符號對應的地址。而模塊在加載過程中,其本質就是能動態連接到內核,如果在模塊中引用了內核或其它模塊的符號,就要EXPORT_SYMBOL這些符號,這樣才能找到對應的地址連接。
二、 EXPORT_SYMBOL使用方法
第一、在模塊函數定義之後使用EXPORT_SYMBOL(函數名)
第二、在調用該函數的模塊中使用extern對之聲明
第三、首先加載定義該函數的模塊,再加載調用該函數的模塊
要調用別的模塊實現的函數接口和全局變量,就要導出符號 /usr/src/linux-headers-2.6.32-33-generic/Module.symvers
ABstatic int num =10;
static void show(void)
{
printk("%d \n",num);
}
EXPORT_SYMBOL(show);
extern void show(void);
函數A先將show() 函數導出,函數B 使用extern 對其聲明,要注意:
a -- 編譯a模塊後,要將 Module.symvers 拷貝到b模塊下
b -- 然後才能編譯b模塊
c -- 加載:先加載a模塊,再加載b模塊
d -- 卸載:先卸載b模塊,再卸載a模塊
三、示例
代碼a ,hello.c
[cpp] view
plain copy





#include <linux/module.h>
static int num =10;
static void show(void)
{
printk("show(),num = %d\n",num);
}
static int hello_init(void)
{
printk("hello_init");
return 0;
}
static void hello_exit(void)
{
printk("hello_exit \n");
}
EXPORT_SYMBOL(show);
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
代碼b show.c
[cpp] view
plain copy





#include <linux/module.h>
extern void show(void);
static int show_init(void)
{
printk("show_init");
show();
return 0;
}
static void show_exit(void)
{
printk("show_exit \n");
}
MODULE_LICENSE("GPL");
module_init(show_init);
module_exit(show_exit);<strong>
</strong>
編譯後加載模塊,卸載模塊,可以用 dmesg 查看內核打印信息。
Copyright © Linux教程網 All Rights Reserved