【发布时间】:2015-01-22 16:29:46
【问题描述】:
在 Linux 中,如果设备驱动程序被构建为可加载的内核模块,那么在插入设备驱动程序内核模块时,内核将调用设备驱动程序的 init 函数,如module_init() 宏所指出的那样。
这对于静态编译到内核中的设备驱动程序如何工作?他们的init函数是怎么调用的?
【问题讨论】:
标签: linux operating-system linux-kernel linux-device-driver
在 Linux 中,如果设备驱动程序被构建为可加载的内核模块,那么在插入设备驱动程序内核模块时,内核将调用设备驱动程序的 init 函数,如module_init() 宏所指出的那样。
这对于静态编译到内核中的设备驱动程序如何工作?他们的init函数是怎么调用的?
【问题讨论】:
标签: linux operating-system linux-kernel linux-device-driver
内置驱动程序的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”将导致每个 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
【讨论】:
do_initcalls() in init/main.c 通常发生在大部分硬件初始化之后。内核代码很少被结构化来调用驱动程序,除非它是ops 函数或其他模块接口入口点。您真正要问的是哪种“电话”?
What kind of "call" are you really asking about?为此我想这样提出问题:假设我有3种switch_dev类型的设备(它们的状态需要导出到用户空间)。在这里,设备 1 调用 device_initcall(提供了使对象 switch_dev->kobj 可用于其他 2 个设备的例程)。其他两个设备使用普通的 module_init。我的兴趣是将 device_initcall 例程移动到板文件并为所有 3 使用 module_init。如何做到这一点?
由内核 init.h 中的 cmets 指定
"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(注意:仅适用于静态编译的模块)。
【讨论】: