Linux 系统错误码 errno 剖析
一、errno 介绍
1.1 errno 简介
Linux 中系统调用的错误都存储于错误码 errno 中。errno 由操作系统维护,存储就近发生的错误,即下一次的错误码会覆盖掉上一次的错误。
errno 是一个包含在 <errno.h> 中的预定义的外部 int 变量,用于表示最近一个函数调用是否产生了错误。
- 若为0,则无错误;
- 其它值均表示某一种错误。
代码中需要包含 #include <errno.h>,当一个系统调用或着库函数的调用失败时,将会重置错误码 errno。用户在判断程序出错后,立即检验 errno 的值可以获取错误码和错误信息。
1.2 errno是线程安全的
在 Linux 上,全局 errno 变量是特定于线程的。POSIX 要求 errno 必须是线程安全的。
参阅:Thread-safety and POSIX.1 (unix.org)
在 POSIX.1 中,errno 被定义为外部全局变量。但是此定义在多线程环境中是不可接受的,因为使用它会导致不确定的结果。问题是两个或多个线程可能会遇到错误,所有错误都会导致设置相同的错误号。在这种情况下,一个线程可能已经被另一个线程更新后,最终检查 errno。
为了避免产生不确定性,POSIX.1c 将 errno 重新定义为可以访问每个线程错误号的服务:
- 某些函数可能在通过符号 errno 访问的变量中提供错误号。
- errno 符号是通过包括 C 标准所指定的标头来定义的。
- 对于进程的每个线程,errno 的值不应受函数调用或其他线程对 errno 的分配的影响。
参阅:errno(3): number of last error - Linux man page (die.net)
errno 是线程本地的;在一个线程中设置它不会影响在其他任何线程中的值
我们可以通过在机器上运行一个简单的程序来进行检查。
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#define NTHREADS 5
void *thread_function(void *dummyPtr)
{
printf("Thread number %ld addr(errno):%p\n", pthread_self(), &errno);
}
int main()
{
pthread_t thread_id[NTHREADS];
int i, j;
for (i = 0; i < NTHREADS; i++)
{
pthread_create(&thread_id[i], NULL, thread_function, NULL);
}
for (j = 0; j < NTHREADS; j++)
{
pthread_join(thread_id[j], NULL);
}
return 0;
}
输出结果:

1.3 errno 宏定义
在头文件「/usr/include/asm-generic/errno-base.h」中对基础的常用 errno 进行了宏定义:
| define | errno | explain |
|---|---|---|
| EPERM | 1 | Operation not permitted |
| ENOENT | 2 | No such file or directory |
| ESRCH | 3 | No such process |
| EINTR | 4 | Interrupted system call |
| EIO | 5 | I/O error |
| ENXIO | 6 | No such device or address |
| E2BIG | 7 | Argument list too long |
| ENOEXEC | 8 | Exec format error |
| EBADF | 9 | Bad file number |
| ECHILD | 10 | No child processes |
| EAGAIN | 11 | Try again |
| ENOMEM | 12 | Out of memory |
| EACCES | 13 | Permission denied |
| EFAULT | 14 | Bad address |
| ENOTBLK | 15 | Block device required |
| EBUSY | 16 | Device or resource busy |
| EEXIST | 17 | File exists |
| EXDEV | 18 | Cross-device link |
| ENODEV | 19 | No such device |
| ENOTDIR | 20 | Not a directory |
| EISDIR | 21 | Is a directory |
| EINVAL | 22 | Invalid argument |
| ENFILE | 23 | File table overflow |
| EMFILE | 24 | Too many open files |
| ENOTTY | 25 | Not a typewriter |
| ETXTBSY | 26 | Text file busy |
| EFBIG | 27 | File too large |
| ENOSPC | 28 | No space left on device |
| ESPIPE | 29 | Illegal seek |
| EROFS | 30 | Read-only file system |
| EMLINK | 31 | Too many links |
| EPIPE | 32 | Broken pipe |
| EDOM | 33 | Math argument out of domain of func |
| ERANGE | 34 | Math result not representable |
在 「/usr/include/asm-generic/errno.h」 中,对剩余的 errno 做了宏定义:
| define | errno | explain |
|---|---|---|
| EDEADLK | 35 | Resource deadlock would occur |
| ENAMETOOLONG | 36 | File name too long |
| ENOLCK | 37 | No record locks available |
| ENOSYS | 38 | Function not implemented |
| ENOTEMPTY | 39 | Directory not empty |
| ELOOP | 40 | Too many symbolic links encountered |
| EWOULDBLOCK | EAGAIN | Operation would block |
| ENOMSG | 42 | No message of desired type |
| EIDRM | 43 | Identifier removed |
| ECHRNG | 44 | Channel number out of range |
| EL2NSYNC | 45 | Level 2 not synchronized |
| EL3HLT | 46 | Level 3 halted |
| EL3RST | 47 | Level 3 reset |
| ELNRNG | 48 | Link number out of range |
| EUNATCH | 49 | Protocol driver not attached |
| ENOCSI | 50 | No CSI structure available |
| EL2HLT | 51 | Level 2 halted |
| EBADE | 52 | Invalid exchange |
| EBADR | 53 | Invalid request descriptor |
| EXFULL | 54 | Exchange full |
| ENOANO | 55 | No anode |
| EBADRQC | 56 | Invalid request code |
| EBADSLT | 57 | Invalid slot |
| EDEADLOCK | EDEADLK | |
| EBFONT | 59 | Bad font file format |
| ENOSTR | 60 | Device not a stream |
| ENODATA | 61 | No data available |
| ETIME | 62 | Timer expired |
| ENOSR | 63 | Out of streams resources |
| ENONET | 64 | Machine is not on the network |
| ENOPKG | 65 | Package not installed |
| EREMOTE | 66 | Object is remote |
| ENOLINK | 67 | Link has been severed |
| EADV | 68 | Advertise error |
| ESRMNT | 69 | Srmount error |
| ECOMM | 70 | Communication error on send |
| EPROTO | 71 | Protocol error |
| EMULTIHOP | 72 | Multihop attempted |
| EDOTDOT | 73 | RFS specific error |
| EBADMSG | 74 | Not a data message |
| EOVERFLOW | 75 | Value too large for defined data type |
| ENOTUNIQ | 76 | Name not unique on network |
| EBADFD | 77 | File descriptor in bad state |
| EREMCHG | 78 | Remote address changed |
| ELIBACC | 79 | Can not access a needed shared library |
| ELIBBAD | 80 | Accessing a corrupted shared library |
| ELIBSCN | 81 | .lib section in a.out corrupted |
| ELIBMAX | 82 | Attempting to link in too many shared libraries |
| ELIBEXEC | 83 | Cannot exec a shared library directly |
| EILSEQ | 84 | Illegal byte sequence |
| ERESTART | 85 | Interrupted system call should be restarted |
| ESTRPIPE | 86 | Streams pipe error |
| EUSERS | 87 | Too many users |
| ENOTSOCK | 88 | Socket operation on non-socket |
| EDESTADDRREQ | 89 | Destination address required |
| EMSGSIZE | 90 | Message too long |
| EPROTOTYPE | 91 | Protocol wrong type for socket |
| ENOPROTOOPT | 92 | Protocol not available |
| EPROTONOSUPPORT | 93 | Protocol not supported |
| ESOCKTNOSUPPORT | 94 | Socket type not supported |
| EOPNOTSUPP | 95 | Operation not supported on transport endpoint |
| EPFNOSUPPORT | 96 | Protocol family not supported |
| EAFNOSUPPORT | 97 | Address family not supported by protocol |
| EADDRINUSE | 98 | Address already in use |
| EADDRNOTAVAIL | 99 | Cannot assign requested address |
| ENETDOWN | 100 | Network is down |
| ENETUNREACH | 101 | Network is unreachable |
| ENETRESET | 102 | Network dropped connection because of reset |
| ECONNABORTED | 103 | Software caused connection abort |
| ECONNRESET | 104 | Connection reset by peer |
| ENOBUFS | 105 | No buffer space available |
| EISCONN | 106 | Transport endpoint is already connected |
| ENOTCONN | 107 | Transport endpoint is not connected |
| ESHUTDOWN | 108 | Cannot send after transport endpoint shutdown |
| ETOOMANYREFS | 109 | Too many references: cannot splice |
| ETIMEDOUT | 110 | Connection timed out |
| ECONNREFUSED | 111 | Connection refused |
| EHOSTDOWN | 112 | Host is down |
| EHOSTUNREACH | 113 | No route to host |
| EALREADY | 114 | Operation already in progress |
| EINPROGRESS | 115 | Operation now in progress |
| ESTALE | 116 | Stale file handle |
| EUCLEAN | 117 | Structure needs cleaning |
| ENOTNAM | 118 | Not a XENIX named type file |
| ENAVAIL | 119 | No XENIX semaphores available |
| EISNAM | 120 | Is a named type file |
| EREMOTEIO | 121 | Remote I/O error |
| EDQUOT | 122 | Quota exceeded |
| ENOMEDIUM | 123 | No medium found |
| EMEDIUMTYPE | 124 | Wrong medium type |
| ECANCELED | 125 | Operation Canceled |
| ENOKEY | 126 | Required key not available |
| EKEYEXPIRED | 127 | Key has expired |
| EKEYREVOKED | 128 | Key has been revoked |
| EKEYREJECTED | 129 | Key was rejected by service |
| EOWNERDEAD | 130 | Owner died |
| ENOTRECOVERABLE | 131 | State not recoverable |
| ERFKILL | 132 | Operation not possible due to RF-kill |
| EHWPOISON | 133 | Memory page has hardware error |
二、打印 errno
若想要打印 errno,需要包含头文件 #include <errno.h>。
2.1 使用 perror 打印错误信息
函数原型:void perror(const char *s)
头 文 件:#include <stdio.h>
作 用:打印系统错误信息
2.2 使用 strerror 显示错误信息
函数原型:char *strerror(int errnum);
头 文 件:#include <string.h>
作 用:将错误码以字符串的信息显示出来
三、输出 errno 的小 Demo
#include <stdio.h>
#include <string.h>
static void __Process(char *str)
{
if (strstr(str, "_ASM_GENERIC_") != NULL || strstr(str, "define") == NULL)
{
return;
}
if (strstr(str, "EWOULDBLOCK") != NULL || strstr(str, "EDEADLOCK") != NULL)
{
return;
}
char szDefine[64] = {0};
int errno = -1;
char szExplain[64] = {0};
sscanf(str, "%*[^A-Z]%s%*[^0-9]%d %*[^*]* %[^*]s", szDefine, &errno, szExplain);
szExplain[strlen(szExplain) - 1] = 0; // 去除最后的空格
char buf[1024] = {0};
snprintf(buf, sizeof(buf), "|%s|%d|%s|", szDefine, errno, szExplain);
puts(buf);
}
int main()
{
freopen("/usr/include/asm-generic/errno-base.h", "r", stdin); // 读文件
// freopen("E:\\Documents\\stdin&&stdout\\stdout\\文件名", "w", stdout); // 写文件
while (1)
{
char str[1024] = {0};
fgets(str, sizeof(str), stdin);
if (0 == strncmp(str, "#endif", 6))
break;
__Process(str);
}
puts("-----------------------------------------------------------");
freopen("/usr/include/asm-generic/errno.h", "r", stdin); // 读文件
while (1)
{
char str[1024] = {0};
fgets(str, sizeof(str), stdin);
if (0 == strncmp(str, "#endif", 6))
break;
__Process(str);
}
}

浙公网安备 33010602011771号