1. 先搞清楚:Resource temporarily unavailable 到底是什么
1.1 先用一个小程序看清 errno 11 的真面目
第一次在非阻塞 socket 上看到 Resource temporarily unavailable 时,我的第一反应是查内存、查文件描述符数,还以为是系统资源被耗尽了。后来才发现,这句话在 Linux 上的含义其实非常朴素:它对应 errno 编号 11,宏定义是 EAGAIN,而且在 Linux 上 EWOULDBLOCK 和它根本是同一个值,也都是 11。strerror() 翻译出来,就是这句看起来特别吓人的 Resource temporarily unavailable。
它的真实含义是:当前这个非阻塞操作如果继续等下去会阻塞,所以内核直接告诉你“现在没数据,你回头再试”。注意,这不是一次失败,而是一个预期中的返回结果。你可以用下面这段小程序快速验证:
c复制#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void) {
errno = EAGAIN;
printf("EAGAIN = %d\n", EAGAIN);
printf("EWOULDBLOCK = %d\n", EWOULDBLOCK);
printf("strerror : %s\n", strerror(EAGAIN));
return 0;
}
运行结果如下:
code复制EAGAIN = 11
EWOULDBLOCK = 11
strerror : Resource temporarily unavailable
所以以后再见到日志里刷 Resource temporarily unavailable,先别急着告警。先确认一下是不是某个 read()、accept()、write() 在非阻塞 fd 上返回了 -1,并且 errno 正好是 11。在绝大多数多路复用程序里,这往往意味着逻辑写对了,而不是系统出问题了。
很多人第一次接触非阻塞编程时会犯一个错误:看到 read 返回负值就认定是出错,然后打 error 日志、关闭连接。但在非阻塞网络编程里,EAGAIN 恰恰是“数据已经全部读完”的信号。如果你把它当成错误处理,结果只有两种:要么日志被刷爆,要么把本不该断开的连接给强关掉,线上事故就是这么来的。
1.2 非阻塞 read 在什么情况下会返回 EAGAIN
要理解 EAGAIN,先得知道一条 TCP 数据从网线到用户态程序要经过什么。大致路径是:网卡收到报文,内核协议栈把数据放进这个 socket 对应的接收缓冲区,然后用户态的 read() / recv() 把数据从内核缓冲区拷贝到用户态缓冲区。对于阻塞 socket,缓冲区空时 read() 会一直挂起,等到有数据了再返回;对于非阻塞 socket,缓冲区空时 read() 立刻返回 -1,并把 errno 置成 EAGAIN。
这也是“非阻塞”三个
