以module_init(Demo_init);為例
定義文件:
includelinuxinit.h
#define module_init(x)?__initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn)?__define_initcall("6",fn,6)
#define __define_initcall(level,fn,id)
static initcall_t __initcall_##fn##id __attribute_used__
__attribute__((__section__(".initcall" level ".init"))) = fn
展開為
static initcall_t __initcall_Demo_init6 __attribute_used__?__attribute__((__section__(".initcall6.init"))) =?Demo_init;
typedef int (*initcall_t)(void);
這里
typedef int (init_fnc_t)??(void); 定義一種函數(shù)類型
typedef int (*init_fnc_t) (void); 定義一種類型的函數(shù)指針
所以展開的宏定義就是定義名為__initcall_Demo_init6的函數(shù)指針
屬性有兩個:
1.
在gcc 3.4之前的編譯器被展開成__attribute__((unused))來禁止編譯器彈出有關(guān)函數(shù)沒有被用到的的警告信息
在gcc 3.4之后被展開成__attribute__((used))功能一樣
2.加載到段.initcall6.init,其地址為Demo_init的地址
段的分布順序在鏈接腳本中有
編譯內(nèi)核后,會有vmlinux.lds的打印信息,里面有各段位置
__initcall_start = .;
*(.initcallearly.init) __early_initcall_end = .;
*(.initcall0.init)
*(.initcall0s.init)
*(.initcall1.init)
*(.initcall1s.init)
*(.initcall2.init)
*(.initcall2s.init)
*(.initcall3.init)
*(.initcall3s.init)
*(.initcall4.init)
*(.initcall4s.init)
*(.initcall5.init)
*(.initcall5s.init)
*(.initcallrootfs.init)
*(.initcall6.init)
*(.initcall6s.init)
*(.initcall7.init)
*(.initcall7s.init)
__initcall_end = .;
當(dāng)insmod的時候,內(nèi)核從initcall6.init段中讀取到驅(qū)動入口地址,然后跳轉(zhuǎn)到該地址去執(zhí)行入口函數(shù),
一般入口函數(shù)會進行注冊驅(qū)動,例如
register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
usb_register(struct usb_driver * driver)
spi_register_driver(struct spi_driver * sdrv)
等等注冊函數(shù),再依次調(diào)用相應(yīng)設(shè)備結(jié)構(gòu)體中的ioctl或者直接調(diào)用file_operations結(jié)構(gòu)體
?
評論
查看更多