在Linux中,如果设备驱动程序是作为可加载内核模块构build的,那么在插入设备驱动程序内核模块时,内核调用module_init()
macros指出的设备驱动程序的init函数。
这是如何工作的静态编译到内核的设备驱动程序? 他们的init函数是如何调用的?
内置驱动程序的init例程仍然可以使用module_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. */
我假设设备驱动程序的这些小节与Linux内核源代码树的drivers
目录中的子目录相对应,并且链接顺序记录在drivers
中每个子目录的built-in.o文件中。 因此,在内核启动期间,每个内置驱动程序的init例程最终由init/main.c
do_initcalls()
执行。
设备驱动程序的init例程负责探测系统,以确认硬件设备是否存在。 探测失败时,驱动程序不应分配任何资源或注册任何设备。
更新 :
在内核命令行上传递选项“initcall_debug”将导致定时信息被打印到每个初始调用的控制台。 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
参考: http : //elinux.org/Initcall_Debug
正如内核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( 注意:仅适用于静态编译的模块 )。