linux中的errno,你是否踩过它的坑?

之所以写这篇文章,是因为我踩过它的坑。背景是我在做一个项目时,其中有一部分是在QEMU中调用cephfs的接口,期间有个函数ceph_lstat(),明明返回了错误的值,但QEMU缺认为这个调用没用问题,当时我没有觉得这个函数本身有问题,所以一直在debug别的东西。整整花了一周,终于找到了原因:QEMU中有些错误处理依赖于errno的值,但cephfs的接口在发生error时不会去设置它,我勒个去,当时我就泪奔了。。。鉴于我惨痛的经历,所以在这里将errno的信息整理一下,方便大家了解。另外也希望给大家提一个醒,当你得程序涉及系统调用时,记得要考虑errno这个隐形的杀手。

1. errno是个什么东西?

       The <errno.h> header file defines the integer variable errno, which
       is set by system calls and some library functions in the event of an
       error to indicate what went wrong.  Its value is significant only
       when the return value of the call indicated an error (i.e., -1 from
       most system calls; -1 or NULL from most library functions); a
       function that succeeds is allowed to change errno.

       Valid error numbers are all nonzero; errno is never set to zero by
       any system call or library function.

       For some system calls and library functions (e.g., getpriority(2)),
       -1 is a valid return on success.  In such cases, a successful return
       can be distinguished from an error return by setting errno to zero
       before the call, and then, if the call returns a status that
       indicates that an error may have occurred, checking to see if errno
       has a nonzero value.

       errno is defined by the ISO C standard to be a modifiable lvalue of
       type int, and must not be explicitly declared; errno may be a macro.
       errno is thread-local; setting it in one thread does not affect its
       value in any other thread.
以上是linux中关于errno的manpage,大意可以概括为:
1)errno是一个整型变量,当系统调用和一些库函数发生错误时会通过设置errno的值来告诉调用者出了什么问题。
2)errno的有效值都是非零的。(这个manpage有个悖论,第二段中说,errno从来不能被设为0,而在第三段又说有些接口会将其设置为0)
3)errno在ISO C标准中定义的,它可能是一个宏并且不能被显示声明(也就是重新定义)。
4)errno是线程安全的,在一个线程中设置它,不会影响别的线程对它的使用。这一点很重要,不知道有没有像我之前有过这样的问题:看起来errno像是一个全局的变量,应该不是线程安全的吧?看了这个之后,就有答案了,errno是thread-local的,也就是每个线程都有一个。

2. errno的定义
errno被定义在/usr/include/bits/errno.h文件中:

#  if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value.  */
#   define errno (*__errno_location ())
#  endif
# endif /* !__ASSEMBLER__ */
#endif /* _ERRNO_H */
也就是说当编译器检查没有定义_LIBC或者定义了_LIBC_REENTRANT,errno就是线程安全的。可以通过如下程序来判断你的系统中这些宏是否被定义:
[root@cephenv ~]# cat testenv.c
#include <stdio.h>
#include <errno.h>

int main( void )
{
#ifndef __ASSEMBLER__
        printf( "__ASSEMBLER__ is not defined\n");
#else
        printf( "__ASSEMBLER__ is defined\n");
#endif

#ifndef __LIBC
        printf( "__LIBC is not defined\n");
#else
        printf( "__LIBC is defined\n");
#endif

#ifndef _LIBC_REENTRANT
        printf( "_LIBC_REENTRANT is not defined\n");
#else
        printf( "_LIBC_REENTRANT is defined\n");
#endif

        return 0;
}
在我的系统中输出的结果是:
[root@cephenv ~]# ./a.out
__ASSEMBLER__ is not defined
__LIBC is not defined
_LIBC_REENTRANT is not defined
由于 __LIBC没有被定义,所以errno是线程安全的。其实,通过gcc编译一般的应用程序时__LIBC都是没有定义的。

3. errno各返回值的含义:
它的每个值的含义定义在/usr/include/asm-generic/errno-base.h文件中(貌似不全啊),也可以通过strerror写一个小程序来输出错误码对应的解释:

[root@cephenv ~]# cat printerrno.c
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
	int i = 0;

	for (; i < 256; i++)
		printf("errno %d: %s\n", i, strerror(i));

	return 0;
}
趁机也贴出每个errno的解释:
[root@cephenv ~]# ./a.out
errno 0: Success
errno 1: Operation not permitted
errno 2: No such file or directory
errno 3: No such process
errno 4: Interrupted system call
errno 5: Input/output error
errno 6: No such device or address
errno 7: Argument list too long
errno 8: Exec format error
errno 9: Bad file descriptor
errno 10: No child processes
errno 11: Resource temporarily unavailable
errno 12: Cannot allocate memory
errno 13: Permission denied
errno 14: Bad address
errno 15: Block device required
errno 16: Device or resource busy
errno 17: File exists
errno 18: Invalid cross-device link
errno 19: No such device
errno 20: Not a directory
errno 21: Is a directory
errno 22: Invalid argument
errno 23: Too many open files in system
errno 24: Too many open files
errno 25: Inappropriate ioctl for device
errno 26: Text file busy
errno 27: File too large
errno 28: No space left on device
errno 29: Illegal seek
errno 30: Read-only file system
errno 31: Too many links
errno 32: Broken pipe
errno 33: Numerical argument out of domain
errno 34: Numerical result out of range
errno 35: Resource deadlock avoided
errno 36: File name too long
errno 37: No locks available
errno 38: Function not implemented
errno 39: Directory not empty
errno 40: Too many levels of symbolic links
errno 41: Unknown error 41
errno 42: No message of desired type
errno 43: Identifier removed
errno 44: Channel number out of range
errno 45: Level 2 not synchronized
errno 46: Level 3 halted
errno 47: Level 3 reset
errno 48: Link number out of range
errno 49: Protocol driver not attached
errno 50: No CSI structure available
errno 51: Level 2 halted
errno 52: Invalid exchange
errno 53: Invalid request descriptor
errno 54: Exchange full
errno 55: No anode
errno 56: Invalid request code
errno 57: Invalid slot
errno 58: Unknown error 58
errno 59: Bad font file format
errno 60: Device not a stream
errno 61: No data available
errno 62: Timer expired
errno 63: Out of streams resources
errno 64: Machine is not on the network
errno 65: Package not installed
errno 66: Object is remote
errno 67: Link has been severed
errno 68: Advertise error
errno 69: Srmount error
errno 70: Communication error on send
errno 71: Protocol error
errno 72: Multihop attempted
errno 73: RFS specific error
errno 74: Bad message
errno 75: Value too large for defined data type
errno 76: Name not unique on network
errno 77: File descriptor in bad state
errno 78: Remote address changed
errno 79: Can not access a needed shared library
errno 80: Accessing a corrupted shared library
errno 81: .lib section in a.out corrupted
errno 82: Attempting to link in too many shared libraries
errno 83: Cannot exec a shared library directly
errno 84: Invalid or incomplete multibyte or wide character
errno 85: Interrupted system call should be restarted
errno 86: Streams pipe error
errno 87: Too many users
errno 88: Socket operation on non-socket
errno 89: Destination address required
errno 90: Message too long
errno 91: Protocol wrong type for socket
errno 92: Protocol not available
errno 93: Protocol not supported
errno 94: Socket type not supported
errno 95: Operation not supported
errno 96: Protocol family not supported
errno 97: Address family not supported by protocol
errno 98: Address already in use
errno 99: Cannot assign requested address
errno 100: Network is down
errno 101: Network is unreachable
errno 102: Network dropped connection on reset
errno 103: Software caused connection abort
errno 104: Connection reset by peer
errno 105: No buffer space available
errno 106: Transport endpoint is already connected
errno 107: Transport endpoint is not connected
errno 108: Cannot send after transport endpoint shutdown
errno 109: Too many references: cannot splice
errno 110: Connection timed out
errno 111: Connection refused
errno 112: Host is down
errno 113: No route to host
errno 114: Operation already in progress
errno 115: Operation now in progress
errno 116: Stale file handle
errno 117: Structure needs cleaning
errno 118: Not a XENIX named type file
errno 119: No XENIX semaphores available
errno 120: Is a named type file
errno 121: Remote I/O error
errno 122: Disk quota exceeded
errno 123: No medium found
errno 124: Wrong medium type
errno 125: Operation canceled
errno 126: Required key not available
errno 127: Key has expired
errno 128: Key has been revoked
errno 129: Key was rejected by service
errno 130: Owner died
errno 131: State not recoverable
errno 132: Operation not possible due to RF-kill
4. errno使用的注意事项。
其实也就两条:
1)当使用系统调用和库函数时,除了函数的返回值,记得还要考虑errno的值啊。
2)并不是所有的系统调用活着库函数都会设置errno的值,如果你的程序对它有依赖,需要开发人员在接口错误处理中,手工设置。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值