内核调试方法
基于 ARM64 开发板 (5.10.0-openeuler aarch64) 实机验证,所有命令均带有实际输出示例。
目录
- 基础环境查看命令
- [/proc 文件系统调试](#/proc 文件系统调试)
- [/sys 缓存几何信息](#/sys 缓存几何信息)
- [ftrace 内核跟踪](#ftrace 内核跟踪)
- [cache-stat 缓存统计工具](#cache-stat 缓存统计工具)
- [dmesg 内核日志](#dmesg 内核日志)
- [Magic SysRq 魔术键](#Magic SysRq 魔术键)
- [Dynamic Debug 动态调试](#Dynamic Debug 动态调试)
- [printk 内核日志系统](#printk 内核日志系统)
- [lockup 检测器](#lockup 检测器)
- [KASAN/KFENCE/kmemleak 内存调试](#KASAN/KFENCE/kmemleak 内存调试)
- [RCU 调试与测试](#RCU 调试与测试)
- 内核启动参数调试
- [taskset CPU 亲和性](#taskset CPU 亲和性)
- [chrt 实时调度策略](#chrt 实时调度策略)
- [cyclictest 实时延迟测试](#cyclictest 实时延迟测试)
- [stress-ng 压力测试](#stress-ng 压力测试)
- [fw_printenv/fw_setenv U-Boot 环境变量](#fw_printenv/fw_setenv U-Boot 环境变量)
- [proc/sys 内核参数调优](#proc/sys 内核参数调优)
- 自定义调试工具
1. 基础环境查看命令
1.1 查看内核版本
bash
root@board:~# uname -a
Linux board 5.10.0-openeuler #22 SMP PREEMPT Mon Jun 1 01:58:33 CST 2026 aarch64 aarch64 aarch64 GNU/Linux
# ^ ^ ^ ^ ^ ^ ^
# | | | | | | +-- GNU/Linux
# | | | | | +-- CPU架构(aarch64)
# | | | | +-- 编译时间
# | | | +-- 内核版本号(第22次编译, PREEMPT)
# | | +-- 内核名称
# | +-- 主机名
# +-- 内核类型(Linux)
root@board:~# cat /proc/version
Linux version 5.10.0-openeuler (builder@build-host) (aarch64-openeuler-linux-gnu-gcc 10.3.1, GNU ld 2.37) #22 SMP PREEMPT Mon Jun 1 01:58:33 CST 2026
# ^ ^ ^ ^
# | | | +-- 编译时间
# | | +-- 编译器版本
# | +-- 编译主机
# +-- 内核版本
1.2 查看 CPU 信息
bash
root@board:~# cat /proc/cpuinfo | head -10
processor : 0 # CPU逻辑编号(0-7, 共8核)
BogoMIPS : 48.00 # 估算的CPU每秒百万指令数(用于性能参考)
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
# ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
# | | | | | | | | | | | | | | +-- asimd dot product
# | | | | | | | | | | | | | +-- LRCPC指令
# | | | | | | | | | | | | +-- 原子指令(LSE)
# | | | | | | | | | | | +-- CRC32
# | | | | | | | | | | +-- SHA2
# | | | | | | | | | +-- SHA1
# | | | | | | | | +-- 半精度浮点(FP16)
# | | | | | | | +-- 单精度/双精度浮点
# | | | | | | +-- 原子操作(AArch32)
# | | | | | +-- CRC32(AArch32)
# | | | | +-- SHA2(AArch32)
# | | | +-- SHA1(AArch32)
# | | +-- PMULL(多项式乘法,用于加密)
# | +-- AES加密指令
# +-- 浮点/NEON
CPU implementer : 0x41 # CPU厂商: 0x41=ARM, 0x42=Broadcom, 0x43= Cavium
CPU architecture: 8 # ARM架构版本: 8=ARMv8, 9=ARMv9
CPU variant : 0x3 # CPU变体(内部版本)
CPU part : 0xd0d # CPU型号: 0xd0d=Cortex-A76, 0xd0e=Cortex-A77
CPU revision : 1 # CPU修订版本
root@board:~# nproc
7 # 返回7表示用户态可见7核
root@board:~# cat /sys/devices/system/cpu/possible
0-7 # 系统实际有8核(0-7),CPU7被isolcpus隔离
说明 :
nproc返回 7 是因为 CPU7 被isolcpus隔离,但possible显示 0-7 共 8 个核。
1.3 查看内核启动参数 (cmdline)
bash
root@board:~# cat /proc/cmdline
uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=/dev/mmcblk0p5 rw rootwait nohz=on nohz_full=7 isolcpus=domain,managed_irq,7 rcu_nocbs=7 irqaffinity=0-6
# ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# | | | | +-- irqaffinity=0-6: 中断只发到CPU0-6,保护CPU7
# | | | +-- RCU回调卸载到非隔离核
# | | +-- isolcpus=domain,managed_irq,7: 隔离CPU7
# | | domain=从调度域移除; managed_irq=不接收托管中断
# | +-- nohz_full=7: CPU7关闭定期时钟tick
# +-- earlycon=uart8250,mmio32,0xfeb50000: 早期串口调试输出
# console=ttyFIQ0: 控制台输出到FIQ串口
# irqchip.gicv3_pseudo_nmi=0: 禁用伪NMI
# root=/dev/mmcblk0p5: 根文件系统在eMMC分区5
# rw: 根文件系统读写挂载
# rootwait: 等待根设备就绪
# nohz=on: 启用动态时钟(dyntick)
1.4 查看内存信息
bash
root@board:~# cat /proc/meminfo | head -10
MemTotal: 3992272 kB # 物理内存总量(约3.8GB)
MemFree: 3677960 kB # 完全空闲的内存(约3.5GB,系统刚启动)
MemAvailable: 3815448 kB # 可用于启动新程序的估算值(含可回收缓存)
Buffers: 46764 kB # 块设备缓冲区(raw disk)
Cached: 112624 kB # 页面缓存(文件读缓存)
SwapCached: 0 kB # 交换缓存(未启用swap所以为0)
Active: 116112 kB # 最近被访问过的内存(不易回收)
Inactive: 55028 kB # 最近未访问的内存(可优先回收)
Active(anon): 584 kB # 活跃的匿名页(进程堆栈等)
Inactive(anon): 26636 kB # 不活跃的匿名页(可换出)
1.5 查看系统运行时间和负载
bash
root@board:~# cat /proc/uptime
692527.23 5284485.38
# ^^^^^^^^ ^^^^^^^^
# | +-- 所有CPU空闲时间总和(秒), 5284485/(8核*692527)≈95.4%空闲
# +-- 系统已运行时间(秒), 692527秒≈8天
root@board:~# cat /proc/loadavg
0.07 0.02 0.00 1/184 728080
# ^^^^ ^^^^ ^^^^ ^^^^^ ^^^^^^
# | | | | +-- 最后创建的PID(进程ID)
# | | | +-- 正在运行的进程数/总进程数(1个在跑,184个总进程)
# | | +-- 15分钟平均负载
# | +-- 5分钟平均负载
# +-- 1分钟平均负载(0.07,系统非常空闲)
# 负载含义: 单核CPU负载1.0=满载,8核CPU负载8.0=满载。当前0.07表示极空闲。
root@board:~# cat /proc/version
Linux version 5.10.0-openeuler ...
2. /proc 文件系统调试
2.1 查看中断分布 (/proc/interrupts)
查看每个 CPU 的中断处理数量,用于判断中断是否均衡分布。
bash
root@board:~# cat /proc/interrupts | head -12
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
11: 692545722 37899191 54126437 39191535 39096395 38223640 37408833 36000462 GICv3 30 Level arch_timer
# ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^ ^^^^^ ^^^^^^^^^^
# | | | | | +-- 中断源名称(arch_timer=架构定时器)
# | | | | +-- 触发方式(Level=电平触发, Edge=边沿触发)
# | | | +-- 中断号(GIC内部的中断ID=30)
# | | +-- 中断控制器(GICv3)
# | +-- 各CPU处理此中断的次数(CPU0约6.9亿次,CPU7仅3600万次,说明分配不均)
# +-- 中断向量号(Linux内部IRQ号)
14: 0 493916 14192146 992788 1843754 1389806 1912844 7 GICv3 321 Level rk_timer
15: 52554 51468 51504 51463 97363 97451 96568 397 GICv3 23 Level arm-pmu
# ^^^^^^^^
# +-- arm-pmu=性能监视单元
17: 3 0 0 0 0 0 0 0 GICv3 126 Level fb000000.gpu
# ^^^^^^^^^^^^
# +-- GPU设备中断
22: 328 0 0 0 0 0 0 0 GICv3 250 Level ehci_hcd:usb2
27: 459 0 0 0 0 0 0 0 GICv3 349 Level fd880000.i2c
2.2 查看软中断分布 (/proc/softirqs)
bash
root@board:~# cat /proc/softirqs
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
HI: 1 70 67 15 15 0 0 0 # 高优先级tasklet
TIMER: 13167256 2741982 19976962 3371045 3970589 2891871 5163948 9 # 定时器软中断(CPU7仅9次!)
NET_TX: 97879 0 44 27576 0 2 0 0 # 网络发送
NET_RX: 5262 76 156 92 402 36 184 0 # 网络接收
BLOCK: 1958935 0 0 0 391382 181157 146065 0 # 块设备IO(磁盘读写)
TASKLET: 312 175 74 30 30 0 1 0 # tasklet/小任务
SCHED: 100557916 953737 16327919 1491408 2471045 2274322 3348199 0 # 调度器(负载均衡)
RCU: 13158781 19841256 17802903 19672186 14472393 14234901 18944186 29 # RCU回调(CPU7仅29次)
# ^ ^ ^
# | | +-- CPU7几乎为0, 隔离效果显著
# | +-- 各CPU处理此软中断的总次数
# +-- 软中断类型名称
关键观察:CPU7 的软中断几乎全部为 0(只有 RCU 有极少触发),证明了隔离核的有效性。
2.3 查看系统统计 (/proc/stat)
bash
root@board:~# cat /proc/stat | grep -E 'ctxt|btime|processes|procs_running|procs_blocked'
ctxt 283396533 # 上下文切换总数(从开机到现在)
btime 1780505989 # 系统启动的Unix时间戳
processes 727892 # 创建的进程总数(含线程)
procs_running 2 # 当前正在运行(或可运行)的进程数
procs_blocked 0 # 当前阻塞在I/O上的进程数(若持续>0表示I/O瓶颈)
root@board:~# cat /proc/stat | head -5
cpu 20550138 5476 4649018 527653961 7498 0 13423 0 0 0 # 所有CPU合计
# ^^^^^^^^ ^^^^ ^^^^^^^ ^^^^^^^^^ ^^^^ ^ ^^^^^ ^ ^ ^
# | | | | | | | | +-- steal(被虚拟化偷走的时间)
# | | | | | | | +-- softirq(软中断时间)
# | | | | | | +-- irq(硬中断时间)
# | | | | | +-- iowait(等待IO完成时间)
# | | | | +-- idle(空闲时间,占绝大部分)
# | | | +-- system(内核态时间)
# | | +-- nice(低优先级用户态时间)
# | +-- user(用户态时间)
# +-- 列标签(单位: 百分之一秒,即jiffies*10ms)
cpu0 2865332 267 723868 65328769 980 0 12130 0 0 0 # CPU0单独统计
cpu1 2808219 191 791726 65546641 1637 0 43 0 0 0
2.4 查看虚拟内存统计 (/proc/vmstat)
bash
root@board:~# cat /proc/vmstat | head -10
nr_free_pages 919123 # 空闲页数(每页4KB, 919123*4KB≈3.5GB)
nr_zone_inactive_anon 6774 # 非活跃匿名页(进程栈/堆,可swap换出)
nr_zone_active_anon 146 # 活跃匿名页(频繁访问的进程内存)
nr_zone_inactive_file 7115 # 非活跃文件页(读缓存,可回收)
nr_zone_active_file 28899 # 活跃文件页(频繁访问的文件缓存)
nr_zone_unevictable 1280 # 不可换出页(mlock锁定)
nr_zone_write_pending 0 # 正在回写磁盘的页数
nr_mlock 0 # mlock锁定的页数
nr_page_table_pages 320 # 页表占用的页数(320*4KB=1.25MB)
nr_bounce 0 # 反弹缓冲区的页数(低端内存兼容)
2.5 cache_color 自定义统计 (/proc/cache_color_stats)
当内核启用了 cache color 功能时,可通过此接口查看策略执行统计:
bash
root@board:~# cat /proc/cache_color_stats
# cache_color P0 stats
iso_alloc_ok 0 # 隔离核从自己颜色池分配成功的次数
iso_alloc_miss 0 # 隔离核在目标颜色上分配失败(需回退)的次数
noniso_alloc_into_iso 0 # 非隔离核分配到了隔离颜色(颜色泄露)
policy_fallback 0 # 因目标颜色无空闲页而回退到任意颜色的次数
pcp_refill_from_nonpreferred 0 # 每CPU页缓存从非偏好颜色补货次数(越高说明颜色策略被稀释)
3. /sys 缓存几何信息
3.1 查看 L3 (LLC) 缓存参数
bash
root@board:~# cat /sys/devices/system/cpu/cpu0/cache/index3/size
3072K
root@board:~# cat /sys/devices/system/cpu/cpu0/cache/index3/ways_of_associativity
12
root@board:~# cat /sys/devices/system/cpu/cpu0/cache/index3/coherency_line_size
64
root@board:~# cat /sys/devices/system/cpu/cpu0/cache/index3/number_of_sets
4096
| 参数 | 值 | 含义 |
|---|---|---|
| size | 3072K (3MB) | L3 缓存总大小 |
| ways_of_associativity | 12 | 12 路组相联 |
| coherency_line_size | 64B | 缓存行大小 |
| number_of_sets | 4096 | set 数量 |
推导公式:
set 重复周期 = number_of_sets × line_size = 4096 × 64B = 256KB
缓存总大小 = set 数 × 路数 × line_size = 4096 × 12 × 64B = 3MB
3.2 查看 L1/L2 缓存
bash
root@board:~# cat /sys/devices/system/cpu/cpu0/cache/index0/size # L1 data
64K
root@board:~# cat /sys/devices/system/cpu/cpu0/cache/index1/size # L1 instruction
64K
root@board:~# cat /sys/devices/system/cpu/cpu0/cache/index2/size # L2
512K
4. ftrace 内核跟踪
ftrace 是 Linux 内核内置的跟踪框架,TL3588 上通过 tracefs(挂载在 /sys/kernel/tracing/)访问。
4.1 查看可用的跟踪器
bash
root@board:~# cat /sys/kernel/tracing/available_tracers
blk function_graph function nop
4.2 函数跟踪 (function tracer)
跟踪内核函数调用,用于定位热点路径。
bash
# 步骤
root@board:~# echo function > /sys/kernel/tracing/current_tracer
root@board:~# echo 1 > /sys/kernel/tracing/tracing_on
root@board:~# sleep 1
root@board:~# echo 0 > /sys/kernel/tracing/tracing_on
root@board:~# cat /sys/kernel/tracing/trace | head -15
# tracer: function
# entries-in-buffer/entries-written: 85723/189231 #P:8 (缓冲区中/总写入, #P=CPU数)
# TASK-PID CPU# ||||||| TIMESTAMP FUNCTION
# | | | ||||||| | |
<idle>-0 [006] d...2.. 691564.100842: _raw_spin_unlock_irqrestore <-hrtimer_get_next_event
# ^ ^ ^ ^ ^ ^
# | | | | | +-- 调用者函数(谁调用了它)
# | | | | +-- 被跟踪的函数
# | | | +-- 跟踪标记: d=关中断, .=开中断 | h=硬中断内 | s=软中断内 | 数字=preempt深度
# | | +-- CPU编号[006]
# | +-- 进程PID
# +-- 进程名(<idle>=空闲进程)
bash-727917 [004] ....... 691564.100842: mutex_unlock <-tracing_set_tracer
<idle>-0 [002] d...2.. 691564.100843: clockevents_switch_state <-__tick_broadcast_oneshot_control
<idle>-0 [001] d...1.. 691564.100843: cpuidle_not_available <-do_idle
<idle>-0 [003] d...1.. 691564.100843: cpuidle_select <-do_idle
注意 :function tracer 产生的数据量非常大,建议使用
tracing_cpumask限制 CPU 范围。
4.3 函数调用图跟踪 (function_graph tracer)
跟踪函数的进入和退出,显示函数调用关系和执行耗时。
bash
root@board:~# echo function_graph > /sys/kernel/tracing/current_tracer
root@board:~# echo 1 > /sys/kernel/tracing/tracing_on
root@board:~# sleep 1
root@board:~# echo 0 > /sys/kernel/tracing/tracing_on
root@board:~# cat /sys/kernel/tracing/trace | head -20
# tracer: function_graph
#
# CPU DURATION FUNCTION CALLS
# | | | | | | |
# 5) | cpu_pm_exit() {
# ^ ^ | ^
# | | | +-- 函数名: cpu_pm_exit() 开始执行, { 表示函数入口
# | | +-- 竖线: 调用深度标记(越深缩进越多)
# | +-- 执行耗时(us): 空表示函数尚未返回, 数字如0.583us表示耗时
# +-- CPU编号[5]
5) 0.583 us | rcu_irq_enter_irqson();
5) 0.292 us | __rcu_read_lock();
5) | raw_notifier_call_chain() {
5) | gic_cpu_pm_notifier() {
5) | gic_cpu_sys_reg_init() {
5) 0.291 us | cpu_logical_map();
5) 0.291 us | cpu_logical_map();
5) 0.292 us | cpu_logical_map();
5) 0.292 us | cpu_logical_map();
5) 0.291 us | cpu_logical_map();
5) 0.291 us | cpu_logical_map();
5) 6.125 us | }
延迟标记 :
+=超过10us,!=超过100us,#=超过1ms,*=超过10ms,@=超过100ms,$=超过1s
4.4 事件跟踪 (event tracing) --- 调度切换
跟踪 CPU 上的进程切换,用于分析延迟抖动来源。
bash
# 仅跟踪 sched_switch 事件
root@board:~# echo nop > /sys/kernel/tracing/current_tracer
root@board:~# echo 1 > /sys/kernel/tracing/events/sched/sched_switch/enable
root@board:~# echo 1 > /sys/kernel/tracing/tracing_on
root@board:~# sleep 2
root@board:~# echo 0 > /sys/kernel/tracing/tracing_on
root@board:~# cat /sys/kernel/tracing/trace | head -20
# tracer: nop
# TASK-PID CPU# ||||||| TIMESTAMP FUNCTION
# | | | ||||||| | |
<idle>-0 [000] d...2.. 691566.578057: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=bash next_pid=727927 next_prio=139
# ^ ^
# | +-- 下一个进程: bash(PID=727927,优先级139,120=普通,99=RT)
# +-- 上一个进程: swapper/0(PID=0,优先级120,状态R=Running)
# ==> 表示切换方向: 从prev切换到next
bash-727926 [005] d...2.. 691566.578100: sched_switch: prev_comm=bash prev_pid=727926 prev_prio=139 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120
# ^
# +-- prev_state=S=Sleep(自愿睡眠)
<idle>-0 [002] d...2.. 691566.578512: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=rcu_preempt next_pid=13 next_prio=120
rcu_preempt-13 [002] d...2.. 691566.578523: sched_switch: prev_comm=rcu_preempt prev_pid=13 prev_prio=120 prev_state=I ==> next_comm=swapper/2 next_pid=0 next_prio=120
sleep-727927 [000] d...2.. 691566.579570: sched_switch: prev_comm=sleep prev_pid=727927 prev_prio=139 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
用途:检查隔离核上是否有意外进程运行(prev_prio=120 表示普通进程,99 表示 RT 进程)。如果 CPU7 上出现非 idle 进程的切换记录,说明隔离不彻底。
4.5 事件跟踪 --- 中断处理
bash
root@board:~# echo 1 > /sys/kernel/tracing/events/irq/irq_handler_entry/enable
root@board:~# echo 1 > /sys/kernel/tracing/tracing_on
root@board:~# sleep 1
root@board:~# echo 0 > /sys/kernel/tracing/tracing_on
root@board:~# cat /sys/kernel/tracing/trace | head -10
<idle>-0 [000] dn.h1.. 691591.123353: irq_handler_entry: irq=1 name=IPI
# ^^^ ^^^^
# | +-- 中断名: IPI=核间中断(CPU之间通信)
# +-- Linux IRQ号
<idle>-0 [003] dn.h1.. 691591.123355: irq_handler_entry: irq=1 name=IPI
4.6 可用的事件分类
bash
root@board:~# ls /sys/kernel/tracing/events/
alarmtimer avc block bpf_trace bridge btrfs cfg80211 cgroup clk cma compaction
context_tracking cpuhp devfreq dma_fence drm dwc3 enable filter i2c iommu irq
ipi kgsl kmem kvm lock maple_tree mct mdio memleak migrate mmap
module napi net oom page_isolation pagemap percpu power printk ras rcu
regmap regulator resctrl rpmsg rpm rseq rtc sched scmi scsi signal
skb smbus smmu sock spi swiotlb sync_trace task tcp thermal timer
udp v4l2 vb2 vmscan workqueue writeback xdp xfs xhci-hcd
4.7 跟踪实例 (trace instances)
使用 trace instance 可以独立配置跟踪,不影响全局跟踪状态。这是 rt_init_debug.sh 中使用的方法。
bash
# 创建实例
root@board:~# mkdir -p /sys/kernel/tracing/instances/my-test
# 配置实例:只跟踪 CPU7 (mask=0x80)
root@board:~# echo 0 > /sys/kernel/tracing/instances/my-test/tracing_on
root@board:~# echo 16384 > /sys/kernel/tracing/instances/my-test/buffer_size_kb
root@board:~# echo 80 > /sys/kernel/tracing/instances/my-test/tracing_cpumask
# 启用事件
root@board:~# echo 1 > /sys/kernel/tracing/instances/my-test/events/sched/sched_switch/enable
root@board:~# echo 1 > /sys/kernel/tracing/instances/my-test/events/irq/irq_handler_entry/enable
root@board:~# echo 1 > /sys/kernel/tracing/instances/my-test/events/irq/irq_handler_exit/enable
# 开始跟踪
root@board:~# echo 1 > /sys/kernel/tracing/instances/my-test/tracing_on
root@board:~# sleep 5
root@board:~# echo 0 > /sys/kernel/tracing/instances/my-test/tracing_on
# 读取跟踪结果
root@board:~# cat /sys/kernel/tracing/instances/my-test/trace
# 清理
root@board:~# echo 0 > /sys/kernel/tracing/instances/my-test/events/enable
root@board:~# rmdir /sys/kernel/tracing/instances/my-test
同时跟踪多个事件的完整配置(生产环境常用):
bash
INST=/sys/kernel/tracing/instances/cpu7-latency
mkdir -p $INST
echo 0 > $INST/tracing_on
echo 16384 > $INST/buffer_size_kb
echo 80 > $INST/tracing_cpumask # CPU7 only
# 启用关键事件组
echo 1 > $INST/events/sched/sched_switch/enable
echo 1 > $INST/events/sched/sched_wakeup/enable
echo 1 > $INST/events/irq/irq_handler_entry/enable
echo 1 > $INST/events/irq/irq_handler_exit/enable
echo 1 > $INST/events/irq/softirq_entry/enable
echo 1 > $INST/events/irq/softirq_exit/enable
echo 1 > $INST/events/timer/hrtimer_expire_entry/enable
echo 1 > $INST/events/timer/hrtimer_expire_exit/enable
echo 1 > $INST/events/ipi/ipi_entry/enable
echo 1 > $INST/events/ipi/ipi_exit/enable
echo 1 > $INST/events/power/cpu_idle/enable
: > $INST/trace # 清空缓冲区
echo 1 > $INST/tracing_on # 开始跟踪
# ... 运行测试 ...
echo 0 > $INST/tracing_on # 停止跟踪
cat $INST/trace > /tmp/trace_output.txt
4.8 trace_marker --- 手动插入标记
在跟踪流中插入自定义标记,便于定位代码执行点:
bash
# 先确保 markers 选项已启用
root@board:~# cat /sys/kernel/tracing/options/markers
1 # 1=已启用
# 清空缓冲区并写入标记
root@board:~# echo nop > /sys/kernel/tracing/current_tracer
root@board:~# : > /sys/kernel/tracing/trace
root@board:~# echo "my_test_marker: trace_start" > /sys/kernel/tracing/trace_marker
root@board:~# sleep 0.3
root@board:~# echo "my_test_marker: trace_end" > /sys/kernel/tracing/trace_marker
root@board:~# cat /sys/kernel/tracing/trace | grep my_test_marker
<...>-728111 [003] ....... 692534.071945: tracing_mark_write: my_test_marker: trace_start
<...>-728111 [003] ....... 692534.373900: tracing_mark_write: my_test_marker: trace_end
5. cache-stat 缓存统计工具
TL3588 上未安装 perf,但提供了自定义的 cache-stat 工具,基于 perf_event_open 系统调用直接读取 PMU 计数器。
5.1 基本用法
bash
root@board:~# /usr/local/bin/cache-stat --help
Usage: /usr/local/bin/cache-stat --cpu-list LIST --duration SECONDS --output PATH
5.2 监控 CPU7 (隔离核) 缓存统计
bash
root@board:~# /usr/local/bin/cache-stat --cpu-list 7 --duration 3 --output /tmp/perf.csv && cat /tmp/perf.csv
event,value
cycles,27776 # CPU时钟周期数
instructions,19125 # 执行的指令数
cache-references,4920 # 缓存访问总次数
cache-misses,126 # 缓存未命中次数
LLC-loads,456 # 最后一级缓存(L3)加载次数
LLC-load-misses,21 # L3加载未命中次数
cache-hit-pct,97.44 # 总体缓存命中率 = (1 - 126/4920) × 100% = 97.44%
llc-load-hit-pct,95.39 # L3命中率 = (1 - 21/456) × 100% = 95.39%
指标含义:
| 指标 | 含义 |
|---|---|
| cycles | CPU 周期数 |
| instructions | 执行的指令数 |
| cache-references | 缓存总访问次数 |
| cache-misses | 缓存未命中次数 |
| LLC-loads | 最后一级缓存(L3)加载次数 |
| LLC-load-misses | LLC 加载未命中次数 |
| cache-hit-pct | 总体缓存命中率 = (1 - misses/references) × 100% |
| llc-load-hit-pct | LLC 加载命中率 = (1 - llc-misses/llc-loads) × 100% |
5.3 同时监控隔离核和 housekeeping 核
bash
# 监控 CPU7
/usr/local/bin/cache-stat --cpu-list 7 --duration 120 --output /tmp/perf-iso.csv &
# 监控 CPU0-6
/usr/local/bin/cache-stat --cpu-list 0-6 --duration 120 --output /tmp/perf-hk.csv &
6. dmesg 内核日志
bash
root@board:~# dmesg | tail -10
[ 134.107043] perf: interrupt took too long (2507 > 2500), lowering kernel.perf_event_max_sample_rate to 79000
# ^^^^^^^^^^ ^
# | +-- 日志级别(数字越大越不紧急)
# +-- 内核时间戳(秒.微秒)
[ 152.821696] perf: interrupt took too long (3157 > 3133), lowering kernel.perf_event_max_sample_rate to 63000
[ 230.577447] perf: interrupt took too long (3954 > 3946), lowering kernel.perf_event_max_sample_rate to 50000
[ 5209.639310] perf: interrupt took too long (4967 > 4942), lowering kernel.perf_event_max_sample_rate to 40000
[540010.273575] rk_gmac-dwmac fe1b0000.ethernet end0: Link is Down
[540013.347411] rk_gmac-dwmac fe1b0000.ethernet end0: Link is Up - 100Mbps/Full - flow control rx/tx
# ^^^^^^^^ ^^^^^^
# | +-- 双工模式(Full=全双工)
# +-- 链路速度(100Mbps)
提示 :可以使用
dmesg -w实时查看内核日志输出(类似tail -f)。
7. Magic SysRq 魔术键
Magic SysRq 是内核内置的紧急调试工具,即使系统看似挂死,只要内核还在运行就能响应。
7.1 启用方式
bash
# 查看当前 sysrq 功能掩码 (16=允许同步等操作)
root@board:~# cat /proc/sys/kernel/sysrq
16
值含义:
0=禁用,1=全部启用,>1=按位掩码启用
7.2 常用命令 (通过 /proc/sysrq-trigger)
bash
# 查看当前所有任务列表(输出会进dmesg)
root@board:~# echo t > /proc/sysrq-trigger
# 输出被写入 dmesg,分两部分:
# 第一部分:每个进程一行简略信息
root@board:~# dmesg | grep 'task:' | head -5
task:systemd state:S stack: 0 pid: 1 ppid: 0 flags:0x00000004
task:rcu_preempt state:I stack: 0 pid: 13 ppid: 2 flags:0x00000000
task:ksoftirqd/1 state:S stack: 0 pid: 27 ppid: 2 flags:0x00000000
task:spi1 state:S stack: 0 pid: 288 ppid: 2 flags:0x00000000
task:bash state:S stack: 0 pid:728080 ppid:727954 flags:0x00000000
# 解读: task=进程名 | state:S(Sleep)/R(Running)/D(不可中断)/I(Idle)/Z(Zombie)
# | stack=内核栈使用量(bytes) | pid/ppid=进程/父进程ID
# 第二部分:每个CPU的调度器状态 + 可运行任务列表
root@board:~# dmesg | grep -A 20 'runnable tasks:' | head -25
runnable tasks:
S task PID tree-key switches prio wait-time sum-exec sum-sleep
-------------------------------------------------------------------------------------------------------
S cpuhp/6 41 442.071844 9 120 0.000000 0.156627 0.000000 /
S migration/6 42 0.000000 63973 0 0.000000 713.687594 0.000000 /
S ksoftirqd/6 43 12802026.988321 230735 120 0.000000 9689.865378 0.000000 /
I kworker/u17:1 287 531.362388 2 100 0.000000 0.017208 0.000000 /
>R systemd-journal 317 12802494.012100 11202 120 0.000000 1452.889992 0.000000 /
# 第一列: 状态(S=Sleep,R=Running,I=Idle,D=不可中断,>=正在运行)
# tree-key: 调度虚拟时间 | switches: 上下文切换次数 | prio: 优先级(120=普通,99=RT)
# wait-time: 总等待时间 | sum-exec: 总执行时间 | sum-sleep: 总睡眠时间(均为秒)
# 查看当前内存信息
root@board:~# echo m > /proc/sysrq-trigger
root@board:~# dmesg | grep -E 'DMA free|Normal free|Free swap' | head -5
[692516.105443] DMA free:3652136kB min:30600kB low:34420kB high:38240kB ...
[692516.105496] Normal free:4548kB min:4420kB low:4580kB high:4740kB ...
[692516.105515] DMA: 5996*4kB (UMEC) 11909*8kB (UMEC) 11556*16kB (UMEC) ...
[692516.105603] Free swap = 0kB
# 输出各内存区域的空闲/最小/低/高水位线,以及内存碎片情况
# 查看所有CPU的栈回溯(排查CPU卡死、死锁)
root@board:~# echo l > /proc/sysrq-trigger
root@board:~# dmesg | grep -A 10 'Call trace:'
Call trace:
secondary_start_kernel+0x134/0x194
cpu_startup_entry+0x30/0x80
cpuidle_enter_state+0xcc/0x430
cpuidle_enter+0x40/0x6c
call_cpuidle+0x3c/0xa0
do_idle+0x28c/0x340
cpu_startup_entry+0x30/0x80
rest_init+0xdc/0x100
start_kernel+0x5b4/0x600
# 输出每个CPU当前执行的函数调用栈,可以帮助定位哪个CPU卡在哪个函数里
# 上例显示所有CPU都在idle循环(do_idle),系统正常
# 显示所有hrtimer信息
root@board:~# echo q > /proc/sysrq-trigger
root@board:~# dmesg | tail -5
[692527.186507] clock 6:
[692527.186521] clock 7:
[692527.186531] ktime_get_clocktai
# 显示所有D状态(阻塞)任务
root@board:~# echo w > /proc/sysrq-trigger
root@board:~# dmesg | tail -3
[693439.865144] sysrq: Show Blocked State
# 如果没有任何D状态任务,就只有这行提示
# 设置控制台日志级别 (0-9)
echo 9 > /proc/sysrq-trigger # 最高日志级别
echo 0 > /proc/sysrq-trigger # 只有emerg消息
# 紧急同步所有文件系统
echo s > /proc/sysrq-trigger
# 紧急重启(不同步磁盘!)
echo b > /proc/sysrq-trigger
# 触发crash(如果配置了kdump)
echo c > /proc/sysrq-trigger
# 导出ftrace缓冲区
echo z > /proc/sysrq-trigger
7.3 功能说明速查
| 键 | 功能 | 适用场景 |
|---|---|---|
b |
立即重启 | 系统完全挂死 |
c |
触发crash dump | kdump调试 |
d |
显示所有锁 | 死锁诊断 |
e |
发SIGTERM给所有进程 | 清理失控进程 |
f |
触发OOM killer | 内存耗尽恢复 |
i |
发SIGKILL给所有进程 | 进程完全失控 |
l |
所有CPU栈回溯 | 定位CPU卡住位置 |
m |
输出内存信息 | 内存调试 |
q |
显示hrtimer信息 | 定时器调试 |
s |
同步挂载的文件系统 | 紧急同步 |
t |
显示所有任务 | 查看进程状态 |
w |
显示D状态任务 | 排查hung task |
z |
导出ftrace缓冲区 | ftrace数据抢救 |
0-9 |
设置console loglevel | 控制日志输出量 |
8. Dynamic Debug 动态调试
Dynamic Debug 允许在运行时动态启用/禁用内核中的 pr_debug() / dev_dbg() / print_hex_dump_debug() 打印,无需重新编译内核。
8.1 检查是否可用
bash
root@board:~# ls /proc/dynamic_debug/control
/proc/dynamic_debug/control
如果存在此文件,说明内核已启用
CONFIG_DYNAMIC_DEBUG。
8.2 查看所有可控制的调试点
bash
# 统计数量
root@board:~# cat /proc/dynamic_debug/control | wc -l
6608
# 查看前几行了解格式
root@board:~# cat /proc/dynamic_debug/control | head -5
# filename:lineno [module]function flags format
net/mac80211/main.c:1283 [mac80211]ieee80211_register_hw =p "copying sband ..."
net/mac80211/rate.c:291 [mac80211]ieee80211_check_rate_mask =p "%s: no overlap ..."
net/mac80211/mlme.c:1654 [mac80211]ieee80211_handle_pwr_constr =p "%s: Limiting ..."
net/mac80211/mlme.c:1664 [mac80211]ieee80211_handle_pwr_constr =p "%s: Limiting ..."
# 查看 main 模块(内核核心)的调试点
root@board:~# grep 'module main' /proc/dynamic_debug/control | head -5
init/main.c:1178 [main]initcall_blacklist =_ "blacklisting initcall %s\012"
init/main.c:1217 [main]initcall_blacklisted =_ "initcall %s blacklisted\012"
init/main.c:1423 [main]run_init_process =_ " with arguments:\012"
init/main.c:1425 [main]run_init_process =_ " %s\012"
init/main.c:1426 [main]run_init_process =_ " with environment:\012"
8.3 启用/禁用调试打印
bash
# 启用某个文件的pr_debug
echo 'file svcsock.c +p' > /proc/dynamic_debug/control
# 启用某个模块的所有pr_debug
echo 'module xfs +p' > /proc/dynamic_debug/control
# 启用某个函数中的pr_debug
echo 'func svc_process +p' > /proc/dynamic_debug/control
# 禁用
echo 'module xfs -p' > /proc/dynamic_debug/control
# 启用并附加函数名和模块名到输出
echo 'func svc_process +pfm' > /proc/dynamic_debug/control
8.4 通过内核启动参数启用
在 bootargs 中添加:
bash
# 在内核启动时就启用某个模块的调试
dyndbg="file ec.c +p"
# 模块特定的调试
xfs.dyndbg="func xfs_bmap* +p"
注意 :你可能需要同时调整
loglevel才能在控制台看到输出。
9. printk 内核日志系统
9.1 控制台日志级别
bash
root@board:~# cat /proc/sys/kernel/printk
7 4 1 7
四列分别表示:控制台日志级别、默认消息日志级别、最小控制台级别、默认控制台级别。
| 值 | 级别 | 含义 |
|---|---|---|
| 0 | KERN_EMERG | 系统不可用 |
| 1 | KERN_ALERT | 必须立即处理 |
| 2 | KERN_CRIT | 严重条件 |
| 3 | KERN_ERR | 错误条件 |
| 4 | KERN_WARNING | 警告条件 |
| 5 | KERN_NOTICE | 正常但重要 |
| 6 | KERN_INFO | 信息性 |
| 7 | KERN_DEBUG | 调试级别 |
9.2 控制台日志控制
bash
# 设置控制台日志级别(数字越小越紧急)
echo 8 > /proc/sys/kernel/printk # 显示所有消息包括debug
echo 3 > /proc/sys/kernel/printk # 只显示ERR及以上
# 启动时忽略日志级别(显示所有内核消息)
# 在 bootargs 中添加:ignore_loglevel
# 增大内核日志缓冲区(启动参数)
# log_buf_len=4M
# 在Oops/panic时自动升高日志级别
# printk.always_kmsg_dump=1
9.3 从用户空间写入内核日志
bash
echo "my_debug_tag: test message" > /dev/kmsg
9.4 内核崩溃时调试辅助
bash
# panic时打印所有任务信息
# panic_print=1 在bootargs中添加
# 触发panic后自动重启(秒数)
echo 10 > /proc/sys/kernel/panic
# panic或oops时自动dump ftrace(启动参数)
# ftrace_dump_on_oops=1
10. lockup 检测器
内核内置了软锁(soft lockup)和硬锁(hard lockup/NMI watchdog)检测器。
10.1 检查是否启用
bash
# 当前内核未编译 LOCKUP_DETECTOR,相关文件不存在
root@board:~# cat /proc/sys/kernel/watchdog_thresh
cat: /proc/sys/kernel/watchdog_thresh: No such file or directory
root@board:~# cat /proc/sys/kernel/hung_task_timeout_secs
cat: /proc/sys/kernel/hung_task_timeout_secs: No such file or directory
# 检查内核编译选项
root@board:~# zcat /proc/config.gz 2>/dev/null | grep LOCKUP
# CONFIG_LOCKUP_DETECTOR is not set
说明 :当前 TL3588 内核未启用
CONFIG_LOCKUP_DETECTOR。如需使用需重新编译内核。
10.2 启用方法 (内核重编译)
在 defconfig 中添加:
bash
CONFIG_LOCKUP_DETECTOR=y
CONFIG_SOFTLOCKUP_DETECTOR=y
CONFIG_HARDLOCKUP_DETECTOR=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
10.3 启用后的常用命令
bash
# 查看/设置阈值
cat /proc/sys/kernel/watchdog_thresh # 默认10秒
echo 30 > /proc/sys/kernel/watchdog_thresh # 改为30秒
# 配置 panic 行为
echo 1 > /proc/sys/kernel/softlockup_panic # softlockup时panic
echo 0 > /proc/sys/kernel/nmi_watchdog # 禁用hardlockup检测
# hung task 检测 (需 CONFIG_DETECT_HUNG_TASK=y)
cat /proc/sys/kernel/hung_task_timeout_secs # 默认120秒
echo 1 > /proc/sys/kernel/hung_task_panic # 检测到hung task时panic
# dmesg 检测到 lockup 时的输出示例
# watchdog: BUG: soft lockup - CPU#7 stuck for 22s! [stress-ng:1234]
# INFO: task bash:12345 blocked for more than 120 seconds.
10.4 替代方案:SysRq 手动排查
即使没有 lockup 检测器,仍可用 SysRq 排查:
bash
# 查看所有任务状态(含 D 状态)
root@board:~# echo t > /proc/sysrq-trigger
root@board:~# dmesg | grep -E 'task:|state:D' | head -10
# 输出示例:
# sysrq: Show State
# task:systemd state:S stack:0 pid:1 ppid:0 flags:0x00000004
# 查看所有 CPU 栈回溯
root@board:~# echo l > /proc/sysrq-trigger
root@board:~# dmesg | grep -A 20 'CPU#0' | head -25
11. KASAN/KFENCE/kmemleak 内存调试
11.1 检查可用性
bash
# kmemleak: 当前内核未编译 CONFIG_DEBUG_KMEMLEAK
root@board:~# ls /sys/kernel/debug/kmemleak 2>/dev/null
ls: /sys/kernel/debug/kmemleak: No such file or directory
# page_owner: 当前内核未启用
root@board:~# ls /sys/kernel/debug/page_owner 2>/dev/null
ls: /sys/kernel/debug/page_owner: No such file or directory
# 内核 debug 功能总览
root@board:~# ls /sys/kernel/debug/ | head -15
asoc dri kprobes
bdi dma_buf mmc0
block dmaengine pm_qos
bluetooth dma_pools regmap
cec extfrag
11.2 KFENCE (低开销内存错误检测器)
需要在 bootargs 中添加启动参数:
bash
# 启用 KFENCE,100ms 采样间隔
kfence.sample_interval=100
# 检测到错误时的 dmesg 输出示例:
# ==================================================================
# BUG: KFENCE: use-after-free in xxx+0xYY/0xZZ
# ==================================================================
注意:需要内核开启
CONFIG_KFENCE=y。
11.3 kmemleak (内核内存泄漏检测)
需要在 bootargs 中添加启用:
bash
# 启动参数启用
kmemleak=on
# 运行时触发扫描
echo scan > /sys/kernel/debug/kmemleak
# 查看泄漏报告
cat /sys/kernel/debug/kmemleak
# 启动时禁用(如果编译了但不想用)
# kmemleak=off
注意:需要内核开启
CONFIG_DEBUG_KMEMLEAK=y。
11.4 slab 调试
bash
# 查看 slab 分配器信息(无需额外内核选项)
root@board:~# cat /proc/slabinfo | head -8
slabinfo - version: 2.1
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables ...
ext4_groupinfo_4k 56 56 144 28 1 : tunables 0 0 0 : slabdata 2 2 0
# ^^ ^^ ^^^ ^^ ^
# | | | | +-- 每个slab占用页数(1页=4KB)
# | | | +-- 每个slab中对象数(28个)
# | | +-- 每个对象大小(144字节)
# | +-- 已分配的对象数(56个)
# +-- 活跃对象数(56个,都在用)
btrfs_path 0 0 112 36 1 : tunables 0 0 0 : slabdata 0 0 0
# ^ ^ ^ ^
# | | | +-- 对象大小(112字节)
# | | +-- 预留对象数
# | +-- 活跃对象数(0表示无btrfs活动)
# +-- slab缓存名称(按用途命名)
# 查看 slab 缓存使用详情
root@board:~# cat /sys/kernel/slab/kmalloc-128/alloc_calls 2>/dev/null | head -5
# 启动参数启用 slab 检查
# slab_debug=zp (z=poison对象, p=追踪alloc/free)
12. RCU 调试与测试
RCU(Read-Copy-Update)是内核的核心同步机制,以下参数用于调试RCU相关问题。
12.1 RCU CPU Stall 检测
bash
# 查看当前RCU stall超时设置
# 注意:当前内核未导出此 sysctl
root@board:~# cat /proc/sys/kernel/rcu_cpu_stall_timeout
cat: /proc/sys/kernel/rcu_cpu_stall_timeout: No such file or directory
# 通过内核启动参数设置
# rcupdate.rcu_cpu_stall_timeout=30 # 30秒超时
# 抑制RCU stall告警
# rcupdate.rcu_cpu_stall_suppress=1
# 发生RCU stall时dump ftrace
# rcupdate.rcu_cpu_stall_ftrace_dump=1
12.2 RCU 相关内核参数 (bootargs)
bash
# 将CPU7的RCU回调卸载到其他CPU(减少隔离核抖动)
rcu_nocbs=7
# 设置RCU kthread优先级
# rcutree.kthread_prio=1 # SCHED_FIFO 优先级1
# 强制使用expedited RCU(减少延迟)
# rcupdate.rcu_expedited=1
# 启动RCU自检
# rcupdate.rcu_self_test=1
12.3 在 /proc 中查看RCU状态
bash
root@board:~# cat /proc/stat | grep -E 'cpu.*'
# CPU时间中的softirq包含了RCU处理时间
root@board:~# cat /proc/softirqs
# RCU软中断在每个CPU上的分布
13. 内核启动参数调试
以下启动参数可在 U-Boot 的 bootargs 中使用,用于调试内核启动问题。
13.1 跟踪启动过程
bash
# 显示所有initcall执行过程
initcall_debug
# 只跟踪特定函数(需FTRACE支持)
ftrace=function
ftrace_filter="initcall*"
# 启动时分配ftrace快照缓冲区(可在启动完成后读取snapshot)
alloc_snapshot
# 启用特定trace event
trace_event=sched:sched_switch,irq:irq_handler_entry
# 禁用KASLR(便于地址关联调试信息)
nokaslr
# 忽略日志级别(显示所有日志)
ignore_loglevel
13.2 调试特定子系统
bash
# ACPI调试
acpi.debug_layer=0xffffffff acpi.debug_level=0xffffffff
# 调度器调试
sched_verbose
# 内存初始化调试
mminit_loglevel=4
# DMA API调试
dma_debug=on
# 中断调试
# irqhandler.duration_warn_us=1000 # IRQ处理超过1000us时告警
13.3 内核崩溃/Oops 调试
bash
# 在Oops时panic
oops=panic
# WARN()时panic
panic_on_warn=1
# panic时打印所有任务信息
panic_print=0x7f # 打印任务/内存/定时器/锁/ftrace等信息
# panic后自动重启
panic=10 # 10秒后重启
# 崩溃时自动dump ftrace缓冲区
ftrace_dump_on_oops=1
# 禁用哈希指针(显示真实地址)
no_hash_pointers
13.4 TL3588 常用调试 bootargs 组合
bash
# 轻度调试:只增加启动跟踪
bootargs=... initcall_debug ignore_loglevel
# 中度调试:增加ftrace和panic后重启
bootargs=... initcall_debug ignore_loglevel ftrace_dump_on_oops panic=10
# 重度调试:全部开启
bootargs=... initcall_debug ignore_loglevel ftrace_dump_on_oops panic=10 nokaslr no_hash_pointers panic_on_warn=1 oops=panic
# 检查当前cmdline中是否包含调试参数
dmesg | grep "command line"
14. taskset CPU 亲和性
将进程绑定到指定 CPU 运行。
7.1 启动时绑定
bash
# 将进程绑定到 CPU7
taskset -c 7 /usr/local/bin/cyclictest -p 99 -m -a 7 -t 1 -D 60s
# 将 stress 绑定到 CPU0-6
taskset -c 0-6 /usr/local/bin/stress-ng --cpu 7 --io 7 --timeout 30s
7.2 运行时绑定
bash
# 查看进程当前 CPU 亲和性
taskset -pc <PID>
# 运行时修改
taskset -pc 0-6 <PID>
7.3 实际测试场景
这是 cache-color 项目中 cyclictest 的标准用法:
bash
# 隔离核 CPU7 运行 cyclictest,主线程绑定到 0-6
taskset -c 7 /usr/local/bin/cyclictest -p 99 -m -q -a 7 --mainaffinity=0-6 -t 1 -i 200 -D 60
# housekeeping CPU 0-6 运行压力
taskset -c 0-6 /usr/local/bin/stress-ng --cpu 7 --io 7 --vm 7 --vm-bytes 80% --hdd 7 --hdd-bytes 512M --timeout 70s
15. chrt 实时调度策略
15.1 查看进程调度策略
bash
root@board:~# chrt -p 1
pid 1's current scheduling policy: SCHED_OTHER
pid 1's current scheduling priority: 0
15.2 修改为 FIFO 实时调度
bash
# 将 ksoftirqd/7 设为 SCHED_FIFO 优先级 1
root@board:~# chrt -f -p 1 $(pgrep -x 'ksoftirqd/7')
15.3 实际场景(rt_init.sh 中的用法)
bash
# 提升隔离核关键内核线程到实时调度
for tname in "ksoftirqd/7" "rcuc/7" "irq_work/7"; do
pid=$(pgrep -x "$tname" 2>/dev/null)
[ -n "$pid" ] && chrt -f -p 1 $pid
done
16. cyclictest 实时延迟测试
cyclictest 是 RT 测试的核心工具,用于测量系统的调度延迟。
16.1 基本用法
bash
# CPU7 上运行,周期 200us,持续 60 秒
root@board:~# /usr/local/bin/cyclictest -p 99 -m -q -a 7 --mainaffinity=0-6 -t 1 -i 200 -D 60s
T: 0 ( 560) P:99 I:200 C: 300000 Min: 1 Act: 2 Avg: 2 Max: 97
输出字段 :
T=线程编号,P=优先级,I=间隔(us),C=计数,Min/Act/Avg/Max=最小/当前/平均/最大延迟(us)
16.2 参数说明
| 参数 | 含义 |
|---|---|
-p 99 |
实时优先级 99 (最高) |
-m |
锁定内存 (mlockall) |
-q |
安静模式,只输出最终统计 |
-a 7 |
绑定到 CPU7 |
--mainaffinity=0-6 |
主线程绑定到 CPU0-6 |
-t 1 |
1 个测试线程 |
-i 200 |
间隔 200 微秒 |
-D 60s |
持续 60 秒 |
-h 71 |
直方图桶数 71 |
17. stress-ng 压力测试
17.1 常用压力模式
bash
# CPU 压力
stress-ng --cpu 7 --cpu-method matrixprod --timeout 30s
# 内存压力 (80%)
stress-ng --vm 7 --vm-bytes 80% --timeout 30s
# IO 压力
stress-ng --io 7 --hdd 7 --hdd-bytes 512M --timeout 30s
# 缓存抖动
stress-ng --cache 7 --timeout 30s
# 混合压力 (cache-color 项目标准配置)
stress-ng --cpu 7 --io 7 --vm 7 --vm-bytes 80% --hdd 7 --hdd-bytes 512M
17.2 配合隔离核使用
bash
# 压力运行在 housekeeping CPU,隔离核不受影响
taskset -c 0-6 /usr/local/bin/stress-ng --cpu 7 --io 7 --vm 7 --vm-bytes 80% --hdd 7 --hdd-bytes 512M --timeout 70s
18. fw_printenv/fw_setenv U-Boot 环境变量
18.1 查看环境变量
bash
root@board:~# fw_printenv | head -10
arch=arm # CPU架构
baudrate=115200 # 串口波特率
board=evb_rk3588 # 板卡型号
board_name=evb_rk3588 # 板卡名称
boot_partition=bootA # 启动分区(双分区切换)
boot_targets=mmc1 mmc0 nvme scsi usb pxe dhcp spi # 启动设备搜索顺序
bootcmd=fboot # U-Boot启动命令
bootdelay=0 # 启动延迟(秒),0=不等待
cpu=armv8 # CPU架构
fdt_addr_r=0x12000000 # 设备树(Device Tree)加载地址
18.2 查看和设置 bootargs (内核启动参数)
bash
# 查看所有与 bootargs 相关的环境变量
root@board:~# fw_printenv | grep bootargs
bootargs_iso=earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=/dev/mmcblk0p5 rw rootwait nohz=on nohz_full=7 isolcpus=domain,managed_irq,7 rcu_nocbs=7 irqaffinity=0-6
bootargs_iso_color=earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=/dev/mmcblk0p5 rw rootwait nohz=on nohz_full=7 isolcpus=domain,managed_irq,7 rcu_nocbs=7 irqaffinity=0-6 cache_color_num_colors=8 cache_color_iso_cpu=7 cache_color_iso_color=1 cache_color_iso_count=1 cache_color_iso_strict=1
bootargs=earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=/dev/mmcblk0p5 rw rootwait nohz=on nohz_full=7 isolcpus=domain,managed_irq,7 rcu_nocbs=7 irqaffinity=0-6
# 设置新的 bootargs
root@board:~# fw_setenv bootargs "<新的启动参数>"
# 例如:启用 cache-color 隔离
root@board:~# fw_setenv bootargs "earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=/dev/mmcblk0p5 rw rootwait nohz=on nohz_full=7 isolcpus=domain,managed_irq,7 rcu_nocbs=7 irqaffinity=0-6 cache_color_num_colors=8 cache_color_iso_cpu=7 cache_color_iso_color=1 cache_color_iso_count=1 cache_color_iso_strict=1"
设置 bootargs 后需要重启板子才能生效。
19. proc/sys 内核参数调优
实时优化常用的内核参数调整(来自 rt_init.sh):
19.1 内核调度参数 (查看当前值)
bash
root@board:~# cat /proc/sys/kernel/timer_migration
0 # 0=禁用timer迁移到隔离核
root@board:~# cat /proc/sys/kernel/sched_rt_runtime_us
-1 # -1=无限制, RT进程可用100% CPU
root@board:~# cat /proc/sys/vm/swappiness
0 # 0=不主动使用swap
root@board:~# cat /proc/sys/kernel/sched_schedstats
0 # 0=禁用调度统计(减少开销)
19.2 中断亲和性
bash
# 查看默认中断亲和性
root@board:~# cat /proc/irq/default_smp_affinity
7f # 0x7f = CPU0-6
# 查看某个具体中断的亲和性
root@board:~# cat /proc/irq/11/smp_affinity
7f # arch_timer 中断被限制在 CPU0-6
19.3 workqueue 绑定
bash
# 查看当前 workqueue CPU 掩码
root@board:~# cat /sys/devices/virtual/workqueue/cpumask
7f # 0x7f = CPU0-6, 不会在 CPU7 上运行
# 手动设置
echo "7f" > /sys/devices/virtual/workqueue/cpumask
19.4 VM 统计间隔
bash
root@board:~# cat /proc/sys/vm/stat_interval
120 # 120秒更新一次vmstat
root@board:~# cat /proc/sys/vm/dirty_writeback_centisecs
1500 # 1500厘秒=15秒回写一次脏页
root@board:~# cat /proc/sys/kernel/perf_event_max_sample_rate
40000 # perf事件最大采样率
19.5 查看所有调度相关参数
bash
root@board:~# ls /proc/sys/kernel/ | grep -E 'sched|timer|rt'
sched_cfs_bandwidth_slice_us
sched_child_runs_first
sched_deadline_period_max_us
sched_deadline_period_min_us
sched_domain
sched_energy_aware
sched_latency_ns
sched_migration_cost_ns
sched_min_granularity_ns
sched_nr_migrate
sched_rr_timeslice_ms
sched_rt_period_us
sched_rt_runtime_us
sched_schedstats
sched_tunable_scaling
sched_util_clamp_max
sched_util_clamp_min
sched_util_clamp_min_rt_default
sched_wakeup_granularity_ns
timer_migration
19.6 systemd 服务隔离
将系统服务限制到指定的 CPU 集合:
bash
# 限制 system 和 user 服务到 housekeeping CPU
systemctl set-property --runtime system.slice AllowedCPUs="0-6"
systemctl set-property --runtime user.slice AllowedCPUs="0-6"
systemctl set-property --runtime init.scope AllowedCPUs="0-6"
# 对所有 service 单元逐一设置
for svc in $(systemctl list-units --type=service --all --no-legend --no-pager | awk '{print $1}'); do
[ -n "$svc" ] && systemctl set-property --runtime "$svc" AllowedCPUs="0-6"
done
20. 自定义调试工具
20.1 l3-victim-chase --- LLC 命中率测量
在隔离核上运行,测量特定工作集大小的 LLC 命中率。
bash
# 测量 384KB 工作集,步长 64B,持续 120 秒,锁定内存
root@board:~# /usr/local/bin/l3-victim-chase --size-bytes 393216 --duration 120 --stride 64 --mlock
size_bytes=393216
stride_bytes=64
duration_s=120
iterations=4879526255
checksum=...
| 参数 | 说明 |
|---|---|
--size-bytes |
工作集大小 (字节),393216 = 384KB |
--duration |
测试持续时间 (秒) |
--stride |
访问步长 (字节),64B = 1 cache line |
--mlock |
锁定内存防止换出 |
20.2 l3-hk-aggressor --- LLC 压力生成
在 housekeeping CPU 上运行,产生缓存压力。
bash
# 7 个线程,48MB 工作集,步长 128B,读写模式
root@board:~# /usr/local/bin/l3-hk-aggressor --threads 7 --size-bytes 50331648 --stride 128 --mode rw --timeout 130s
| 参数 | 说明 |
|---|---|
--threads |
线程数 |
--size-bytes |
工作集大小,50331648 = 48MB |
--stride |
访问步长 |
--mode |
read/rw/chase |
--timeout |
超时时间 |
21.5 缓存命中率计算公式
cache-hit-pct = (1 - cache-misses / cache-references) × 100%
llc-load-hit-pct = (1 - LLC-load-misses / LLC-loads) × 100%