多行搜索,正在匹配 如下代码:
}
__setup(xxx

多行搜索,正则匹配 如下代码:
}
__setup(xxx

要匹配以 } 结尾的行及其下一行以 __setup( 开头的模式,可以使用以下正则表达式(支持多行搜索):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import re

# 示例文本(包含目标模式)
text = """
some code...
}
__setup(xxx
other code...
}
__setup(xxx
"""

# 正则表达式
pattern = r'^\}\s*\n__setup\(.*?$'

# 多行匹配(re.MULTILINE 确保 ^ 和 $ 匹配每行的开头和结尾)
matches = re.findall(pattern, text, re.MULTILINE)

# 输出匹配结果
print(matches)

代码说明:

  1. 正则表达式分解

    • ^\}:匹配行首的 } 字符(^ 表示行首)。
    • \s*:匹配可能的空白字符(空格/制表符等)。
    • \n:匹配换行符。
    • __setup\(:匹配下一行开头的固定字符串 __setup(
    • .*?$:匹配该行剩余内容直到行尾($)。
  2. 关键标志

    • re.MULTILINE:使 ^$ 匹配每行的开头和结尾(而非整个字符串)。

输出示例:

对于上面的示例文本,输出结果为:

1
['}\n__setup(xxx', '}\n   __setup(xxx']

根据需求调整:

  • 如果需捕获 xxx 部分,使用分组:
    1
    pattern = r'^\}\s*\n__setup\((.*?)\)?$'  # 捕获括号内的内容
  • 如果需处理 Windows 换行符(\r\n
    1
    pattern = r'^\}\s*\r?\n__setup\(.*?$'  # \r? 兼容 \r\n 和 \n

此方案直接解决多行匹配需求,并兼容常见空白符和换行格式。

Linux内核中__setup返回值的作用

在 Linux 内核中,__setup 宏用于注册内核命令行参数的处理函数,其注册的处理函数返回值决定了该参数是否从内核命令行中移除。具体作用如下:

1. 返回值的作用

当内核解析到匹配的命令行参数时,会调用通过 __setup 注册的处理函数:

  • 返回 1:表示参数未被完全处理,该参数会被保留在内核命令行中(后续可能被其他处理函数或用户空间进程使用)
  • 返回 0:表示参数已处理完毕,该参数会从内核命令行中移除(不会传递给用户空间)

2. 使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 注册命令行参数处理函数
static int __init my_early_param(char *str)
{
if (!strcmp(str, "on")) {
enable_feature = 1;
return 0; // 已处理,从命令行移除
} else if (!strcmp(str, "off")) {
enable_feature = 0;
return 0; // 已处理,从命令行移除
}
return 1; // 未识别,保留在命令行
}
__setup("myparam=", my_early_param);

3. 内部机制

当内核启动时,会调用 parse_args() 处理命令行参数:

1
2
3
4
5
// 内核源码示例 (init/main.c)
while ((param = next_arg(param, &val)) {
ret = parse_one(param, val, ...);
if (ret < 0) break;
}

parse_one() 中:

1
2
3
4
5
6
7
// 简化的处理逻辑
for (p = __setup_start; p < __setup_end; p++) {
if (strcmp(param, p->str) == 0) {
if (p->setup_func(val) == 0) // 调用注册函数
return 0; // 已处理则移除参数
}
}

4. 典型应用场景

返回值 使用场景 示例参数
0 已处理的专用参数 console=ttyS0
1 需要传递给用户空间的参数 root=/dev/sda1
1 格式错误需保留供其他处理的参数 无法识别的参数值

5. 注意事项

  1. 早期初始化__setup 处理函数在内核初始化早期执行(在内存管理完全初始化之前)
  2. 字符串格式:参数格式应为 param=value,处理函数接收 value 部分
  3. 替代方案:现代内核推荐使用 early_param(),它在更早阶段执行
  4. 内存限制:处理函数不能使用动态内存分配(此时内存管理未完全初始化)

6. 实际内核示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 内核源码示例 (kernel/reboot.c)
static int __init reboot_setup(char *str)
{
for (;;) {
switch (*str) {
case 'w': // 进入硬件看门狗
reboot_type = 0x801;
break;
case 'c': // 崩溃后重启
reboot_force = 1;
break;
default:
return 1; // 未处理
}
str = strchr(str, ',');
if (!str) break;
str++;
}
return 0; // 已处理
}
__setup("reboot=", reboot_setup);

总结:__setup 处理函数的返回值是内核命令行参数处理流程的关键控制点,通过返回 01 决定参数是否从内核命令行中移除,直接影响参数是否传递给用户空间进程。

Linux使能dynamic debug打印

在 Linux 内核中,Dynamic Debug(动态调试) 功能允许在运行时动态启用或禁用内核中的调试打印语句(如 pr_debug()dev_dbg())。以下是启用和使用 Dynamic Debug 的完整指南:


一、启用 Dynamic Debug 的必要条件

  1. 内核配置

    1
    2
    3
    4
    5
    # 启用 CONFIG_DYNAMIC_DEBUG
    CONFIG_DYNAMIC_DEBUG=y

    # 启用 DEBUG_FS(提供控制接口)
    CONFIG_DEBUG_FS=y

    通过 make menuconfig 确认:

    1
    2
    3
    Kernel hacking -> 
    [*] Enable dynamic printk() support
    [*] Debug Filesystem
  2. 挂载 debugfs

    1
    sudo mount -t debugfs none /sys/kernel/debug

二、控制 Dynamic Debug 的三种方式

方法 1:通过 /sys/kernel/debug/dynamic_debug/control 文件

这是最常用的动态控制方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 查看当前所有调试点
sudo cat /sys/kernel/debug/dynamic_debug/control

# 2. 启用特定文件的调试(示例:启用 ext4 文件系统调试)
sudo echo "file fs/ext4/* +p" > /sys/kernel/debug/dynamic_debug/control

# 3. 启用特定函数的调试
sudo echo "func ext4_* +p" > /sys/kernel/debug/dynamic_debug/control

# 4. 启用模块调试(示例:nvidia 驱动)
sudo echo "module nvidia +p" > /sys/kernel/debug/dynamic_debug/control

# 5. 禁用调试
sudo echo "file fs/ext4/* -p" > /sys/kernel/debug/dynamic_debug/control

方法 2:通过内核启动参数

在内核命令行中添加 dyndbg 参数:

1
2
# 在 GRUB 配置 /boot/grub/grub.cfg 中添加:
linux /boot/vmlinuz ... dyndbg="file fs/ext4/* +p"

或使用模块特定参数:

1
modprobe nvidia dyndbg=+p

方法 3:在代码中直接启用

在驱动代码中添加:

1
2
3
4
#include <linux/dynamic_debug.h>

// 在初始化函数中启用
dynamic_debug_enable("file driver.c line 123-456 +p");

三、控制指令详解

指令 作用 示例
+p 启用打印 file ext4*.c +p
-p 禁用打印 module usb_storage -p
+l 添加行号信息 func foo* +l
+m 添加模块名信息 file net/*.c +m
+t 添加线程ID信息 func bar +t
范围限定 按文件/函数/模块/行号过滤 file driver.c line 100-200 +p

四、实际使用示例

场景:调试 USB 存储驱动

1
2
3
4
5
6
7
8
9
10
# 1. 启用所有 usb-storage 相关调试
sudo echo "module usb_storage +p" > /sys/kernel/debug/dynamic_debug/control

# 2. 查看内核日志
sudo dmesg -w

# 3. 执行 USB 操作后,查看调试输出

# 4. 禁用调试
sudo echo "module usb_storage -p" > /sys/kernel/debug/dynamic_debug/control

场景:调试特定文件的某几行

1
2
# 启用 ext4/inode.c 中 100-200 行的调试
sudo echo "file fs/ext4/inode.c line 100-200 +p" > /sys/kernel/debug/dynamic_debug/control

五、高级技巧

  1. 组合使用标志

    1
    2
    # 启用打印并显示行号、模块名
    sudo echo "file net/core/dev.c +pml" > /sys/kernel/debug/dynamic_debug/control
  2. 查询当前状态

    1
    2
    # 显示所有已启用的调试点
    grep "+p" /sys/kernel/debug/dynamic_debug/control
  3. 永久生效
    /etc/modprobe.d/debug.conf 中添加:

    1
    options usb_storage dyndbg=+p

六、工作原理

  1. 当使用 pr_debug()dev_dbg() 时,这些调用会被编译进特殊段 .data._dyndbg
  2. 内核启动时解析这些调试描述符
  3. 通过 debugfs 接口动态修改标志位
  4. 打印函数执行前会检查 DYNAMIC_DEBUG_BRANCH

七、注意事项

  1. 性能影响:启用大量调试打印会显著降低系统性能
  2. 日志风暴:避免在生产环境启用,可能刷屏内核日志
  3. 依赖关系:需确保 debugfs 已挂载
  4. 内核版本:完整功能需要 Linux 3.0+ 内核

通过 Dynamic Debug,您可以在不重启系统、不重新编译内核的情况下,精准控制调试信息的输出,极大提高内核调试效率。

如何查看linux内核动态打印日志

查看 Linux 内核动态打印日志的完整指南

在 Linux 中使用动态调试(Dynamic Debug)功能后,查看生成的调试日志有几种主要方法。以下是最常用的查看方式及其详细说明:

一、核心查看方法

1. 使用 dmesg 命令(推荐)

1
2
3
4
5
6
7
8
# 实时查看新日志(按 Ctrl+C 退出)
sudo dmesg -w

# 显示最后100条日志并实时刷新
sudo dmesg -H --follow | tail -n 100

# 仅显示动态调试日志(过滤 pr_debug/dev_dbg 输出)
sudo dmesg | grep -i "\[dyn\]"

2. 使用 journalctl 命令(systemd 系统)

1
2
3
4
5
6
7
8
# 实时查看内核日志
sudo journalctl -k -f

# 查看特定模块的日志(如 ext4)
sudo journalctl -k -g "ext4"

# 查看过去5分钟的日志
sudo journalctl -k --since "5 minutes ago"

3. 直接读取内核日志缓冲区

1
2
3
4
5
# 方法1:通过 /proc/kmsg(需要 root)
sudo cat /proc/kmsg

# 方法2:通过 /dev/kmsg(更结构化)
sudo cat /dev/kmsg

二、优化日志查看体验

1. 提高日志缓冲区大小

1
2
3
4
5
# 临时增大内核日志缓冲区(128K→2M)
sudo sysctl -w kernel.printk_ringbuffer=2097152

# 永久生效(添加到 /etc/sysctl.conf)
echo "kernel.printk_ringbuffer=2097152" | sudo tee -a /etc/sysctl.conf

2. 日志级别过滤

1
2
3
4
5
# 显示所有级别日志(默认只显示 warn 以上)
sudo dmesg -n 8

# 恢复默认级别
sudo dmesg -n 4

3. 彩色输出和人性化时间戳

1
2
3
4
5
# 安装彩色支持
sudo apt install grc

# 带颜色和相对时间的输出
sudo dmesg -H --color=always | grcat dmesg

三、高级调试技巧

1. 特定模块日志过滤

1
2
3
4
5
# 跟踪 USB 存储模块日志
sudo dmesg -w | grep -E 'usb_storage|sd'

# 跟踪网络子系统日志
sudo dmesg -w | grep -E 'net|eth|wlan'

2. 将日志保存到文件

1
2
3
4
5
# 保存日志到文件(后台运行)
sudo dmesg -w > dyn_debug.log &

# 按时间范围检索
sudo journalctl -k --since "2023-10-01 09:00:00" --until "2023-10-01 10:00:00"

3. 使用专用工具

1
2
3
4
5
# 安装内核日志查看专用工具
sudo apt install kinfo klogd

# 使用 kinfo 查看
sudo kinfo -f

四、动态调试日志识别特征

动态调试日志通常有以下特征:

  1. 前缀为 [dyn] 或模块名
  2. 包含 DEBUG 标识
  3. 有额外的上下文信息(如行号、函数名)

示例日志:

1
2
[ 1234.567890] usb_storage: [dyn] scsi_add_host: line 128: Adding host for USB device
[ 1234.567901] nvidia: [dyn] nv_setup:42: Initializing GPU context

五、常见问题解决

问题1:看不到任何输出

解决方案:

  1. 确认已启用动态调试:
    1
    grep "DYNAMIC_DEBUG" /boot/config-$(uname -r)
  2. 检查 debugfs 是否挂载:
    1
    mount | grep debugfs
  3. 确认已启用打印标志:
    1
    cat /sys/kernel/debug/dynamic_debug/control | grep "+p"

问题2:日志被刷屏太快

解决方案:

1
2
3
4
5
# 限制输出速率
sudo dmesg -w | rate

# 使用缓冲区
sudo dmesg -w | less

问题3:日志不包含源代码位置

解决方案:
启用行号和模块名:

1
2
3
4
5
# 添加行号信息
echo "+l" > /sys/kernel/debug/dynamic_debug/control

# 添加模块名
echo "+m" > /sys/kernel/debug/dynamic_debug/control

六、最佳实践建议

  1. 精确控制:只启用需要的模块/文件,避免日志洪水

    1
    echo "file drivers/usb/storage/* +p" > /sys/kernel/debug/dynamic_debug/control
  2. 时间同步:确保系统时间准确,方便日志分析

    1
    sudo timedatectl set-ntp true
  3. 日志轮转:防止日志填满磁盘

    1
    2
    sudo apt install logrotate
    sudo nano /etc/logrotate.d/dmesg
  4. 远程日志:对于嵌入式系统,配置远程日志

    1
    2
    # 在 /etc/rsyslog.conf 添加:
    kern.* @192.168.1.100:514

通过以上方法,您可以高效地查看和分析 Linux 内核的动态调试日志。对于生产环境,建议结合 systemd-journald 的持久化日志功能,确保关键调试信息不会丢失。