Linux 如何确定模块 init 调用的顺序? [英] How does Linux determine the order of module init calls?

查看:20
本文介绍了Linux 如何确定模块 init 调用的顺序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有 SPI 闪存的设备,我想在该闪存设备上使用 UBIFS 文件系统作为我的 rootfs.我面临的问题是 UBI 模块在 SPI 模块初始化之前初始化.因此,当 UBI 加载时,它无法连接到我告诉它的 UBI 设备(通过内核命令行),因此没有 rootfs.下面的控制台输出说明了这一点.

I have a device with SPI flash storage I'd like to use an UBIFS filesystem on that flash device as my rootfs. The problem I'm facing is that the UBI module initializes before the SPI module initializes. Because of this, when UBI loads, it cannot attach to the UBI device that I've told it to (via the kernel command line), so there is no rootfs. The console output below illustrates this.

我已经深入研究了源代码,看到 init/main.c 有一个 do_initcalls() 函数,它只调用函数指针列表.这些函数指针指向内核内置模块的所有 module_init() 函数.这些函数指针被放置在内核二进制文件的一个特殊部分,所以这个顺序是在编译时选择的.但是,我还没有弄清楚该顺序是如何确定的.

I've been diving into the source enough to see that init/main.c has a do_initcalls() function that simply calls a list of function pointers. Those function pointers point to the all the module_init() functions of the modules that are built-in to the kernel. Those function pointers are placed in a special section in the kernel binary, so this order is chosen at compile-time. However, I haven't yet figured out how that order is determined.

    [    0.482500] UBI error: ubi_init: UBI error: cannot initialize UBI, error -19
    [    0.492500] atmel_spi atmel_spi.0: Using dma0chan0 (tx) and  dma0chan1 (rx) for DMA transfers
    [    0.500000] atmel_spi atmel_spi.0: Atmel SPI Controller at 0xf0000000 (irq 13)
    [    0.507500] m25p80 spi0.1: mx25l25635e (32768 Kbytes)
    [    0.512500] Creating 7 MTD partitions on "jedec_flash":
    [    0.520000] 0x000000000000-0x000000020000 : "loader"
    [    0.527500] 0x000000020000-0x000000060000 : "u-boot"
    [    0.537500] 0x000000060000-0x000000080000 : "u-boot-env"
    [    0.547500] 0x000000080000-0x000000280000 : "kernel0"
    [    0.557500] 0x000000280000-0x000000480000 : "kernel1"
    [    0.567500] 0x000000480000-0x000001240000 : "fs"
    [    0.575000] 0x000001240000-0x000002000000 : "play"
    [    0.590000] AT91SAM9 Watchdog enabled (heartbeat=15 sec, nowayout=0)
    [    0.607500] TCP cubic registered
    [    0.615000] VFS: Cannot open root device "ubi0:root0" or unknown-block(0,0)
    [    0.622500] Please append a correct "root=" boot option; here are the available partitions:
    [    0.630000] 1f00             128 mtdblock0  (driver?)
    [    0.635000] 1f01             256 mtdblock1  (driver?)
    [    0.640000] 1f02             128 mtdblock2  (driver?)
    [    0.645000] 1f03            2048 mtdblock3  (driver?)
    [    0.650000] 1f04            2048 mtdblock4  (driver?)
    [    0.655000] 1f05           14080 mtdblock5  (driver?)
    [    0.660000] 1f06           14080 mtdblock6  (driver?)
    [    0.665000] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

推荐答案

由内核初始化的模块的 init 例程(当它们被静态链接到内核中)被包装在一个 initcall() 宏中,该宏表示在启动序列中时,它们应该运行.

The init routines for a module that is initialized by the kernel (when they are statically linked into the kernel) are wrapped in an initcall() macro that indicates when in the startup sequence they should be run.

请参阅包含文件:include/linux/init.h 以获取宏列表及其顺序.

See the include file: include/linux/init.h for a list of the macros and their ordering.

那里指定的顺序是:

  • early_initcall
  • pure_initcall
  • core_initcall
  • postcore_initcall
  • arch_initcall
  • subsys_initcall
  • fs_initcall
  • rootfs_initcall
  • device_initcall
  • late_initcall

其中大部分都有一个initcall_sync() 阶段,用于等待完成该阶段中的所有模块初始化例程.宏用于构建每个阶段的函数指针表,依次调用do_initcalls().

Most of these have an "initcall_sync() phase, which is used to wait for completion of all module initialization routines within that phase. The macros are used to build a table of function pointers for each phase, which are called in sequence by do_initcalls().

如果使用module_init()"来包装初始化函数,则默认情况下,initcall() 将调用置于初始化的设备"阶段.在该阶段内,项目按链接顺序排序.这意味着该表是按照遇到的函数的顺序创建的通过链接器.

If "module_init()" is used to wrap the initialization function, then by default initcall() puts the call in the "device" phase of initialization. Within that phase, the items are ordered by link order. This means that the table is created by the order of the functions as they are encountered by the linker.

您可以将初始化移动到更早的阶段,通过更改哪个initcall 宏封装了模块初始化函数,但要小心,因为各个模块之间存在顺序相关性.另一种方法更改初始化顺序(在一个阶段内)将调整链接内核中模块的顺序.

You may be able to move an initialization to an earlier phase, by changing which initcall macro wraps the modules initialization function, but be careful because there are order dependencies between the various modules. Another method of changing the initialization order (within a phase) would be to adjust the link order for the module in the kernel.

这篇关于Linux 如何确定模块 init 调用的顺序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆