在 Linux 内核启动调试中,initcall_debug 和 ignore_loglevel 是关键的启动参数,可深度追踪初始化过程和日志输出,下面以魔方派 3 Ubuntu/Debian 系统使用的 6.8 内核为例说明。
initcall_debug
参数启用后,内核会打印所有初始化函数和驱动 probe 等函数的执行细节,包括函数返回值、耗时和运行的进程 ID。这对定位启动卡顿或初始化失败至关重要,如下所示:
[ 7.042579] probe of 100000000.gpio-regulator returned -517 after 0 usecs
[ 7.042587] probe of 0.gpio-regulator returned -517 after 0 usecs
[ 7.042702] calling gpio_regulator_init+0x0/0x50 @ 1
[ 7.042799] initcall gpio_regulator_init+0x0/0x50 returned 0 after 1000 usecs
启用方法:
-
编辑板子中的 /etc/default/grub 文件,在
GRUB_CMDLINE_LINUX变量中追加initcall_debug,如下所示:GRUB_CMDLINE_LINUX="root=LABEL=writable rw console=ttyMSM0,115200n8 pcie_pme=nomsi earlycon ignore_loglevel initcall_debug" -
使用如下命令更新
grub的配置,重启生效,若想在开机时将调试信息通过串口打印出来,可在GRUB_CMDLINE_LINUX变量中加入ignore_loglevel参数;sudo update-grub sudo reboot
或在开机时在 GRUB 菜单按下 e 键,使用新的启动参数启动内核。
源码分析:
init/main.c
bool initcall_debug;
core_param(initcall_debug, initcall_debug, bool, 0644);
若内核启动参数中定义了 initcall_debug,则会将 initcall_debug 变量置为 true,在 start_kernel 函数中,调用 initcall_debug_enable;
最终通过 trace_initcall_start_cb 和 trace_initcall_finish_cb 函数打印初始化函数的执行参数,如下所示:
void start_kernel(void)
{
...
if (initcall_debug)
initcall_debug_enable();
...
}
static void __init initcall_debug_enable(void)
{
int ret;
ret = register_trace_initcall_start(trace_initcall_start_cb,
&initcall_calltime);
ret |= register_trace_initcall_finish(trace_initcall_finish_cb,
&initcall_calltime);
WARN(ret, "Failed to register initcall tracepoints\n");
}
static __init_or_module void
trace_initcall_start_cb(void *data, initcall_t fn)
{
ktime_t *calltime = data;
printk(KERN_DEBUG "calling %pS @ %i\n", fn, task_pid_nr(current));
*calltime = ktime_get();
}
static __init_or_module void
trace_initcall_finish_cb(void *data, initcall_t fn, int ret)
{
ktime_t rettime, *calltime = data;
rettime = ktime_get();
printk(KERN_DEBUG "initcall %pS returned %d after %lld usecs\n",
fn, ret, (unsigned long long)ktime_us_delta(rettime, *calltime));
}
对于 probe 函数的信息打印则是在 __driver_probe_device 函数通过,initcall_debug 变量判断调用 really_probe 或 really_probe_debug 函数:
drivers/base/dd.c
static int __driver_probe_device(struct device_driver *drv, struct device *dev)
{
...
if (initcall_debug)
ret = really_probe_debug(dev, drv);
else
ret = really_probe(dev, drv);
...
}
ignore_loglevel
强制在终端输出所有级别的内核日志,避免因日志级别过滤丢失调试信息。适用于早期启动问题排查。
启用方法
-
编辑板子中的 /etc/default/grub 文件,在
GRUB_CMDLINE_LINUX变量中追加ignore_loglevel,如下所示:GRUB_CMDLINE_LINUX="root=LABEL=writable rw console=ttyMSM0,115200n8 pcie_pme=nomsi earlycon ignore_loglevel initcall_debug" -
使用如下命令更新
grub的配置,重启生效;sudo update-grub sudo reboot
或在开机时在 GRUB 菜单按下 e 键,使用新的启动参数启动内核。
源码分析
kernel/printk/printk.c
static bool __read_mostly ignore_loglevel;
static int __init ignore_loglevel_setup(char *str)
{
ignore_loglevel = true;
pr_info("debug: ignoring loglevel setting.\n");
return 0;
}
early_param("ignore_loglevel", ignore_loglevel_setup);
module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ignore_loglevel,
"ignore loglevel setting (prints all kernel messages to the console)");
static bool suppress_message_printing(int level)
{
return (level >= console_loglevel && !ignore_loglevel);
}
将 ignore_loglevel 加入启动参数后,根据上述代码可以发现 ignore_loglevel 变量会置为 true,那么 suppress_message_printing 函数会始终返回 false,表示不禁用日至显示;
同时在开机后,也可通过 /sys/module/printk/parameters/ignore_loglevel 这个节点随时设置 ignore_loglevel 参数:
cat /sys/module/printk/parameters/ignore_loglevel # 查看当前
echo 1 > /sys/module/printk/parameters/ignore_loglevel # 设置为 true
echo 0 > /sys/module/printk/parameters/ignore_loglevel # 设置为 false
更多的内核参数可查看内核源码中的 Documentation/admin-guide/kernel-parameters.txt 文件查看。
更新日期:2025.06.30