msleep/mdelay以及early_initcall调用时机

本文详细介绍了Linux内核启动过程中不同阶段的初始化调用机制,包括early_initcall、pure_initcall等多个级别的调用顺序及其实现原理。这些知识点对于理解Linux内核启动流程及其模块加载顺序至关重要。

要尽量使用sleep函数(msleep等)而不是delay函数(mdelay等),两者对系统性能以及功耗的影响相差很远,特别是活动进程很少的时候。

 

看了一下kernel启动时early_initcall等的调用时机,early_initcall也是在init进程里调用的,稍早于pure_initcall、core_initcall等,见下面:
在start_kernel函数的最后调用rest_init
在rest_init里创建kernel_init内核线程:kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
在内核线程kernel_init里,do_pre_smp_initcalls()负责调用early_initcall的函数,do_basic_setup()负责调用所有从pure_initcall到late_initcall_sync的所有函数。代码是:
...
    do_pre_smp_initcalls();
    smp_init();
    sched_init_smp();
    cpuset_init_smp();
    do_basic_setup();
...
可看出do_pre_smp_initcalls与do_basic_setup很近。


#define __define_initcall(level,fn,id) /
 static initcall_t __initcall_##fn##id __used /
 __attribute__((__section__(".initcall" level ".init"))) = fn

/*
 * Early initcalls run before initializing SMP.
 *
 * Only for built-in code, not modules.
 */
#define early_initcall(fn)  __define_initcall("early",fn,early)

/*
 * A "pure" initcall has no dependencies on anything else, and purely
 * initializes variables that couldn't be statically initialized.
 *
 * This only exists for built-in code, not for modules.
 */
#define pure_initcall(fn)  __define_initcall("0",fn,0)

#define core_initcall(fn)  __define_initcall("1",fn,1)
#define core_initcall_sync(fn)  __define_initcall("1s",fn,1s)
#define postcore_initcall(fn)  __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn)  __define_initcall("3",fn,3)
#define arch_initcall_sync(fn)  __define_initcall("3s",fn,3s)
#define subsys_initcall(fn)  __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn)   __define_initcall("5",fn,5)
#define fs_initcall_sync(fn)  __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn)  __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn)  __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn)  __define_initcall("7",fn,7)
#define late_initcall_sync(fn)  __define_initcall("7s",fn,7s)

 

root@4f90412002c9:/eap/PLATFORM/build# make BOARD_TYPE=en7529_EAP61xGPv1_common PID=eap WEB_PAGES_ID=VI_2.0 cpu_timer.ko Makefile.flags:54: "userspace cpu bit width: 64:, 32:y" Makefile.flags:61: "kernel cpu bit width: 64:, 32:y" Makefile:41: "product_configs/en7529_EAP61xGPv1_common/spec/CT/custom.mk not found!" ecn/Makefile.sdk:33: "/eap/PLATFORM/build/../build/product_configs/en7529_EAP61xGPv1_common/config.sdk Checked" ecn/Makefile.sdk:35: "/eap/PLATFORM/build/../build/product_configs/en7529_EAP61xGPv1_common/config.bba Checked" ecn/Makefile.sdk:87: "BOOT_LED_CONF" /eap/PLATFORM/build/../sdk/ecn/en7529/bootloader/Uboot/u-boot-2014.04-rc1/include/tp_led_gpio_def.h ecn/Makefile.sdk:88: "UBOOT_DIR" /eap/PLATFORM/build/../sdk/ecn/en7529/bootloader/Uboot make[1]: Entering directory &#39;/eap/PLATFORM/sdk/ecn/en7529/linux-4.4.115&#39; CC [M] /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.o LD [M] /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpu_timer.o Building modules, stage 2. MODPOST 1 modules CC /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpu_timer.mod.o LD [M] /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpu_timer.ko make[1]: Leaving directory &#39;/eap/PLATFORM/sdk/ecn/en7529/linux-4.4.115&#39; built and installed <cpu_timer.ko> at Tue Oct 14 11:41:44 CST 2025. root@4f90412002c9:/eap/PLATFORM/build# make BOARD_TYPE=en7529_EAP61xGPv1_common PID=eap WEB_PAGES_ID=VI_2.0 cpu_timer.ko Makefile.flags:54: "userspace cpu bit width: 64:, 32:y" Makefile.flags:61: "kernel cpu bit width: 64:, 32:y" Makefile:41: "product_configs/en7529_EAP61xGPv1_common/spec/CT/custom.mk not found!" ecn/Makefile.sdk:33: "/eap/PLATFORM/build/../build/product_configs/en7529_EAP61xGPv1_common/config.sdk Checked" ecn/Makefile.sdk:35: "/eap/PLATFORM/build/../build/product_configs/en7529_EAP61xGPv1_common/config.bba Checked" ecn/Makefile.sdk:87: "BOOT_LED_CONF" /eap/PLATFORM/build/../sdk/ecn/en7529/bootloader/Uboot/u-boot-2014.04-rc1/include/tp_led_gpio_def.h ecn/Makefile.sdk:88: "UBOOT_DIR" /eap/PLATFORM/build/../sdk/ecn/en7529/bootloader/Uboot make[1]: Entering directory &#39;/eap/PLATFORM/sdk/ecn/en7529/linux-4.4.115&#39; CC [M] /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.o /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.c:18:23: error: unknown type name &#39;useconds_t&#39; static void busy_wait(useconds_t usecs) ^ /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.c: In function &#39;cpu0_stress_func&#39;: /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.c:45:13: warning: implicit declaration of function &#39;busy_wait&#39; [-Wimplicit-function-declaration] busy_wait(busy_us); ^ scripts/Makefile.build:269: recipe for target &#39;/eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.o&#39; failed make[2]: *** [/eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.o] Error 1 Makefile:1439: recipe for target &#39;_module_/eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer&#39; failed make[1]: *** [_module_/eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer] Error 2 make[1]: Leaving directory &#39;/eap/PLATFORM/sdk/ecn/en7529/linux-4.4.115&#39; Makefile.smb_priv:626: recipe for target &#39;cpu_timer.ko_build&#39; fai
10-15
<think>我们正在解决Bazel构建内核模块时的编译错误。错误信息是“error executing KernelModule command Exit 2”。这个错误通常发生在构建过程中,可能由多种原因引起,比如编译选项配置错误、依赖缺失、环境问题等。 由于Bazel用于构建内核模块相对较少见,我们需要逐步排查。以下步骤基于常规内核模块构建和Bazel使用经验: ### 1. **检查Bazel构建文件(BUILD)** 确保为内核模块正确配置了构建规则。例如,使用`kernel_module`规则时,需要指定内核源代码路径和正确的配置(如`.config`文件)。示例规则: ```python kernel_module( name = "mymodule", srcs = ["mymodule.c"], kernel = "//path/to/kernel:kernel", # 指向内核构建目标 ) ``` ### 2. **确认内核头文件可用** 构建内核模块需要内核头文件。如果使用预编译的内核头文件,请确保路径正确。如果从源代码构建,确保内核已配置并准备好模块构建。 ### 3. **检查Bazel输出日志** 运行Bazel时添加`--subcommands`选项,查看详细命令执行过程: ```bash bazel build --subcommands //your:target ``` 观察失败的具体命令(可能是`make`命令或脚本),并检查其输出错误。 ### 4. **环境变量和工具链** 确保构建环境包含必要的工具链(如gcc、make)。Bazel可能使用自定义工具链,检查是否包含内核构建所需工具。 ### 5. **内核配置** 内核模块构建需要正确的内核配置(如`CONFIG_MODULES=y`)。确保使用的内核配置正确。 ### 6. **权限问题** 如果构建过程中涉及文件创建,确保Bazel有足够的权限(特别是在沙盒环境中)。 ### 7. **依赖项** 如果模块依赖其他内核头文件或模块,确保这些依赖在Bazel中正确声明。 ### 示例调试步骤 假设错误日志显示`make`命令失败,我们可以尝试手动执行相同命令(从Bazel输出中复制)来调试。 #### 常见错误场景: - **缺少头文件**:检查内核头文件路径是否包含在编译器的`-I`选项中。 - **符号未定义**:可能缺少内核导出符号,检查模块代码是否正确使用`EXPORT_SYMBOL`。 - **编译器选项不兼容**:如使用过高版本的gcc编译旧内核,可能导致错误。 ### 参考引用 在构建内核模块时,原子上下文(atomic context)问题通常出现在运行时,而非编译时。但编译时错误Exit 2一般表示编译失败,需检查编译日志[^1]。另外,内核参数配置(如CPU隔离)通常不影响编译,但可能影响运行时行为[^2]。 §§相关问题§§ 1. 如何为Bazel配置自定义内核构建? 2. Bazel构建内核模块时如何指定交叉编译工具链? 3. 如何解决内核模块编译中的头文件缺失问题?</think>### 解决 Bazel 构建内核模块的编译错误(Exit 2) 当使用 Bazel 构建外部内核模块时遇到 `error executing KernelModule command Exit 2` 错误,通常由以下原因导致。请按顺序逐步排查: #### 1. **检查内核头文件依赖** - 确保系统已安装与当前内核**完全匹配**的头文件: ```bash # 查看当前内核版本 uname -r # 安装对应头文件(Ubuntu示例) sudo apt install linux-headers-$(uname -r) ``` - 验证头文件路径: Bazel 需要正确指向内核头文件目录(通常在 `/lib/modules/$(uname -r)/build`)。在 BUILD 文件中显式指定: ```python kernel_module( name = "your_module", srcs = ["module.c"], hdrs = ["header.h"], kernel = "/lib/modules/$(uname -r)/build", # 关键路径 ) ``` #### 2. **确认 Bazel 配置** - 在 WORKSPACE 文件中声明内核依赖: ```python local_repository( name = "kernel_build", path = "/lib/modules/$(uname -r)/build", # 路径需与实际一致 ) ``` - 检查内核模块规则是否正确定义(示例): ```python load("@bazel_tools//tools/build_defs/kernel:kernel.bzl", "kernel_module") kernel_module( name = "my_module", srcs = glob(["*.c"]), deps = [":module_headers"], kernel = "@kernel_build//:kernel", # 引用WORKSPACE定义 ) ``` #### 3. **排查权限问题** - Bazel 可能因沙盒权限无法访问内核文件: ```bash # 临时禁用沙盒测试 bazel build --spawn_strategy=local //your:target ``` - 若成功,需调整沙盒策略或添加权限规则。 #### 4. **检查编译器兼容性** - 确保内核模块使用与内核**相同版本的 GCC**: ```bash # 查看内核编译使用的GCC版本 cat /proc/version # 强制Bazel使用指定编译器 bazel build --cxxopt="-std=gnu11" --host_cxxopt="-std=gnu11" //your:target ``` #### 5. **分析详细构建日志** 获取完整错误信息: ```bash bazel build --subcommands --verbose_failures //your:target ``` 重点关注: - `make` 命令的失败输出(通常涉及缺失符号或头文件) - 文件路径解析错误(如绝对/相对路径混淆) #### 6. **验证模块源代码兼容性** - 确保模块代码不包含**用户空间API**(如 `libc` 函数) - 检查是否使用**原子上下文不兼容操作**(如休眠函数)[^1]: ```c // 错误示例:在原子上下文中调用休眠 static irqreturn_t handler(int irq, void *dev) { msleep(100); // 会触发 scheduling while atomic return IRQ_HANDLED; } ``` #### 7. **处理内核配置依赖** 若模块依赖特定内核选项(如 `CONFIG_X=y`): - 确保当前内核已启用该选项: ```bash grep CONFIG_X /boot/config-$(uname -r) ``` - 在 BUILD 文件中声明依赖: ```python kernel_module( ... config_settings = {"CONFIG_X": "y"}, # Bazel特定语法 ) ``` ### 常见解决方案总结 | 错误类型 | 解决措施 | |-------------------------|------------------------------------------| | 头文件缺失 | 安装匹配的 `linux-headers`,检查路径 | | 编译器版本不匹配 | 使用 `--copt` 指定兼容的GCC标志 | | 原子上下文违规操作 | 替换休眠函数为 `udelay()` 或重设计逻辑 [^1] | | 符号未导出 | 在内核中使用 `EXPORT_SYMBOL()` 导出符号 | | Bazel 沙盒权限问题 | 添加 `--spawn_strategy=local` 或调整沙盒规则 | > **重要提示**:若问题涉及原子上下文(如网卡驱动 `start_xmit`),需通过 `preempt_count()` 判断上下文类型,并在原子上下文中使用 `GFP_ATOMIC` 替代 `GFP_KERNEL` [^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值