module_init()
在 Linux 中,如果设备驱动程序被构建为可加载的内核模块,那么在插入设备驱动程序内核模块时,内核会调用宏所指出的设备驱动程序的 init 函数。
这对于静态编译到内核中的设备驱动程序如何工作?他们的 init 函数是如何调用的?
module_init()
在 Linux 中,如果设备驱动程序被构建为可加载的内核模块,那么在插入设备驱动程序内核模块时,内核会调用宏所指出的设备驱动程序的 init 函数。
这对于静态编译到内核中的设备驱动程序如何工作?他们的 init 函数是如何调用的?
内置驱动程序的initmodule_init()
例程仍然可以使用宏来声明该入口点。或者device_initcall()
当驱动程序永远不会被编译为可加载模块时,驱动程序可以使用。或者要在引导序列的早期移动其初始化,驱动程序可以使用subsys_initcall()
.
在include/linux/init.h
调用这些init例程的顺序中描述为:
/* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*
* The `id' arg to __define_initcall() is needed so that multiple initcalls
* can point at the same handler without causing duplicate-symbol build errors.
*/
我假设设备驱动程序的这些小节对应于drivers
Linux 内核源代码树目录中的子目录,并且链接顺序记录在每个子目录的built-in.o文件中drivers
。因此,在内核启动期间,每个内置驱动程序的initdo_initcalls()
例程最终都由in执行init/main.c
。
设备驱动程序的init例程负责探测系统以验证硬件设备是否确实存在。当探测失败时,驱动程序不应分配任何资源或注册任何设备。
更新:
在内核命令行上传递选项“initcall_debug”将导致每次 initcall 的计时信息打印到控制台。initcalls 用于初始化静态链接的内核驱动程序和子系统,并为 Linux 引导过程贡献大量时间。输出如下所示:
calling tty_class_init+0x0/0x44 @ 1
initcall tty_class_init+0x0/0x44 returned 0 after 9765 usecs
calling spi_init+0x0/0x90 @ 1
initcall spi_init+0x0/0x90 returned 0 after 9765 usecs
如内核 init.h 中的注释所指定
“module_init() 将在 do_initcalls() 期间(如果是内置的)或在模块插入时(如果是模块)被调用。”
如果你查看 init.h 那么你会看到
定义 module_init(x) __initcall(x);
如果你仔细观察
定义 __initcall(fn) device_initcall(fn)
和
定义 device_initcall(fn) __define_initcall("6",fn,6)
所以基本上模块 init 只在启动时导致 initcall (注意:仅适用于静态编译的模块)。