libev 事件循环:机制、移植与适用边界
libev 是一个单线程的事件循环库(Reactor 模式)。使用时把要等待的事件------文件描述符可读、定时器到期、跨线程通知------注册进去,事件发生后由库调用回调函数处理。在嵌入式系统中,它可以作为一个 RT-Thread 线程运行,STM32F407 上已用 select 后端验证了 ev_io、ev_timer、ev_async 三种用法;它也可以承接中断处理中的耗时部分。是否引入 libev,取决于两点:数据来源能不能映射成文件描述符(fd),以及模块自身是否已经有一套完整的并发处理机制。
1. 定位:单线程事件循环,不处理硬件中断
1.1 libev 是什么
libev 用 C 语言编写,对多种操作系统的 I/O 多路复用机制(select / poll / epoll / kqueue / port 等)提供统一接口。它本身不做业务,只负责一件事:等待用户注册的事件发生,然后调用对应的回调函数。一个循环可以同时等待成百上千个事件,事件之间串行处理,不需要为每件事开一个线程。
最常用的三种事件类型:
| watcher | 等待什么 | 典型场景 |
|---|---|---|
ev_io |
文件描述符可读 / 可写 | 网络消息、串口数据 |
ev_timer |
定时器到期 | 心跳、超时 |
ev_async |
其他线程 / ISR 发来通知 | 跨线程完成通知 |
1.2 libev 与中断的关系
libev 不能接管硬件中断:硬件中断由 CPU / 内核 / RTOS 的中断控制器直接跳转到 ISR,有纳秒级的响应要求,而事件循环有调度延迟,不适合在 ISR 上下文运行。
但它可以和中断配合,做法是 Linux 内核常用的「上半部 / 下半部」分工:ISR 里只做必须立刻完成的事(读 FIFO、把字节写入缓冲区),然后通过 ev_async_send 通知事件循环;解析、分发、写盘等耗时操作全部在事件循环里做。串口环形缓冲示例演示的正是这个流程:模拟 ISR 只把字节写入环形缓冲并调用 ev_async_send,后续处理在事件循环中完成。
与中断相关的三个接口:
- 硬件中断:libev 不直接处理,只承接中断之后的耗时操作。
- POSIX 信号 :
ev_signal可以把信号转成事件循环里的一次回调,避免信号打断带来的竞态;但它只在 Linux 可用,RT-Thread 没有 POSIX 信号,编译时需要关闭(EV_SIGNAL_ENABLE=0)。 - 跨线程 / ISR 通知 :
ev_async_send是 libev 唯一可以从其他线程安全调用的接口,相当于从任意线程或 ISR 给事件循环发一个通知,内部用 pipe 实现,所有平台都可用。
#mermaid-svg-4mvMzUCe4sCpq9Z3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .error-icon{fill:#552222;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .marker.cross{stroke:#333333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4mvMzUCe4sCpq9Z3 p{margin:0;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .cluster-label text{fill:#333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .cluster-label span{color:#333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .cluster-label span p{background-color:transparent;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .label text,#mermaid-svg-4mvMzUCe4sCpq9Z3 span{fill:#333;color:#333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .node rect,#mermaid-svg-4mvMzUCe4sCpq9Z3 .node circle,#mermaid-svg-4mvMzUCe4sCpq9Z3 .node ellipse,#mermaid-svg-4mvMzUCe4sCpq9Z3 .node polygon,#mermaid-svg-4mvMzUCe4sCpq9Z3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .rough-node .label text,#mermaid-svg-4mvMzUCe4sCpq9Z3 .node .label text,#mermaid-svg-4mvMzUCe4sCpq9Z3 .image-shape .label,#mermaid-svg-4mvMzUCe4sCpq9Z3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .rough-node .label,#mermaid-svg-4mvMzUCe4sCpq9Z3 .node .label,#mermaid-svg-4mvMzUCe4sCpq9Z3 .image-shape .label,#mermaid-svg-4mvMzUCe4sCpq9Z3 .icon-shape .label{text-align:center;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .node.clickable{cursor:pointer;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .arrowheadPath{fill:#333333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4mvMzUCe4sCpq9Z3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4mvMzUCe4sCpq9Z3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4mvMzUCe4sCpq9Z3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .cluster text{fill:#333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .cluster span{color:#333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4mvMzUCe4sCpq9Z3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .icon-shape,#mermaid-svg-4mvMzUCe4sCpq9Z3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .icon-shape p,#mermaid-svg-4mvMzUCe4sCpq9Z3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .icon-shape .label rect,#mermaid-svg-4mvMzUCe4sCpq9Z3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4mvMzUCe4sCpq9Z3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4mvMzUCe4sCpq9Z3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .fast>*{fill:#d4edda!important;stroke:#28a745!important;color:#155724!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .fast span{fill:#d4edda!important;stroke:#28a745!important;color:#155724!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .fast tspan{fill:#155724!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .sig>*{fill:#fff3cd!important;stroke:#ffc107!important;color:#856404!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .sig span{fill:#fff3cd!important;stroke:#ffc107!important;color:#856404!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .sig tspan{fill:#856404!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .core>*{fill:#e2d9f3!important;stroke:#6f42c1!important;color:#3b2a6b!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .core span{fill:#e2d9f3!important;stroke:#6f42c1!important;color:#3b2a6b!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .core tspan{fill:#3b2a6b!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .io>*{fill:#cce5ff!important;stroke:#0d6efd!important;color:#084298!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .io span{fill:#cce5ff!important;stroke:#0d6efd!important;color:#084298!important;}#mermaid-svg-4mvMzUCe4sCpq9Z3 .io tspan{fill:#084298!important;} 硬件中断
POSIX 信号
跨线程 / ISR
中断源
ISR 上半部
读 FIFO / 写缓冲区
ev_signal
仅 Linux
ev_async
所有平台
事件循环
ev_io
网络 / 串口
ev_timer
心跳 / 超时
2. 内部设计:三层结构与主循环
2.1 三层架构与单编译单元
libev 自顶向下分三层:API 层、事件循环核心层、后端抽象层。
#mermaid-svg-kStWnNvhIENZCQuS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kStWnNvhIENZCQuS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kStWnNvhIENZCQuS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kStWnNvhIENZCQuS .error-icon{fill:#552222;}#mermaid-svg-kStWnNvhIENZCQuS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kStWnNvhIENZCQuS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kStWnNvhIENZCQuS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kStWnNvhIENZCQuS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kStWnNvhIENZCQuS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kStWnNvhIENZCQuS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kStWnNvhIENZCQuS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kStWnNvhIENZCQuS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kStWnNvhIENZCQuS .marker.cross{stroke:#333333;}#mermaid-svg-kStWnNvhIENZCQuS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kStWnNvhIENZCQuS p{margin:0;}#mermaid-svg-kStWnNvhIENZCQuS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kStWnNvhIENZCQuS .cluster-label text{fill:#333;}#mermaid-svg-kStWnNvhIENZCQuS .cluster-label span{color:#333;}#mermaid-svg-kStWnNvhIENZCQuS .cluster-label span p{background-color:transparent;}#mermaid-svg-kStWnNvhIENZCQuS .label text,#mermaid-svg-kStWnNvhIENZCQuS span{fill:#333;color:#333;}#mermaid-svg-kStWnNvhIENZCQuS .node rect,#mermaid-svg-kStWnNvhIENZCQuS .node circle,#mermaid-svg-kStWnNvhIENZCQuS .node ellipse,#mermaid-svg-kStWnNvhIENZCQuS .node polygon,#mermaid-svg-kStWnNvhIENZCQuS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kStWnNvhIENZCQuS .rough-node .label text,#mermaid-svg-kStWnNvhIENZCQuS .node .label text,#mermaid-svg-kStWnNvhIENZCQuS .image-shape .label,#mermaid-svg-kStWnNvhIENZCQuS .icon-shape .label{text-anchor:middle;}#mermaid-svg-kStWnNvhIENZCQuS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kStWnNvhIENZCQuS .rough-node .label,#mermaid-svg-kStWnNvhIENZCQuS .node .label,#mermaid-svg-kStWnNvhIENZCQuS .image-shape .label,#mermaid-svg-kStWnNvhIENZCQuS .icon-shape .label{text-align:center;}#mermaid-svg-kStWnNvhIENZCQuS .node.clickable{cursor:pointer;}#mermaid-svg-kStWnNvhIENZCQuS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kStWnNvhIENZCQuS .arrowheadPath{fill:#333333;}#mermaid-svg-kStWnNvhIENZCQuS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kStWnNvhIENZCQuS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kStWnNvhIENZCQuS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kStWnNvhIENZCQuS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kStWnNvhIENZCQuS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kStWnNvhIENZCQuS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kStWnNvhIENZCQuS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kStWnNvhIENZCQuS .cluster text{fill:#333;}#mermaid-svg-kStWnNvhIENZCQuS .cluster span{color:#333;}#mermaid-svg-kStWnNvhIENZCQuS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kStWnNvhIENZCQuS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kStWnNvhIENZCQuS rect.text{fill:none;stroke-width:0;}#mermaid-svg-kStWnNvhIENZCQuS .icon-shape,#mermaid-svg-kStWnNvhIENZCQuS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kStWnNvhIENZCQuS .icon-shape p,#mermaid-svg-kStWnNvhIENZCQuS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kStWnNvhIENZCQuS .icon-shape .label rect,#mermaid-svg-kStWnNvhIENZCQuS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kStWnNvhIENZCQuS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kStWnNvhIENZCQuS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kStWnNvhIENZCQuS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 核心层 ev.c
后端层
epoll
kqueue
poll / select
port / linuxaio / iouring
API 层
ev.h
C API
ev++.h
C++ 封装
event.h
libevent 兼容
watcher 管理
优先级调度
定时器最小堆
ev_run 主循环
内核
一个重要的实现选择是单编译单元 :ev.c 在编译期用 #include 把各个后端的 .c 文件内联进来,全部编译进同一个翻译单元。好处是编译器能看到全部代码,便于内联优化;代价是无法单独编译某个后端。编译期根据 HAVE_SYS_EPOLL_H、HAVE_KQUEUE 等宏决定启用哪些后端,运行期可以用 ev_supported_backends() 和 ev_recommended_backends() 查询。
2.2 核心数据结构
四组结构支撑整个循环:
| 结构 | 作用 | 关键字段 |
|---|---|---|
ev_watcher 公共头 |
所有 watcher 类型前面字段一致,可安全互相转换 | active / pending / priority / data / cb |
ANFD 数组 |
按 fd 下标直接索引,避免哈希查找 | head 链表、events 关注位、emask 内核已注册位 |
pendings 队列 |
按优先级存放待执行的事件 | 回调时从高优先级到低优先级取出 |
ANHE 最小堆 |
定时器按到期时间排序 | at 到期时间、w 指向定时器 watcher |
同一个 fd 上注册多个 watcher 时,通过 ANFD.head 链表串联;epoll 模式下比对 emask 与 events 的差异,决定是否需要调用 epoll_ctl 修改注册。
2.3 ev_run 主循环
#mermaid-svg-SfYl0lQcsp0gPWp5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SfYl0lQcsp0gPWp5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SfYl0lQcsp0gPWp5 .error-icon{fill:#552222;}#mermaid-svg-SfYl0lQcsp0gPWp5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SfYl0lQcsp0gPWp5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SfYl0lQcsp0gPWp5 .marker.cross{stroke:#333333;}#mermaid-svg-SfYl0lQcsp0gPWp5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SfYl0lQcsp0gPWp5 p{margin:0;}#mermaid-svg-SfYl0lQcsp0gPWp5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SfYl0lQcsp0gPWp5 .cluster-label text{fill:#333;}#mermaid-svg-SfYl0lQcsp0gPWp5 .cluster-label span{color:#333;}#mermaid-svg-SfYl0lQcsp0gPWp5 .cluster-label span p{background-color:transparent;}#mermaid-svg-SfYl0lQcsp0gPWp5 .label text,#mermaid-svg-SfYl0lQcsp0gPWp5 span{fill:#333;color:#333;}#mermaid-svg-SfYl0lQcsp0gPWp5 .node rect,#mermaid-svg-SfYl0lQcsp0gPWp5 .node circle,#mermaid-svg-SfYl0lQcsp0gPWp5 .node ellipse,#mermaid-svg-SfYl0lQcsp0gPWp5 .node polygon,#mermaid-svg-SfYl0lQcsp0gPWp5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SfYl0lQcsp0gPWp5 .rough-node .label text,#mermaid-svg-SfYl0lQcsp0gPWp5 .node .label text,#mermaid-svg-SfYl0lQcsp0gPWp5 .image-shape .label,#mermaid-svg-SfYl0lQcsp0gPWp5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-SfYl0lQcsp0gPWp5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SfYl0lQcsp0gPWp5 .rough-node .label,#mermaid-svg-SfYl0lQcsp0gPWp5 .node .label,#mermaid-svg-SfYl0lQcsp0gPWp5 .image-shape .label,#mermaid-svg-SfYl0lQcsp0gPWp5 .icon-shape .label{text-align:center;}#mermaid-svg-SfYl0lQcsp0gPWp5 .node.clickable{cursor:pointer;}#mermaid-svg-SfYl0lQcsp0gPWp5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SfYl0lQcsp0gPWp5 .arrowheadPath{fill:#333333;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SfYl0lQcsp0gPWp5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SfYl0lQcsp0gPWp5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SfYl0lQcsp0gPWp5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SfYl0lQcsp0gPWp5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SfYl0lQcsp0gPWp5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SfYl0lQcsp0gPWp5 .cluster text{fill:#333;}#mermaid-svg-SfYl0lQcsp0gPWp5 .cluster span{color:#333;}#mermaid-svg-SfYl0lQcsp0gPWp5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SfYl0lQcsp0gPWp5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SfYl0lQcsp0gPWp5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-SfYl0lQcsp0gPWp5 .icon-shape,#mermaid-svg-SfYl0lQcsp0gPWp5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SfYl0lQcsp0gPWp5 .icon-shape p,#mermaid-svg-SfYl0lQcsp0gPWp5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SfYl0lQcsp0gPWp5 .icon-shape .label rect,#mermaid-svg-SfYl0lQcsp0gPWp5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SfYl0lQcsp0gPWp5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SfYl0lQcsp0gPWp5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SfYl0lQcsp0gPWp5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-SfYl0lQcsp0gPWp5 .entry>*{fill:#D6E4F0!important;stroke:#4A90D9!important;color:#1B3A57!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .entry span{fill:#D6E4F0!important;stroke:#4A90D9!important;color:#1B3A57!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .entry tspan{fill:#1B3A57!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .step>*{fill:#D8EEDC!important;stroke:#6BAA75!important;color:#254A2E!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .step span{fill:#D8EEDC!important;stroke:#6BAA75!important;color:#254A2E!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .step tspan{fill:#254A2E!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .poll>*{fill:#FBE3E9!important;stroke:#D9738A!important;color:#5E2333!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .poll span{fill:#FBE3E9!important;stroke:#D9738A!important;color:#5E2333!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .poll tspan{fill:#5E2333!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .dec>*{fill:#FFF3CD!important;stroke:#E8A33D!important;color:#5C3D00!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .dec span{fill:#FFF3CD!important;stroke:#E8A33D!important;color:#5C3D00!important;}#mermaid-svg-SfYl0lQcsp0gPWp5 .dec tspan{fill:#5C3D00!important;} 否
是
ev_run 入口
fd_reify
同步 fd 变更到内核
time_update
刷新时间
prepare 回调
计算阻塞超时
backend_poll
epoll_wait / select
执行就绪回调
按优先级
check 回调
是否退出
返回
三个实现要点:
- 两种时钟 :内部同时维护单调时钟
mn_now和实时时钟ev_rt_now。ev_timer基于单调时钟,不受系统改时间影响;ev_periodic基于实时时钟,行为类似 crontab,会跟随时间调整。 - 优先级调度 :每个 watcher 的
priority范围 -2, +2,数值越大越先执行。 - 超时计算:阻塞等待的超时取定时器堆顶的时间,避免无谓的唤醒。
2.4 关键不变量
阅读源码和写测试时可以依据四条不变量:
w->active == 1当且仅当 watcher 已挂入循环(链表、堆或队列之一)。w->pending > 0表示 watcher 已在待执行队列中,此期间不会重复入队。anfds[fd].emask与内核注册状态一致(每次 fd_reify 执行后成立)。- 定时器堆满足最小堆性质,堆顶是全局最近的到期时间。
3. RT-Thread 移植:select 后端,每线程一个 loop
libev 4.33 已移植到 RT-Thread 5.2.2,在 Renode 全系统仿真中验证通过,后端只启用 select(EV_USE_SELECT=1)。ev_io、ev_timer、ev_async 三种用法均跑通,与 Linux 的 epoll 后端在事件语义上等价。
ev_run 是阻塞循环,在 RT-Thread 抢占式内核中必须以独立线程承载,与其他线程并存。
#mermaid-svg-x0HsM2YCg1HdrBxj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-x0HsM2YCg1HdrBxj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-x0HsM2YCg1HdrBxj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-x0HsM2YCg1HdrBxj .error-icon{fill:#552222;}#mermaid-svg-x0HsM2YCg1HdrBxj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-x0HsM2YCg1HdrBxj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-x0HsM2YCg1HdrBxj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-x0HsM2YCg1HdrBxj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-x0HsM2YCg1HdrBxj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-x0HsM2YCg1HdrBxj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-x0HsM2YCg1HdrBxj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-x0HsM2YCg1HdrBxj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-x0HsM2YCg1HdrBxj .marker.cross{stroke:#333333;}#mermaid-svg-x0HsM2YCg1HdrBxj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-x0HsM2YCg1HdrBxj p{margin:0;}#mermaid-svg-x0HsM2YCg1HdrBxj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-x0HsM2YCg1HdrBxj .cluster-label text{fill:#333;}#mermaid-svg-x0HsM2YCg1HdrBxj .cluster-label span{color:#333;}#mermaid-svg-x0HsM2YCg1HdrBxj .cluster-label span p{background-color:transparent;}#mermaid-svg-x0HsM2YCg1HdrBxj .label text,#mermaid-svg-x0HsM2YCg1HdrBxj span{fill:#333;color:#333;}#mermaid-svg-x0HsM2YCg1HdrBxj .node rect,#mermaid-svg-x0HsM2YCg1HdrBxj .node circle,#mermaid-svg-x0HsM2YCg1HdrBxj .node ellipse,#mermaid-svg-x0HsM2YCg1HdrBxj .node polygon,#mermaid-svg-x0HsM2YCg1HdrBxj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-x0HsM2YCg1HdrBxj .rough-node .label text,#mermaid-svg-x0HsM2YCg1HdrBxj .node .label text,#mermaid-svg-x0HsM2YCg1HdrBxj .image-shape .label,#mermaid-svg-x0HsM2YCg1HdrBxj .icon-shape .label{text-anchor:middle;}#mermaid-svg-x0HsM2YCg1HdrBxj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-x0HsM2YCg1HdrBxj .rough-node .label,#mermaid-svg-x0HsM2YCg1HdrBxj .node .label,#mermaid-svg-x0HsM2YCg1HdrBxj .image-shape .label,#mermaid-svg-x0HsM2YCg1HdrBxj .icon-shape .label{text-align:center;}#mermaid-svg-x0HsM2YCg1HdrBxj .node.clickable{cursor:pointer;}#mermaid-svg-x0HsM2YCg1HdrBxj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-x0HsM2YCg1HdrBxj .arrowheadPath{fill:#333333;}#mermaid-svg-x0HsM2YCg1HdrBxj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-x0HsM2YCg1HdrBxj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-x0HsM2YCg1HdrBxj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x0HsM2YCg1HdrBxj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-x0HsM2YCg1HdrBxj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x0HsM2YCg1HdrBxj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-x0HsM2YCg1HdrBxj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-x0HsM2YCg1HdrBxj .cluster text{fill:#333;}#mermaid-svg-x0HsM2YCg1HdrBxj .cluster span{color:#333;}#mermaid-svg-x0HsM2YCg1HdrBxj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-x0HsM2YCg1HdrBxj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-x0HsM2YCg1HdrBxj rect.text{fill:none;stroke-width:0;}#mermaid-svg-x0HsM2YCg1HdrBxj .icon-shape,#mermaid-svg-x0HsM2YCg1HdrBxj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x0HsM2YCg1HdrBxj .icon-shape p,#mermaid-svg-x0HsM2YCg1HdrBxj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-x0HsM2YCg1HdrBxj .icon-shape .label rect,#mermaid-svg-x0HsM2YCg1HdrBxj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x0HsM2YCg1HdrBxj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-x0HsM2YCg1HdrBxj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-x0HsM2YCg1HdrBxj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-x0HsM2YCg1HdrBxj .thread>*{fill:#D6E4F0!important;stroke:#4A90D9!important;color:#1B3A57!important;}#mermaid-svg-x0HsM2YCg1HdrBxj .thread span{fill:#D6E4F0!important;stroke:#4A90D9!important;color:#1B3A57!important;}#mermaid-svg-x0HsM2YCg1HdrBxj .thread tspan{fill:#1B3A57!important;}#mermaid-svg-x0HsM2YCg1HdrBxj .backend>*{fill:#D8EEDC!important;stroke:#6BAA75!important;color:#254A2E!important;}#mermaid-svg-x0HsM2YCg1HdrBxj .backend span{fill:#D8EEDC!important;stroke:#6BAA75!important;color:#254A2E!important;}#mermaid-svg-x0HsM2YCg1HdrBxj .backend tspan{fill:#254A2E!important;}#mermaid-svg-x0HsM2YCg1HdrBxj .fd>*{fill:#FBE3E9!important;stroke:#D9738A!important;color:#5E2333!important;}#mermaid-svg-x0HsM2YCg1HdrBxj .fd span{fill:#FBE3E9!important;stroke:#D9738A!important;color:#5E2333!important;}#mermaid-svg-x0HsM2YCg1HdrBxj .fd tspan{fill:#5E2333!important;} libev select 后端
RT-Thread 线程
main 线程
libev_test 线程
uart_hsm 线程
fs_hsm 线程
-
worker
lwip_echo 线程 -
socket
select
RT-Thread POSIX poll
pipe /dev/uart1
RAMFS fd
SAL socket fd
两个要点:
- 每个线程必须用
ev_loop_new创建独立的 loop。ev_default_loop是全局单例,多个线程共享会触发「ev_loop recursion during release detected」断言。 - select 后端最终落到 RT-Thread 的 POSIX poll,把 pipe、RAMFS fd、SAL socket fd 统一成同一套 fd 语义,这是移植能跑通的关键。
4. 适用性判断:三个问题决定该不该用
对已经有「中断 + 线程 + IPC」机制的嵌入式模块,引入 libev 在多数情况下得不偿失------本质是在现有并发模型上再叠一层。每个模块只需回答三个问题:
- 数据来源能不能映射成 fd? libev 的
ev_io只认有「可读 / 可写」状态的 POSIX 文件描述符。 - 模块是否已经有完整的并发 / 同步机制? 如果已经有「线程 + 锁 + 事件」,再引入就是重复。
- 引入是替换还是叠加? 替换有价值,叠加是负担。
4.1 数据来源决定适配成本
| 类别 | 形态 | 能否被 ev_io 驱动 |
|---|---|---|
| A 类 | POSIX fd(socket、pipe、DFS 下的 /dev/xxx) | 直接可用 |
| B 类 | RT-Thread 设备 rt_device_t + 中断回调 |
需要 DFS fd 桥接,成本高 |
| C 类 | 直接操作外设寄存器做同步握手(如 SPI slave 的 CS 中断 + master 轮询) | 没有可读 / 可写状态,无法映射 |
凡是数据来源是「设备 + 中断」而不是 fd 的模块,引入 libev 都要先承担桥接的移植和验证成本。
4.2 引入的代价与决策流程
如果在边界处强行引入 ev_async + worker 线程,代价包括:额外的线程栈和控制块、ev_async 本身(一个 watcher + 一个 pipe)、两套同步机制、数据在调用线程和 worker 之间拷贝或加锁、以及「提交 → 调度 → 执行 → 通知 → 回调」带来的延迟。
什么时候值得:上层本来就跑在事件循环里,且需要非阻塞地触发耗时操作------这时加 ev_async 是顺理成章的。什么时候不值得:上层是同步调用,或者目标操作已经自带 worker 线程------再叠一层只是为异步而异步。
#mermaid-svg-wkvOs6LEQeBxnAbg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wkvOs6LEQeBxnAbg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wkvOs6LEQeBxnAbg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wkvOs6LEQeBxnAbg .error-icon{fill:#552222;}#mermaid-svg-wkvOs6LEQeBxnAbg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wkvOs6LEQeBxnAbg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wkvOs6LEQeBxnAbg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wkvOs6LEQeBxnAbg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wkvOs6LEQeBxnAbg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wkvOs6LEQeBxnAbg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wkvOs6LEQeBxnAbg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wkvOs6LEQeBxnAbg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wkvOs6LEQeBxnAbg .marker.cross{stroke:#333333;}#mermaid-svg-wkvOs6LEQeBxnAbg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wkvOs6LEQeBxnAbg p{margin:0;}#mermaid-svg-wkvOs6LEQeBxnAbg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wkvOs6LEQeBxnAbg .cluster-label text{fill:#333;}#mermaid-svg-wkvOs6LEQeBxnAbg .cluster-label span{color:#333;}#mermaid-svg-wkvOs6LEQeBxnAbg .cluster-label span p{background-color:transparent;}#mermaid-svg-wkvOs6LEQeBxnAbg .label text,#mermaid-svg-wkvOs6LEQeBxnAbg span{fill:#333;color:#333;}#mermaid-svg-wkvOs6LEQeBxnAbg .node rect,#mermaid-svg-wkvOs6LEQeBxnAbg .node circle,#mermaid-svg-wkvOs6LEQeBxnAbg .node ellipse,#mermaid-svg-wkvOs6LEQeBxnAbg .node polygon,#mermaid-svg-wkvOs6LEQeBxnAbg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wkvOs6LEQeBxnAbg .rough-node .label text,#mermaid-svg-wkvOs6LEQeBxnAbg .node .label text,#mermaid-svg-wkvOs6LEQeBxnAbg .image-shape .label,#mermaid-svg-wkvOs6LEQeBxnAbg .icon-shape .label{text-anchor:middle;}#mermaid-svg-wkvOs6LEQeBxnAbg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wkvOs6LEQeBxnAbg .rough-node .label,#mermaid-svg-wkvOs6LEQeBxnAbg .node .label,#mermaid-svg-wkvOs6LEQeBxnAbg .image-shape .label,#mermaid-svg-wkvOs6LEQeBxnAbg .icon-shape .label{text-align:center;}#mermaid-svg-wkvOs6LEQeBxnAbg .node.clickable{cursor:pointer;}#mermaid-svg-wkvOs6LEQeBxnAbg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wkvOs6LEQeBxnAbg .arrowheadPath{fill:#333333;}#mermaid-svg-wkvOs6LEQeBxnAbg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wkvOs6LEQeBxnAbg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wkvOs6LEQeBxnAbg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wkvOs6LEQeBxnAbg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wkvOs6LEQeBxnAbg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wkvOs6LEQeBxnAbg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wkvOs6LEQeBxnAbg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wkvOs6LEQeBxnAbg .cluster text{fill:#333;}#mermaid-svg-wkvOs6LEQeBxnAbg .cluster span{color:#333;}#mermaid-svg-wkvOs6LEQeBxnAbg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wkvOs6LEQeBxnAbg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wkvOs6LEQeBxnAbg rect.text{fill:none;stroke-width:0;}#mermaid-svg-wkvOs6LEQeBxnAbg .icon-shape,#mermaid-svg-wkvOs6LEQeBxnAbg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wkvOs6LEQeBxnAbg .icon-shape p,#mermaid-svg-wkvOs6LEQeBxnAbg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wkvOs6LEQeBxnAbg .icon-shape .label rect,#mermaid-svg-wkvOs6LEQeBxnAbg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wkvOs6LEQeBxnAbg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wkvOs6LEQeBxnAbg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wkvOs6LEQeBxnAbg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-wkvOs6LEQeBxnAbg .ok>*{fill:#d4edda!important;stroke:#28a745!important;color:#155724!important;}#mermaid-svg-wkvOs6LEQeBxnAbg .ok span{fill:#d4edda!important;stroke:#28a745!important;color:#155724!important;}#mermaid-svg-wkvOs6LEQeBxnAbg .ok tspan{fill:#155724!important;}#mermaid-svg-wkvOs6LEQeBxnAbg .warn>*{fill:#fff3cd!important;stroke:#ffc107!important;color:#856404!important;}#mermaid-svg-wkvOs6LEQeBxnAbg .warn span{fill:#fff3cd!important;stroke:#ffc107!important;color:#856404!important;}#mermaid-svg-wkvOs6LEQeBxnAbg .warn tspan{fill:#856404!important;}#mermaid-svg-wkvOs6LEQeBxnAbg .bad>*{fill:#f8d7da!important;stroke:#dc3545!important;color:#721c24!important;}#mermaid-svg-wkvOs6LEQeBxnAbg .bad span{fill:#f8d7da!important;stroke:#dc3545!important;color:#721c24!important;}#mermaid-svg-wkvOs6LEQeBxnAbg .bad tspan{fill:#721c24!important;} 否
是
否
是
已有完整机制
无并发机制
上层是否已经
跑在事件循环里
不引入
同步调用最简单
数据来源能否
映射成 fd
不引入
设备驱动层无法映射
模块自身
是否已异步化
只做边界衔接
ev_async 连接完成通知
可引入
ev_io + ev_timer + ev_async
5. 建议
- 不为了引入而引入 。模块已经完整时,libev 不进入模块内部,只在「上层已经是事件驱动」的边界用
ev_async连接完成通知。 - 动手前先判断数据来源。先问「有没有 fd」,设备 + 中断驱动的模块(尤其涉及外设寄存器的)直接排除。
- host 仿真是 libev 最合适的场景 。用 pipe 或文件提供数据、在没有硬件环境时跑解析逻辑,争议最少;RT-Thread 移植则用 select 后端 + 每个线程一个 loop,功能按
EV_*_ENABLE裁剪。
一句话:libev 把「等待事件发生」和「事件发生后做什么」分开,用一个单线程循环串行处理;它承接的是中断之后的耗时操作。是否引入不取决于 libev 本身,而取决于数据来源的形态和模块现有的并发机制是否给它留了位置。