errno 指南
常见 errno 及处理建议
通用错误
| errno |
名称 |
含义 |
处理建议 |
| 1 |
EPERM |
权限不足 |
检查文件权限/capabilities |
| 2 |
ENOENT |
文件或目录不存在 |
检查路径、先创建 |
| 5 |
EIO |
I/O 错误 |
硬件问题、磁盘故障 |
| 9 |
EBADF |
无效文件描述符 |
fd 已关闭或未打开 |
| 11 |
EAGAIN / EWOULDBLOCK |
资源暂时不可用 |
非阻塞操作,稍后重试 |
| 12 |
ENOMEM |
内存不足 |
检查内存使用、增加 swap |
| 13 |
EACCES |
权限拒绝 |
检查文件/目录权限 |
| 14 |
EFAULT |
地址错误 |
传入了无效指针 |
| 17 |
EEXIST |
文件已存在 |
O_EXCL 创建时冲突 |
| 22 |
EINVAL |
无效参数 |
检查参数值和对齐要求 |
| 28 |
ENOSPC |
磁盘空间不足 |
清理磁盘或扩容 |
文件/目录相关
| errno |
名称 |
含义 |
处理建议 |
| 20 |
ENOTDIR |
不是目录 |
路径中某级不是目录 |
| 21 |
EISDIR |
是目录 |
对目录做了文件操作 |
| 24 |
EMFILE |
进程 fd 数超限 |
ulimit -n 调大 |
| 23 |
ENFILE |
系统 fd 数超限 |
/proc/sys/fs/file-max 调大 |
| 36 |
ENAMETOOLONG |
文件名太长 |
通常限制 255 字节 |
| 39 |
ENOTEMPTY |
目录非空 |
rmdir 前需清空 |
| 40 |
ELOOP |
符号链接循环 |
检查 symlink 链 |
网络相关
| errno |
名称 |
含义 |
处理建议 |
| 98 |
EADDRINUSE |
地址已被使用 |
设置 SO_REUSEADDR |
| 99 |
EADDRNOTAVAIL |
地址不可用 |
检查 IP/端口 |
| 110 |
ETIMEDOUT |
连接超时 |
对端不可达或太慢 |
| 111 |
ECONNREFUSED |
连接被拒绝 |
对端未监听该端口 |
| 104 |
ECONNRESET |
连接被重置 |
对端发送了 RST |
| 32 |
EPIPE |
管道断裂 |
对端已关闭(忽略 SIGPIPE 后会得到这个) |
| 115 |
EINPROGRESS |
操作进行中 |
非阻塞 connect 正常,等待 EPOLLOUT |
| 103 |
ECONNABORTED |
连接中止 |
accept 时连接已被对端关闭 |
| 113 |
EHOSTUNREACH |
主机不可达 |
检查路由和网络 |
| 101 |
ENETUNREACH |
网络不可达 |
检查网络配置 |
信号/进程相关
| errno |
名称 |
含义 |
处理建议 |
| 4 |
EINTR |
被信号中断 |
重试 syscall(或设 SA_RESTART) |
| 3 |
ESRCH |
进程不存在 |
kill 的目标进程已退出 |
| 10 |
ECHILD |
无子进程 |
wait 但没有子进程可等 |
处理模式
EAGAIN / EWOULDBLOCK 处理
// 非阻塞 I/O 中最常见的 errno
ssize_t n = read(fd, buf, size);
if (n == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 正常:没有数据可读,等 epoll 通知
return;
}
// 真正的错误
perror("read");
close(fd);
}
EINTR 处理
// 方法1: 手动重试
ssize_t n;
do {
n = read(fd, buf, size);
} while (n == -1 && errno == EINTR);
// 方法2: SA_RESTART(对 read/write 等有效)
struct sigaction sa = { .sa_handler = handler, .sa_flags = SA_RESTART };
sigaction(SIGUSR1, &sa, NULL);
// 此后 read/write 被信号中断会自动重试
// 注意: epoll_wait/poll/select 即使设了 SA_RESTART 也会返回 EINTR
EINPROGRESS 处理
// 非阻塞 connect 的正常流程
int ret = connect(fd, addr, addrlen);
if (ret == -1 && errno == EINPROGRESS) {
// 连接正在建立中
// 注册 EPOLLOUT 等待连接完成
struct epoll_event ev = { .events = EPOLLOUT, .data.fd = fd };
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
}
// EPOLLOUT 触发后:
int err;
socklen_t len = sizeof(err);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
if (err == 0) { /* 连接成功 */ }
else { /* 连接失败,err 就是错误码 */ }
ECONNRESET 处理
// read 时对端发了 RST
ssize_t n = read(fd, buf, size);
if (n == -1 && errno == ECONNRESET) {
// 对端强制关闭,清理连接即可
close(fd);
return;
}
EMFILE / ENFILE 处理
# 进程级限制
ulimit -n 1048576
# 系统级限制
sysctl fs.file-max=2097152
# 或 /etc/security/limits.conf
* soft nofile 1048576
* hard nofile 1048576
// 程序中处理
int conn = accept4(listen_fd, ...);
if (conn == -1 && errno == EMFILE) {
// fd 耗尽!紧急措施:
// 1. 关闭一些空闲连接
// 2. 拒绝新连接一段时间
// 3. 日志告警
}
调试技巧
# 查看 errno 的含义
python3 -c "import os; print(os.strerror(110))"
# Connection timed out
# strace 输出中的 errno
# read(5, 0x7f..., 4096) = -1 EAGAIN (Resource temporarily unavailable)
# perror 和 strerror
perror("read"); // 输出: read: Resource temporarily unavailable
char *msg = strerror(errno); // 返回错误描述字符串
延伸阅读
man 3 errno — 完整错误码列表
man 2 <syscall> — 每个 syscall 的 ERRORS 章节列出可能返回的 errno
/usr/include/asm-generic/errno-base.h — 1-34
/usr/include/asm-generic/errno.h — 35-133