跳转至

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