Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Red Hat Linux 核心動態追蹤技術:kprobe 與 SystemTap 實戰

編輯:Linux 教學

背景

生產環境核心問題往往難以重現,傳統 printk 重編譯核心週期長、風險高。Linux 核心提供 kprobe(動態插桩)機制,配合 SystemTap 高階腳本語言,實現無重啟、無模組重載的動態觀測。

kprobe 三種探針類型

類型觸發點適用場景存取上下文
kprobe函數入口 (entry)參數檢查、呼叫追蹤、條件觸發函數參數、暫存器
kretprobe函數返回 (return)返回值檢查、執行時間測量、錯誤碼分析返回值、函數參數 (需 entry handler 保存)
jprobe函數入口 (鏡像堆疊)完整參數結構體存取、複雜邏輯注入完整函數參數結構 (需相同原型)

核心模組範例:追蹤 ARP 處理與路由查找

原始碼展示 arp_process、ip_route_input_slow、fib_lookup、fib_validate_source 的進出追蹤:

/* 關鍵結構體定義 */
struct kprobe {
    struct kprobe_ops *ops;
    kprobe_opcode_t *addr;
    const char *symbol_name;  // 以符號名定位,不依賴位址
    unsigned int offset;
    kprobe_pre_handler_t pre_handler;   // entry
    kprobe_post_handler_t post_handler; // return (舊式)
    kprobe_fault_handler_t fault_handler;
};

struct kretprobe {
    struct kprobe kp;
    kretprobe_handler_t handler;        // return handler
    kretprobe_handler_t entry_handler;  // entry handler (可選)
    int maxactive;                      // 並發探針實例上限
    int data_size;                      // entry/return 共享資料大小
};

struct jprobe {
    struct kprobe kp;
    kprobe_opcode_t *entry;             // 鏡像處理函數
    // 需 jprobe_return() 返回
};

註冊與生命週期

int register_kprobe(struct kprobe *kp);
int register_kretprobe(struct kretprobe *rp);
int register_jprobe(struct jprobe *jp);

void unregister_kprobe(struct kprobe *kp);
void unregister_kretprobe(struct kretprobe *rp);
void unregister_jprobe(struct jprobe *jp);

共享資料模式 (kretprobe)

// entry handler 保存指標
static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs) {
    struct fib_result *resp = (struct fib_result *)regs->dx; // 參數在 dx
    *((struct fib_result **)ri->data) = resp;  // 存入共享區
    return 0;
}

// return handler 取用
static int return_handler(struct kretprobe_instance *ri, struct pt_regs *regs) {
    struct fib_result *resp = *((struct fib_result **)ri->data);
    int ret = regs->ax & 0xffff;
    printk("fib_lookup returned %d, type=%d\n", ret, resp->type);
    return 0;
}

// 註冊時指定 data_size
static struct kretprobe rp = {
    .kp.symbol_name = "fib_lookup",
    .entry_handler = entry_handler,
    .handler = return_handler,
    .data_size = sizeof(struct fib_result *),
};

SystemTap:高階腳本語言

SystemTap 將探針邏輯編譯為核心模組,提供類 C/Awk 語法:

# stap -e 'probe kernel.function("sys_open").call { printf("%s(%d) open %s\n", execname(), pid(), user_string($filename)) }'

# 完整腳本範例: trace_open.stp
probe kernel.function("sys_open").call {
    if (pid() == target())
        printf("%s(%d) open(%s, %x, %x)\n", execname(), pid(), user_string($filename), $flags, $mode)
}
probe kernel.function("sys_open").return {
    if (pid() == target())
        printf("  -> fd = %d\n", $return)
}

# 執行
stap -x $(pgrep nginx) trace_open.stp

常用 Tapset 庫

  • kernel.function("name") / .return — 函數進出
  • kernel.trace("tracepoint_name") — 靜態追蹤點
  • syscall.name — 系統呼叫封裝
  • process.begin/end — 程式啟動/結束
  • timer.ms(100) / timer.profile — 定時/效能取樣

實戰場景對照

問題kprobe 方案SystemTap 方案
特定檔案被誰開啟kprobe vfs_open + 檔名比對probe kernel.function("vfs_open").call { if (user_string($filename) ~ /target/) ... }
TCP 連線建立延遲kretprobe tcp_v4_connect + 時間戳probe kernel.function("tcp_v4_connect").return { @lat[pid()] <<< gettimeofday_us() - @entry[pid()] }
記憶體洩漏 (kmalloc/kfree 不配對)kprobe kmalloc/kfree 追蹤 call_siteprobe kernel.function("kmalloc").return { @alloc[caller] <<< $return } probe kernel.function("kfree") { delete @alloc[caller] }
檔案系統延遲kretprobe vfs_read/write + 區塊層追蹤probe ioblock.request { @start[dev, sector] = gettimeofday_us() } probe ioblock.end { @lat[dev] <<< gettimeofday_us() - @start[dev, sector] }

效能與安全考量

  • 開銷:單探針 ~50-200ns,高頻路徑 (如 schedule, do_syscall_64) 慎用
  • 並發:maxactive 預設根據 CPU 核心數,高併發路徑需調大
  • 安全:需 CAP_SYS_ADMIN 或 kernel.kptr_restrict=0,生產環境建議啟用 lockdown 模式限制
  • 穩定性:探針處理函數不可睡眠、不可持有鎖過久、錯誤處理必須完善

現代替代方案:eBPF / bpftrace

Linux 4.1+ 引入 eBPF,提供更安全、可程式化、效能更佳的觀測框架:

# bpftrace 一行指令追蹤 openat
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s(%d) open %s\n", comm, pid, str(args->filename)); }'

# 追蹤 TCP 重傳
bpftrace -e 'kretprobe:tcp_retransmit_skb { @[comm] = count(); } interval:s:5 { print(@); clear(@); }'

# 工具生態
bcc-tools (opensnoop, execsnoop, biolatency, tcplife, runqlat)
bpftrace (腳本語言)
cilium/ebpf (Go 庫)
systemtap 4.0+ 可選用 eBPF 後端

總結

kprobe/SystemTap 是核心工程師的「X 光機」,但門檻高、風險大。現代生產環境優先採用 eBPF/bpftrace/bcc:無需核心模組編譯、安全沙箱、豐富工具生態、支援 Kubernetes 叢集級觀測。保留 kprobe 技能用於:核心開發除錯、舊核心 (pre-4.1) 環境、極度底層路徑 (eBPF 尚不支援)。

Copyright © Windows教程網 All Rights Reserved