跳到主要内容

Linux 错误处理

在 Linux C 编程中,很多系统调用和标准库函数都会通过返回值来表示是否出错。但是,返回值只能告诉你“出错了”,却无法告诉你为什么出错,这时候你就需要使用全局变量错误号 errnoperror() 函数来获取和打印错误信息,帮助快速定位错误原因。

本文将带你了解:

  • errno 是什么,怎么使用?
  • perrorstrerror 的区别与用法;
  • 常见的错误码及其含义;
  • 使用 errno 的注意事项;
  • 一个完整的错误处理示例。

什么是 errno?

errno 是一个全局变量,用于存储最近一次错误的错误代码(错误号)。当你调用某个函数失败时,比如 open()read()malloc() 等,它们通常会设置 errno 为一个具体的错误编号。

这个编号是一个整数,例如:

  • 2 表示 ENOENT:文件不存在;
  • 13 表示 EACCES:权限不足;
  • 12 表示 ENOMEM:内存不足。

你可以在头文件中找到这些定义:

#include <errno.h>

⚠️ 注意:errno 不是一个普通变量,而是一个“线程局部”的宏,底层通过 TLS(线程局部存储)实现。它对多线程是安全的。

使用 perror 打印错误信息

你可以使用 perror() 来根据当前的 errno 值打印一条标准错误信息,非常方便调试:

#include <stdio.h>

perror("open"); // 输出类似 "open: No such file or directory"

这会输出你传入的字符串加上对应的错误信息,自动根据当前 errno 转换为人类可读的字符串。

使用 strerror 获取错误字符串

如果你想自己控制错误信息的格式,可以用 strerror()

#include <string.h>

printf("错误信息:%s\n", strerror(errno));

你也可以用它自定义日志输出:

fprintf(stderr, "打开文件失败(错误号 %d):%s\n", errno, strerror(errno));

示例:打开文件失败时的错误处理

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
int fd = open("not_exist.txt", O_RDONLY);
if (fd == -1) {
// 方法一:用 perror 打印
perror("打开文件失败");

// 方法二:用 strerror 打印
fprintf(stderr, "错误号 %d:%s\n", errno, strerror(errno));
return 1;
}

close(fd);
return 0;
}

运行结果(假设文件不存在)可能是:

打开文件失败: No such file or directory
错误号 2:No such file or directory

使用 errno 的注意事项

  • ⚠️ 不要手动清零 errno,也不要依赖它的初始值。
  • errno 只有在出错时才有意义,函数成功不会重置它;
  • 多线程程序中,errno 是线程安全的;
  • 某些库函数不会设置 errno,因此具体使用要看文档;
  • 如果你要在调用失败后马上用 errno中间不要调用其他函数,以免被覆盖。

errno 错误码列表

完整的 Linux 错误代码及其含义如下表所示。常见的错误有 ENOENTEACCESENOMEMEAGAINEBADFEEXISTEINVALECONNREFUSED 等等,你也可以通过 man 3 errno 查看详细文档。

宏名errno描述含义
EPERM1Operation not permitted操作不允许
ENOENT2No such file or directory文件或目录不存在
ESRCH3No such process没有这样的过程
EINTR4Interrupted system call系统调用被中断
EIO5I/O errorI/O 错误
ENXIO6No such device or address设备或地址不存在
E2BIG7Arg list too long参数列表太长
ENOEXEC8Exec format error执行格式错误
EBADF9Bad file number坏的文件描述符
ECHILD10No child processes没有子进程
EAGAIN11Try again资源暂时不可用(非阻塞 I/O)
ENOMEM12Out of memory内存溢出(内存不足)
EACCES13Permission denied拒绝许可(权限不够)
EFAULT14Bad address错误的地址
ENOTBLK15Block device required块设备请求
EBUSY16Device or resource busy设备或资源忙
EEXIST17File exists文件已存在
EXDEV18Cross-device link无效的交叉链接
ENODEV19No such device设备不存在
ENOTDIR20Not a directory不是一个目录
EISDIR21Is a directory是一个目录
EINVAL22Invalid argument无效的参数
ENFILE23File table overflow打开太多的文件系统
EMFILE24Too many open files打开的文件过多
ENOTTY25Not a tty device不是 tty 设备
ETXTBSY26Text file busy文本文件忙
EFBIG27File too large文件太大
ENOSPC28No space left on device设备上没有空间
ESPIPE29Illegal seek非法移位
EROFS30Read-only file system只读文件系统
EMLINK31Too many links太多的链接
EPIPE32Broken pipe管道破裂
EDOM33Math argument out of domain数值结果超出范围
ERANGE34Math result not representable数值结果不具代表性
EDEADLK35Resource deadlock would occur资源死锁错误
ENAMETOOLONG36Filename too long文件名太长
ENOLCK37No record locks available没有可用锁
ENOSYS38Function not implemented功能没有实现
ENOTEMPTY39Directory not empty目录不空
ELOOP40Too many symbolic links encountered符号链接层次太多
EWOULDBLOCK41Same as EAGAIN和 EAGAIN 一样
ENOMSG42No message of desired type没有期望类型的消息
EIDRM43Identifier removed标识符删除
ECHRNG44Channel number out of range频道数目超出范围
EL2NSYNC45Level 2 not synchronized2级不同步
EL3HLT46Level 3 halted3级中断
EL3RST47Level 3 reset3级复位
ELNRNG48Link number out of range链接数超出范围
EUNATCH49Protocol driver not attached协议驱动程序没有连接
ENOCSI50No CSI structure available没有可用CSI结构
EL2HLT51Level 2 halted2级中断
EBADE52Invalid exchange无效的交换
EBADR53Invalid request descriptor请求描述符无效
EXFULL54Exchange full交换全
ENOANO55No anode没有阳极
EBADRQC56Invalid request code无效的请求代码
EBADSLT57Invalid slot无效的槽
EDEADLOCK58Same as EDEADLK和 EDEADLK 一样
EBFONT59Bad font file format错误的字体文件格式
ENOSTR60Device not a stream设备不是字符流
ENODATA61No data available无可用数据
ETIME62Timer expired计时器过期
ENOSR63Out of streams resources流资源溢出
ENONET64Machine is not on the network机器不上网
ENOPKG65Package not installed没有安装软件包
EREMOTE66Object is remote对象是远程的
ENOLINK67Link has been severed联系被切断
EADV68Advertise error广告的错误
ESRMNT69Srmount errorsrmount 错误
ECOMM70Communication error on send发送时的通讯错误
EPROTO71Protocol error协议错误
EMULTIHOP72Multihop attempted多跳尝试
EDOTDOT73RFS specific errorRFS 特定的错误
EBADMSG74Not a data message非数据消息
EOVERFLOW75Value too large for defined data type值太大,对于定义数据类型
ENOTUNIQ76Name not unique on network名不是唯一的网络
EBADFD77File descriptor in bad state文件描述符在坏状态
EREMCHG78Remote address changed远程地址改变了
ELIBACC79Cannot access a needed shared library无法访问必要的共享库
ELIBBAD80Accessing a corrupted shared library访问损坏的共享库
ELIBSCN81A .lib section in an .out is corrupted.out 中的 .lib 段发生损坏
ELIBMAX82Linking in too many shared libraries试图链接太多的共享库
ELIBEXEC83Cannot exec a shared library directly不能直接执行一个共享库
EILSEQ84Illegal byte sequence无效的或不完整的多字节或宽字符
ERESTART85Interrupted system call should be restarted应该重新启动中断的系统调用
ESTRPIPE86Streams pipe error流管错误
EUSERS87Too many users用户太多
ENOTSOCK88Socket operation on non-socket套接字操作在非套接字上
EDESTADDRREQ89Destination address required需要目标地址
EMSGSIZE90Message too long消息太长
EPROTOTYPE91Protocol wrong type for socketsocket 协议类型错误
ENOPROTOOPT92Protocol not available协议不可用
EPROTONOSUPPORT93Protocol not supported不支持的协议
ESOCKTNOSUPPORT94Socket type not supported套接字类型不受支持
EOPNOTSUPP95Operation not supported on transport不支持的操作
EPFNOSUPPORT96Protocol family not supported不支持的协议族
EAFNOSUPPORT97Address family not supported by protocol协议不支持的地址
EADDRINUSE98Address already in use地址已在使用
EADDRNOTAVAIL99Cannot assign requested address无法分配请求的地址
ENETDOWN100Network is down网络瘫痪
ENETUNREACH101Network is unreachable网络不可达
ENETRESET102Network dropped网络连接丢失
ECONNABORTED103Software caused connection软件导致连接中断
ECONNRESET104Connection reset by连接被重置
ENOBUFS105No buffer space available没有可用的缓冲空间
EISCONN106Transport endpoint传输端点已经连接
ENOTCONN107Transport endpoint传输终点没有连接
ESHUTDOWN108Cannot send after transport传输后无法发送
ETOOMANYREFS109Too many references太多的参考
ETIMEDOUT110Connection timed连接超时
ECONNREFUSED111Connection refused连接被拒绝
EHOSTDOWN112Host is down主机已关闭
EHOSTUNREACH113No route to host没有主机的路由
EALREADY114Operation already已运行
EINPROGRESS115Operation now in正在运行
ESTALE116Stale NFS file handle陈旧的 NFS 文件句柄
EUCLEAN117Structure needs cleaning结构需要清洗
ENOTNAM118Not a XENIX-named不是 XENIX 命名的
ENAVAIL119No XENIX semaphores没有 XENIX 信号量
EISNAM120Is a named type file是一个命名的文件类型
EREMOTEIO121Remote I/O error远程输入/输出错误
EDQUOT122Quota exceeded超出磁盘配额
ENOMEDIUM123No medium found没有磁盘被发现
EMEDIUMTYPE124Wrong medium type错误的媒体类型
ECANCELED125Operation Canceled取消操作
ENOKEY126Required key not available所需键不可用
EKEYEXPIRED127Key has expired关键已过期
EKEYREVOKED128Key has been revoked关键被撤销
EKEYREJECTED129Key was rejected by service关键被拒绝服务
EOWNERDEAD130Owner died所有者死亡
ENOTRECOVERABLE131State not recoverable状态不可恢复
ERFKILL132Operation not possible due to RF-kill由于 RF-kill 而无法操作

小结

通过这篇教程,你了解了:

  • errno 是 Linux 下错误处理的核心变量;
  • perror()strerror() 都可以把错误号转换为人类可读的信息;
  • 如何正确地使用 errno 来进行健壮的错误处理。

掌握好错误处理不仅能让你的程序更健壮,还能帮助你快速定位 bug。如果你正在开发底层系统程序、网络应用或者驱动接口,errno 的使用是你必须掌握的基本功。