本文中分析linux kernel 中的比較重要的,在開發驅動過程中會碰到,萬丈高樓平地起嘛。
文中內容條目清單:
1、數據類型相關
2、module_init和module_exit
3、EXPORT_SYMBOL和EXPORT_SYMBOLGPL
具體內容:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1、數據類型相關
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LCD控制器初始化過程中有個數據轉換
#define PICOS2KHZ(a) (1000000000UL/(a))
#define KHZ2PICOS(a) (1000000000UL/(a))
其中“UL”代表含義:1不加UL的話, 默認地它的類型是int, 這是一個有符號類型,而內核中爲了限制說這個值是個無符號的值,就明確地標出UL,表示C標準中關於一個整數是如何決定它類型的優先級表:
後綴 類型
無 int
long int
long long int
u or U unsigned int
unsigned long int
unsigned long long int
l or L long int
long long int
ul or UL unsigned long int
unsigned long long int
ll or LL long long int
ull or ULL
unsigned long long int
注意, 在上表中,比如對u, 則編譯器依次判斷常量值是否在unsigned int表示範圍內, 是類型就是unsigned int,
否則, 是否在unsigned long int表示範圍內, 是類型就是unsigned long int,否則, 是否在unsigned long long int表示範圍內, 是類型就是unsigned long long int.其餘一樣道理.
更詳細請參考C99標準:
ISO/IEC 9899:1999(草案) 6.4.4.1 Integer constants
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
module_init
module_exit
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
裝載時(insmod),調用module_init(...)中說明的函數。卸載時(rmmod),調用module_exit(...)中說明的函數。
lsmod 查看被加載的驅動列表
數據聲明@kernel\include\linux\init.h
#define __init __section(.init.text) __cold notrace
#define __initdata __section(.init.data)
#define __initconst __section(.init.rodata)
#define __exitdata __section(.exit.data)
#define __exit_call __used __section(.exitcall.exit)
#define __exit __section(.exit.text) __exitused __cold
分析module_init,如果某驅動想以func作爲該驅動的入口,則可以如下聲明:module_init(func);被上面的宏處理過後,變成__initcall_func6 __used加入到內核映像的".initcall"區。內核的加載的時候,會搜索".initcall"中的所有條目,並按優先級加載它們,他的執行優先級爲普通的6。值越小越優先被加載,被聲明爲pure_initcall的最先加載。
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
如果某驅動想以func作爲該驅動的入口,則可以如下聲明:module_init(func);被上面的宏處理過後,變成__initcall_func6 __used加入到內核映像的".initcall"區。內核的加載的時候,會搜索".initcall"中的所有條目,並按優先級加載它們,普通驅動程序的優先級是6。其它模塊優先級列出如下:值越小,越先加載。
#define module_exit(x) __exitcall(x);
#define early_initcall(fn) module_init(fn)
#define core_initcall(fn) module_init(fn)
#define postcore_initcall(fn) module_init(fn)
#define arch_initcall(fn) module_init(fn)
#define subsys_initcall(fn) module_init(fn)
#define fs_initcall(fn) module_init(fn)
#define device_initcall(fn) module_init(fn)
#define late_initcall(fn) module_init(fn)
linux kernel中有很大一部分代碼是設備驅動代碼,這些驅動代碼都有初始化和反初始化函數,這些代碼一般都只執行一次,爲了有更有效的利用內存,這些代碼所佔用的內存可以釋放出來。
linux就是這樣做的,對只需要初始化運行一次的函數都加上__init屬性。在kernel初始化後期,釋放所有這些函數代碼所佔的內存空間
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EXPORT_SYMBOL
EXPORT_SYMBOL_GPL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- EXPORT_SYMBOL(符號名);
- EXPORT_SYMBOL_GPL(符號名)
用法:就是在一個文件中定義符號,這個用EXPORT_SYMBOL或EXPORT_SYMBOL_GPL聲明,可以其他函數中通過extern 聲明調用的對應符號名,那麼在此函數中即可被調用。