module_init
這個宏定義在
include/linux/module.h
module 的含義,即 模塊; 有兩類: builtin 的模塊 (存在在 Image 中) 或者 獨立的模塊(存在在 xx.ko 中)
根據當前在編譯 builtin 還是 編譯 獨立模塊, module 宏有不同的 定義
80#ifndef MODULE 89#define module_init(x) __initcall(x); 103#else /* MODULE */ 131#define module_init(initfn) \ 132 static inline initcall_t __maybe_unused __inittest(void) \ 133 { return initfn; } \ 134 int init_module(void) __copy(initfn) __attribute__((alias(#initfn))); 142#endif
MODULE 宏從哪兒控制?
如果一個 xx.c 文件在 Kconfig 中配置的是 obj-m += xx.c ,那么編譯這個 xx.c 文件時,就會 定義MODULE 宏。
這個機制是 頂層的 Makefile 和 script/Makefile.lib 配合實現的
Makefile
503KBUILD_AFLAGS_MODULE := -DMODULE
504KBUILD_CFLAGS_MODULE := -DMODULE
scripts/Makefile.lib
188part-of-module = $(if $(filter $(basename $@).o, $(real-obj-m)),y) 189quiet_modtag = $(if $(part-of-module),[M], ) 190 191modkern_cflags = \ 192 $(if $(part-of-module), \ 193 $(KBUILD_CFLAGS_MODULE) $(CFLAGS_MODULE), \ 194 $(KBUILD_CFLAGS_KERNEL) $(CFLAGS_KERNEL) $(modfile_flags))
$@ 表示正在編譯的目標,如果是 module 的一部分,則使用 KBUILD_CFLAGS_MODULE 作為 cflags ,即 -DMODULE 被引入 gcc 命令行 ;
作為 builtin 模塊時的module_init
就是 __initcall(x);
include/linux/init.h
195#define ___define_initcall(fn, id, __sec) \ 196 static initcall_t __initcall_##fn##id __used \ 197 __attribute__((__section__(#__sec ".init"))) = fn; 200#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id) 207#define early_initcall(fn) __define_initcall(fn, early) 216#define pure_initcall(fn) __define_initcall(fn, 0) 218#define core_initcall(fn) __define_initcall(fn, 1) 219#define core_initcall_sync(fn) __define_initcall(fn, 1s) 220#define postcore_initcall(fn) __define_initcall(fn, 2) 221#define postcore_initcall_sync(fn) __define_initcall(fn, 2s) 222#define arch_initcall(fn) __define_initcall(fn, 3) 223#define arch_initcall_sync(fn) __define_initcall(fn, 3s) 224#define subsys_initcall(fn) __define_initcall(fn, 4) 225#define subsys_initcall_sync(fn) __define_initcall(fn, 4s) 226#define fs_initcall(fn) __define_initcall(fn, 5) 227#define fs_initcall_sync(fn) __define_initcall(fn, 5s) 228#define rootfs_initcall(fn) __define_initcall(fn, rootfs) 229#define device_initcall(fn) __define_initcall(fn, 6) 230#define device_initcall_sync(fn) __define_initcall(fn, 6s) 231#define late_initcall(fn) __define_initcall(fn, 7) 232#define late_initcall_sync(fn) __define_initcall(fn, 7s) 234#define __initcall(fn) device_initcall(fn)
紅色 標識 出 __initcall 用到的部分 ;
編譯:
234 行 - __initcall 其實就是 device_initcall
229 - device_initcall 是 __define_initcall(fn, 6 ) // 6 標識 id ;
200 - __define_initcall(fn, id) 是 __define_initcall(fn, id, .initcall##id ) // 即 __define_initcall(fn, 6 , initcall6 ) ;
195 - __define_initcall(fn , id, sec) 做出定義
static initcall_t __initcall_fn6 __used __attribute__((__section__( initcall6.init )) = fn ;
只需要將上面的 fn 替換為 module_init(xx) 中填入的 xx 即可。
示例: module_init (i2c_init) ;
static int __initcall_i2c_init6 __used __attribute__((__section__( initcall6.init )) = i2c_init ;
在 initcall6.init 這個 section 里面定義了一個函數指針變量 __initcall_i2c_init6 , 指針賦值為 i2c_init 函數地址;
調用
do_basic_setup -> do_initcalls 里面會 遍歷 __initcall6.init 這個section 里面的函數指針,一個個調用 對應的函數; 即會調用到 i2c_init 函數 ;
作為 外部 xx.ko 模塊時的module_init
編譯
module_init(xx) 即是 希望 在 insmod xx.ko 時 調用 xx 函數;
131#define module_init(initfn) \ 132 static inline initcall_t __maybe_unused __inittest(void) \ 133 { return initfn; } \ 134 int init_module(void) __copy(initfn) __attribute__((alias(#initfn)));
132 定義 一個 static inline 的函數; 返回值 為 initcall_t 即 int ; 名稱為 __inittest ;
static inline int __inittest(){
return initfn ;
}
這個的關鍵 在於 檢查 返回值; 如果 initfn 返回值 不是 int , 就會打印錯誤信息;
GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type
133 行 - 為 initfn 定義一個別名函數 init_module ; 關於gcc alias 定義別名函數, 參考這兒
https://www.cnblogs.com/zhangzhiwei122/p/16125210.html
這樣,每個 xx.ko 里面肯定有一個 函數 init_module , 它是代碼中的 module_init(xx) 定義的函數的別名 ;
調用流程
insmod 調用 init_module 函數
/include/linux/module.h 有 module 結構體定義; 里面有 init 指針,指向 模塊的 init 函數;
361struct module { 362 enum module_state state; 423 /* Startup function. */ 424 int (*init)(void);
在 kernel/module.c 中 do_init_module 函數 會 觸發 mod->init 函數的調用。
3604static noinline int do_init_module(struct module *mod) 3605{ 3622 do_mod_ctors(mod); 3623 /* Start the module */ 3624 if (mod->init != NULL) 3625 ret = do_one_initcall(mod->init);
從 insertmod 到 do_init_module 的過程,參考:
模塊加載流程上
https://blog.csdn.net/lidan113lidan/article/details/119813256
模塊加載流程下
https://blog.csdn.net/lidan113lidan/article/details/119813552
do_one_initcall 就是 init/main.c 中定義的,之前在 do_initcalls 里面也使用的,調用 1 個 init 函數;
1207int __init_or_module do_one_initcall(initcall_t fn) 1208{ 1209 int count = preempt_count(); 1210 char msgbuf[64]; 1211 int ret; 1212 1213 if (initcall_blacklisted(fn)) 1214 return -EPERM; 1215 1216 do_trace_initcall_start(fn); 1217 ret = fn(); 1218 do_trace_initcall_finish(fn, ret); 1219 1220 msgbuf[0] = 0; 1221 1222 if (preempt_count() != count) { 1223 sprintf(msgbuf, "preemption imbalance "); 1224 preempt_count_set(count); 1225 } 1226 if (irqs_disabled()) { 1227 strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf)); 1228 local_irq_enable(); 1229 } 1230 WARN(msgbuf[0], "initcall %pS returned with %s\n", fn, msgbuf); 1231 1232 add_latent_entropy(); 1233 return ret; 1234}
下一篇:
設備驅動-i2c驅動-module_i2c_driver的使用
https://www.cnblogs.com/zhangzhiwei122/p/16125079.html