<errno.h>定义了整数变量extern int errno。errno是一个由POSIX和ISO C标准定义的符号,由系统调用和某些库函数根据事件来设置它,用以表明哪里有问题。这个值只有当调用的返回表明错误的时候有用(比如,对于大多数的系统调用是-1,对于大多数的库函数来说是-1或NULL),正确的函数也可以修改errno。
在程序启动时,errno 设置为零,系统调用或 C 标准库中的特定函数修改它的值为一些非零值以表示某些类型的错误。有效的错误number都是非零的,系统调用和库函数不会把errno设为0。 对于某些系统调用和库函数(比如,getpriority(2)),没有错误的时候也会返回-1。在这种情况下,可以在调用之前先将errno设为0,当不确定有没有错误的时候,可以通过查看errno是不是一个非零值来确定是否发生错误。
ISO C标准将errno定义为一个可以修改的int型左值,并且不允许准确声明,errno可能是一个宏,也可能被定义成一个变量,这个具体要看编译器自己的实现。
系统调用或库函数正确执行,并不保证errno的值不会被改变。
✏️ 1、线程安全性
早些时候,POSIX.1曾把errno定义成extern int errno这种形式,但这种形式实现的errno在多线程环境下是不安全的,errno变量是被多个线程共享的,这样可能线程A发生某些错误改变了errno的值,线程B虽然没有发生任何错误,但是当它检测errno的值的时候,线程B会以为自己发生了错误。
# ifndef __ASSEMBLER__
/* Function to get address of global `errno' variable. */
extern int *__errno_location (void) __THROW __attribute__ ((__const__));
# 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 */
在error.h中:
/* Declare the `errno' variable, unless it's defined as a macro by
bits/errno.h. This is the case in GNU, where it is a per-thread
variable. This redeclaration using the macro still works, but it
will be a function declaration without a prototype and may trigger
a -Wstrict-prototypes warning. */
#ifndef errno
extern int errno;
#endif
可以清晰的看到,bits/errno.h对errno进行了重定义。从 __attribute__ ((__const__))推测出__errno_location ()会返回与参数无关的与线程绑定的一个特定地址,应用层直接从该地址取出errno的。(关于__attribute__用法可以参考Using GNU C attribute)。但是上面使用了条件编译,也就是有两种方法可以使得gcc重定义errno:
显然,__errno_location() 函数的返回值就是__hurd_threadvar_location(_HURD_THREADVAR_ERRNO) 函数的返回值被强转成 int * 了,那 __hurd_threadvar_location(_HURD_THREADVAR_ERRNO) 又是什么函数呢?
在头文件 <hurd/threadvar.h>中有:
#include <machine-sp.h> /* Define __thread_stack_pointer. */
/* Return the location of the current thread's value for the
per-thread variable with index INDEX. */
extern unsigned long int *
__hurd_threadvar_location (enum __hurd_threadvar_index __index) __THROW
/* This declaration tells the compiler that the value is constant
given the same argument. We assume this won't be called twice from
the same stack frame by different threads. */
__attribute__ ((__const__));
_HURD_THREADVAR_H_EXTERN_INLINE unsigned long int *
__hurd_threadvar_location (enum __hurd_threadvar_index __index)
{
return __hurd_threadvar_location_from_sp (__index,
__thread_stack_pointer ());
}
extern unsigned long int *__hurd_threadvar_location_from_sp
(enum __hurd_threadvar_index __index, void *__sp);
_HURD_THREADVAR_H_EXTERN_INLINE unsigned long int *
__hurd_threadvar_location_from_sp (enum __hurd_threadvar_index __index,
void *__sp)
{
unsigned long int __stack = (unsigned long int) __sp;
return &((__stack >= __hurd_sigthread_stack_base &&
__stack < __hurd_sigthread_stack_end)
? __hurd_sigthread_variables
: (unsigned long int *) ((__stack & __hurd_threadvar_stack_mask) +
__hurd_threadvar_stack_offset))[__index];
}
往前有这些变量的定义:
enum __hurd_threadvar_index
{
_HURD_THREADVAR_MIG_REPLY, /* Reply port for MiG user stub functions. */
_HURD_THREADVAR_ERRNO, /* `errno' value for this thread. */
_HURD_THREADVAR_SIGSTATE, /* This thread's `struct hurd_sigstate'. */
_HURD_THREADVAR_DYNAMIC_USER, /* Dynamically-assigned user variables. */
_HURD_THREADVAR_MALLOC, /* For use of malloc. */
_HURD_THREADVAR_DL_ERROR, /* For use of -ldl and dynamic linker. */
_HURD_THREADVAR_RPC_VARS, /* For state of RPC functions. */
_HURD_THREADVAR_LOCALE, /* For thread-local locale setting. */
_HURD_THREADVAR_CTYPE_B, /* Cache of thread-local locale data. */
_HURD_THREADVAR_CTYPE_TOLOWER, /* Cache of thread-local locale data. */
_HURD_THREADVAR_CTYPE_TOUPPER, /* Cache of thread-local locale data. */
_HURD_THREADVAR_MAX /* Default value for __hurd_threadvar_max. */
};
extern unsigned long int __hurd_threadvar_stack_mask;
extern unsigned long int __hurd_threadvar_stack_offset;
extern unsigned long int __hurd_threadvar_stack_mask;
extern unsigned long int __hurd_threadvar_stack_offset;
extern unsigned long int __hurd_sigthread_stack_base;
extern unsigned long int __hurd_sigthread_stack_end;
extern unsigned long int *__hurd_sigthread_variables;
注释如下:
/* The per-thread variables are found by ANDing this mask
with the value of the stack pointer and then adding this offset.
In the multi-threaded case, cthreads initialization sets
__hurd_threadvar_stack_mask to ~(cthread_stack_size - 1), a mask which
finds the base of the fixed-size cthreads stack; and
__hurd_threadvar_stack_offset to a small offset that skips the data
cthreads itself maintains at the base of each thread's stack.
In the single-threaded case, __hurd_threadvar_stack_mask is zero, so the
stack pointer is ignored; and __hurd_threadvar_stack_offset gives the
address of a small allocated region which contains the variables for the
single thread. */
/* A special case must always be made for the signal thread. Even when there
is only one user thread and an allocated region can be used for the user
thread's variables, the signal thread needs to have its own location for
per-thread variables. The variables __hurd_sigthread_stack_base and
__hurd_sigthread_stack_end define the bounds of the stack used by the
signal thread, so that thread can always be specifically identified. */
/* At the location described by the two variables above,
there are __hurd_threadvar_max `unsigned long int's of per-thread data. */