一、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);
}
}
参考资料
- Linux errno详解 - Jimmy_Nie - 博客园 (cnblogs.com)
- errno 介绍_shanandqiu的博客-CSDN博客
- sscanf函数使用详解_faihung的博客-CSDN博客
- fgets函数及其用法,C语言fgets函数详解_发狂的蜗牛的博客-CSDN博客