1. errno是个什么东西?
-
The 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
-
#include
-
-
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
-
#include
-
-
int main(int argc, char *argv[])
-
{
-
int i = 0;
-
-
for (; i < 256; i++)
-
printf("errno %d: %s\n", i, strerror(i));
-
-
return 0;
-
}
趁机也贴出每个errno的解释:
4. errno使用的注意事项。
其实也就两条:
1)当使用系统调用和库函数时,除了函数的返回值,记得还要考虑errno的值啊。
2)并不是所有的系统调用活着库函数都会设置errno的值,如果你的程序对它有依赖,需要开发人员在接口错误处理中,手工设置。