static int __init chg_init(void)
{
return platform_driver_register(&charger_driver);
}
static void __exit chg_exit(void)
{
platform_driver_unregister(&charger_driver);
}
late_initcall(chg_init);
module_exit(chg_exit);
MODULE_DESCRIPTION("Dummy Charger driver");
MODULE_LICENSE("GPL");
late_initcall
init的初始化内存分布
vmlinux.lds文件用来决定初始化所用的内存的分布
内存的分布可以用下图来说明:
__init用来标示的是初始化函数,在初始化后不会再调用
__initdata是初始化数据
__initparam是初始化参数,其他7个初始化宏就是初始化函数会用到的。
所有标示为__init的函数都会在链接的时候都会放在.init.text中,放得顺序是不确定的,与编译、链接顺序有关
同时所有__init函数都会在.initcallx.init中放一个函数指针,初始化的时候按照.initcall1.init->.initcall7.init的顺序初始化,.initcallx.init有不同的别名,在init.h中定义
late_initcall的定义
include\linux\init.h
在include\linux\init.h 可以看到 late_initcall 的定义,是放在了 .initcall7.init中,即第7个段。
而常言的module_init 是定义为了 device_initcall,即放在了第6个段中。
__define_initcall 在这里就不展开了,详情可以先看:
https://blog.youkuaiyun.com/weixin_47491758/article/details/125775025
late_initcall 和 module_init 的区别
前面有提到,module_init 的放在于第6个段执行,
而 late_initcall 是放在第7个段执行,
所以 late_initcall 的优先级最低,是最后执行的。
这里就涉及到了设备之间的依赖关系。
个人认为,使用 late_initcall 可以保证具有依赖关系的设备,可以先等依赖设备先加载好,再去加载该设备。
例如B设备的加载要依赖A设备,可以让A设备用 module_init加载,B设备使用late_initcall 加载。
参考
https://blog.youkuaiyun.com/Ivan804638781/article/details/88419376
https://blog.youkuaiyun.com/qq_39491794/article/details/80412630
驱动开发基础 -- module_init() 和 late_initcall() 的加载顺序
在模块加载的过程中,init头文件非常重要,它定义了module_init和xxx_initcall以及相应的
clearup函数,还决定了模块的加载顺序级别以及模块编译进内核和动态加载时module_init所做的不同的事情。
而vmlinux.lds文件用来决定初始化所用的内存的分布,我们看看内核初始化的内存分布:
-
.init.text : {
-
_sinittext = .;
-
*(.init.text)
-
_einittext = .;
-
}
-
.init.data : {
-
*(.init.data);
-
__vtop_table_begin = .;
-
*(.vtop_fixup);
-
__vtop_table_end = .;
-
__ptov_table_begin = .;
-
*(.ptov_fixup);
-
__ptov_table_end = .;
-
}
-
. = ALIGN(16);
-
__setup_start = .;
-
.init.setup : { *(.init.setup) }
-
__setup_end = .;
-
__initcall_start = .;
-
.initcall.init : {
-
*(.initcall1.init)
-
*(.initcall2.init)
-
*(.initcall3.init)
-
*(.initcall4.init)
-
*(.initcall5.init)
-
*(.initcall6.init)
-
*(.initcall7.init)
-
}
-
__initcall_end = .;
内存的分布可以用下图来说明:
其中__init用来标示的是初始化函数,在初始化后不会再调用,__initdata是初始化数据, __initparam是初始化参数,其他7个初始化宏就是初始化函数会用到的。所有标示为__init的函数都会在链接的时候都会放在.init.text中,放得顺序是不确定的,与编译、链接顺序有关,同时所有__init函数都会在.initcallx.init中放一个函数指针,初始化的时候按照.initcall1.init->.initcall7.init的顺序初始化,.initcallx.init有不同的别名,在init.h中定义:
以下的定义是built-in的定义,即将模