讲师中心 微信公众号

工具推荐

视频效率加速
updream

一键生成高质量推广成片

AniShort
AniShort

AI短剧协同制作平台

立刻MV
立刻MV

低成本歌曲 MV、视频生成

剧云
剧云

中文短剧剧本制作平台

Python asyncio 事件循环的 eBPF 跟踪

秋雪吖_8591

秋雪吖_8591

发布时间:2026-02-25 19:33:16

|

545人浏览过

|

来源于php中文网

原创

eBPF 通过跟踪 epoll_wait 等系统调用定位 asyncio 事件循环卡点,需结合 Python 启动时记录的 epoll_fd 进行精准过滤,并关注 timerfd、signalfd 等非网络 fd,推荐使用 libbpf + CO-RE 实现跨内核版本稳定追踪。

python asyncio 事件循环的 ebpf 跟踪

asyncio 事件循环卡住时怎么用 eBPF 看清它在等谁

eBPF 本身不理解 Python 的 asyncio 概念,它只能跟踪系统调用和内核事件。想看到事件循环“卡在哪”,得聚焦到它实际依赖的底层:主要是 epoll_wait(Linux)、kqueue(macOS)或 select(fallback)。一旦事件循环阻塞在这些系统调用里,eBPF 就能捕获到。

常见错误现象是服务 CPU 占用极低但请求不响应——这往往说明 asyncio.run() 或 loop.run_forever() 正停在 epoll_wait 上,而没人往 fd 写数据、没定时器到期、也没新任务提交。

  • 用 bpftool prog list 确认你的 tracepoint 是否已加载(比如 syscalls/sys_enter_epoll_wait)
  • 优先跟踪 syscalls/sys_enter_epoll_wait 和 syscalls/sys_exit_epoll_wait,对比耗时,确认是否真卡住而非正常等待
  • 别试图 hook asyncio 的 Python 层函数(如 loop._run_once),eBPF 无法安全读取 CPython 解释器栈帧,容易 crash 或返回垃圾值
  • 注意 Python 进程可能 fork 出多个子进程(比如用 multiprocessing 启动 worker),eBPF 默认只跟踪指定 PID,需用 pid == 0 或动态过滤

如何让 eBPF 程序识别出这是 asyncio 的 epoll 实例

Linux 下 asyncio 默认用 epoll_create1(0) 创建实例,但 eBPF 没法直接判断某个 epoll_fd 是不是被 Python 用了。可行办法是结合用户态上下文:在 Python 启动时记录它的 epoll_fd 值,再让 eBPF 只对这个 fd 生效。

使用场景通常是调试线上服务,你没法改业务代码,但可以加一行启动日志:

立即学习“Python免费学习笔记(深入)”;

Python Hardener
Python Hardener

通过完整的安全审查、代码整洁重构、健壮的错误处理、正确的日志记录以及完整的文档字符串覆盖,强化 Python 脚本。

下载
import asyncio
import os
loop = asyncio.get_event_loop()
# 打印 epoll fd(仅 Linux)
if hasattr(loop, '_selector') and hasattr(loop._selector, '_epoll'):
    print(f"EPOLL_FD={loop._selector._epoll._epollfd}", file=open("/tmp/asyncio-epoll-fd", "w"))
  • eBPF 程序里用 bpf_map_lookup_elem(&target_epoll_fds, &fd) 查表,只对已知 fd 做深度追踪
  • 不要硬编码 fd 值——进程重启后 fd 会变,必须每次启动重读 /tmp/asyncio-epoll-fd
  • 如果用 uvloop,它的 epoll fd 存在 loop._selector._epoll._fd,路径略有不同
  • Windows 不适用这套逻辑,asyncio 在 Windows 用 overlapped I/O,eBPF 无法跟踪

跟踪 await 点对应的文件描述符时容易漏掉什么

await 本身不产生系统调用,真正触发的是它背后对象的 __await__ 方法——比如 asyncio.StreamReader.read() 最终调用 recvfrom,而 asyncio.sleep() 则靠 timerfd_settime 驱动。eBPF 跟不到 await 关键字,但能抓到这些底层 fd 操作。

容易踩的坑是只盯着网络 fd,忽略 timerfd 和 signalfd:

  • asyncio.sleep()、loop.call_later() 依赖 timerfd,需跟踪 syscalls/sys_enter_timerfd_settime
  • 如果你用 loop.add_signal_handler(),信号处理走 signalfd,也要单独 hook
  • HTTP 客户端(如 aiohttp)可能用 socketpair 做内部唤醒,这类 fd 很短命,eBPF 要开足够大的 perf buffer,否则丢事件
  • 避免用 bpf_trace_printk() 打印大量内容,它会严重拖慢 epoll_wait 返回,反而掩盖真实问题

为什么用 libbpf + CO-RE 比 BCC 更适合 asyncio 场景

BCC 编译期绑定内核头,而 asyncio 服务常跑在容器里,宿主机和容器内核版本可能不一致;libbpf + CO-RE(Compile Once – Run Everywhere)能在运行时适配结构体偏移,更稳。

性能上差异明显:BCC 的 Python 层要频繁调用 bpf_perf_event_read() 拉数据,而 libbpf 可以用 ringbuf 零拷贝传给用户态,对高吞吐 asyncio 服务更友好。

  • CO-RE 要求目标机器有 vmlinux.h(可通过 bpftool btf dump file /sys/kernel/btf/vmlinux format c 生成)
  • libbpf 加载程序时默认不验证 map 大小,务必手动检查 struct { __u64 fd; __u64 ts; } __attribute__((packed)) 这类结构体是否被编译器 padding
  • 别用 BCC 的 trace.py 一类脚本临时跟踪——它们启动慢、hook 粒度粗,容易错过毫秒级的 epoll 唤醒抖动
  • 如果用 pyperf 或 perf record -e 'syscalls:sys_enter_*' 辅助验证,注意 perf event 和 eBPF tracepoint 的采样精度差异

最麻烦的其实是 Python 解释器自身的内存布局干扰:CPython 的 GIL、gc、以及 asyncio 对 PyThreadState 的修改,会让 eBPF 读取用户栈变得不可靠。所以别指望 eBPF 能告诉你 “卡在 await db.fetch() 这一行”,它能告诉你的上限是 “卡在 epoll_wait,当前注册了 3 个 socket fd 和 1 个 timerfd”。剩下的,得靠日志和 fd 对应关系反推。

热门AI工具

更多
Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

码上飞
码上飞 Hot

一款基于自然语言的AI应用开发平台,可根据用户描述自动生成应用程序,帮助个人和团队快速完成应用原型与开发。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

1849

2023.07.31

python中的format是什么意思
python中的format是什么意思

python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1231

2024.06.27

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

5394

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

5571

2025.07.04

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

6927

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2848

2023.08.10

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

730

2025.09.05

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

343

2025.11.16

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

0

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn