设备驱动-模块-module_init宏解析
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