swoole底层在linux使用epoll、macos/freebsd使用kqueue,非select/poll或用户态轮询;其仅用多路复用监听就绪事件,读写仍为同步操作,依赖协程调度实现非阻塞。

直接说结论:Swoole 底层用的是 epoll(Linux)或 kqueue(macOS/BSD),不是 select,更不是用户态轮询;它只在“等待连接/数据就绪”阶段用多路复用,后续读写仍是同步操作——这点面试被问倒的人最多。
为什么 Swoole 不用 select/poll?
select 和 poll 有硬伤:每次调用都要把整个 fd 集合从用户态拷贝到内核态,还要遍历全部 fd 判断就绪状态。当并发连接数上万时,性能断崖式下跌。Swoole 默认跳过它们,除非你显式降级(比如在旧内核或容器里强制 SWOOLE_SOCK_SYNC 模式)。
实际判断方式很简单:
- 启动 Swoole Server 后执行
strace -p $(pgrep php) -e trace=epoll_wait,poll,select,看到高频epoll_wait就对了 - 检查
/proc/$(pgrep php)/stack,若出现sys_epoll_wait调用栈,说明走的是 epoll -
select在现代 Swoole 中仅用于兜底兼容,且默认关闭;启用需手动编译时加--enable-select
epoll 是同步 IO,不是异步 IO
这是最常被混淆的点。Swoole 的协程非阻塞,不等于底层用了 AIO。它的 epoll 只负责监听哪些 fd “准备好了”,一旦就绪,Swoole 仍用 read()/write() 同步收发数据——只是这些系统调用不会卡住整个线程,因为协程调度器会在阻塞前主动挂起当前协程,切走执行其他任务。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
所以你看到的“非阻塞”,是 epoll + 协程调度共同实现的,不是 epoll 自己能读写数据。
- 错误认知:“epoll = 异步读写” → 实际:epoll = 异步通知,读写仍是同步
- 真正异步 IO(如 Linux
io_uring)Swoole 5.1+ 才开始实验性支持,且需额外开启SWOOLE_HOOK_IOURING - 协程中调用未 hook 的函数(如原生
fread)仍会阻塞整个 Worker,和 epoll 无关
协程里怎么触发 epoll 等待?
不是所有 I/O 操作都会进 epoll。只有被 Swoole Runtime Hook 过、且底层封装为 socket/fd 的操作才会注册进事件循环。典型路径是:Co\Http\Client->get() → 内部创建 socket → connect() 被 hook → 发现未立即连通 → 注册可写事件到 epoll → 挂起协程 → 事件就绪后唤醒。
- 常见能触发 epoll 的操作:
Co\Socket::connect()、Co\Http\Client->recv()、Co\Redis->get()(使用协程版客户端) - 常见不触发 epoll 的操作:
file_get_contents()(除非启用了SWOOLE_HOOK_FILE)、sleep()(必须用Co::sleep())、PDO::__construct()(MySQL 连接本身不进 epoll,但查询会) - 验证是否走 epoll:在协程中执行
Co::sleep(0.1)和usleep(100000),前者不阻塞其他协程,后者会——说明后者绕过了协程调度,也没进 epoll
epoll_wait 超时值怎么影响性能?
Swoole 把 epoll_wait 的 timeout 设为动态值:空闲时设长(如 10ms),有任务积压时自动缩短(甚至 0),避免延迟。这个值不对外暴露,但你能感知到它的存在——比如高并发下 onReceive 回调延迟突增,往往是因为事件循环被大量短连接打满,epoll_wait 来不及响应新事件。
- 不要试图通过
ini_set('swoole.timer.tick', ...)改它,那是定时器精度,和 epoll 无关 - 真正影响 epoll 效率的是 fd 数量和就绪频率,不是 timeout 本身;盲目调小 timeout 反而增加系统调用次数
- 生产环境若发现
epoll_wait耗时异常(用perf record -e syscalls:sys_enter_epoll_wait抓),优先查是否有 fd 泄漏(lsof -p $(pgrep php)看句柄数是否持续上涨)
真正难的不是记住 epoll 名字,而是理解它在哪一层、什么时候介入、又在什么时候退出——Swoole 的协程调度器和 epoll 之间那几百行 C 代码的衔接点,才是线上出问题时最该翻的地方。










