系统卡顿终极诊断:eBPF + ftrace 追踪不可中断进程(D 状态)阻塞链
扫描二维码
随时随地手机看文章
在 Linux 系统运维过程中,系统卡顿是一个令人头疼的问题。当系统出现卡顿时,用户界面无响应、服务延迟增加,严重时甚至会导致业务中断。不可中断进程(处于 D 状态)往往是系统卡顿的“罪魁祸首”之一。这些进程由于等待某些硬件资源(如磁盘 I/O、网络 I/O 等)而无法被信号中断,从而阻塞了整个系统的正常运行。本文将介绍如何利用 eBPF 和 ftrace 这两大强大的工具,追踪不可中断进程的阻塞链,精准定位系统卡顿的根源。
不可中断进程(D 状态)概述
在 Linux 中,进程有多种状态,其中 D 状态(TASK_UNINTERRUPTIBLE)表示进程处于不可中断的睡眠状态。处于 D 状态的进程通常在等待硬件资源,例如磁盘读写操作完成。与可中断状态(S 状态)不同,D 状态的进程不会响应任何信号,包括 SIGKILL 信号,这使得它们难以被强制终止,也增加了系统卡顿排查的难度。
eBPF 与 ftrace 简介
eBPF
eBPF(extended Berkeley Packet Filter)是一种强大的内核技术,它允许用户在内核中运行沙箱化的程序,而无需修改内核代码或加载内核模块。eBPF 程序可以附加到内核的各种钩子点上,如系统调用入口、网络数据包处理路径等,从而实现对系统行为的实时监控和分析。
ftrace
ftrace 是 Linux 内核提供的一个内置跟踪框架,它可以用于跟踪内核函数的调用、执行时间等信息。ftrace 提供了多种跟踪工具和接口,如 function_graph、function 等,可以帮助开发者和运维人员深入了解内核的运行情况。
使用 eBPF 追踪不可中断进程
编写 eBPF 程序
以下是一个简单的 eBPF 程序示例,用于捕获处于 D 状态的进程信息:
c
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct data_t {
u32 pid;
char comm[TASK_COMM_LEN];
u64 state;
};
BPF_PERF_OUTPUT(events);
int trace_sched_process_exec(struct pt_regs *ctx) {
struct task_struct *task;
struct data_t data = {};
task = (struct task_struct *)bpf_get_current_task();
data.pid = task->pid;
bpf_get_current_comm(&data.comm, sizeof(data.comm));
data.state = task->state;
if (data.state == TASK_UNINTERRUPTIBLE) {
events.perf_submit(ctx, &data, sizeof(data));
}
return 0;
}
加载 eBPF 程序
可以使用 BCC(BPF Compiler Collection)工具来加载上述 eBPF 程序。以下是一个 Python 脚本示例:
python
from bcc import BPF
# 加载 eBPF 程序
b = BPF(src_file="d_state_trace.c")
# 定义回调函数处理捕获的事件
def print_event(cpu, data, size):
event = b["events"].event(data)
print(f"PID: {event.pid}, Command: {event.comm.decode()}, State: {event.state}")
# 关联回调函数和性能事件
b["events"].open_perf_buffer(print_event)
# 启动跟踪
while True:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
exit()
运行上述 Python 脚本后,当有进程进入 D 状态时,会输出进程的 PID、命令名和状态信息。
使用 ftrace 追踪阻塞链
启用 ftrace 跟踪
可以使用以下命令启用 ftrace 的 function_graph 跟踪,以跟踪内核函数的调用关系:
bash
# 进入 ftrace 目录
cd /sys/kernel/debug/tracing
# 设置跟踪选项
echo function_graph > current_tracer
# 设置要跟踪的函数(这里以磁盘 I/O 相关函数为例)
echo "submit_bio* blk_mq_start_request* generic_make_request*" > set_ftrace_filter
# 启用跟踪
echo 1 > tracing_on
分析跟踪结果
当系统出现卡顿时,可以通过以下命令查看跟踪结果:
bash
cat tracing/trace
在跟踪结果中,可以查看进程在进入 D 状态前调用了哪些内核函数,从而分析出阻塞链。例如,如果发现进程在调用 submit_bio 函数后进入 D 状态,并且 submit_bio 函数又调用了 blk_mq_start_request 等函数,那么可以推测是磁盘 I/O 操作导致了进程阻塞。
综合分析与优化
通过 eBPF 和 ftrace 追踪到不可中断进程的阻塞链后,就可以针对性地进行优化。例如,如果是磁盘 I/O 瓶颈导致的卡顿,可以考虑升级磁盘硬件、优化磁盘读写策略或调整系统参数等。
总结
系统卡顿问题往往复杂多样,不可中断进程的阻塞是常见原因之一。利用 eBPF 和 ftrace 这两大工具,可以有效地追踪不可中断进程的阻塞链,精准定位系统卡顿的根源。通过编写 eBPF 程序捕获 D 状态进程信息,再结合 ftrace 跟踪内核函数调用关系,运维人员能够深入了解系统的运行情况,从而采取有效的优化措施,提高系统的稳定性和性能。