在errno.h中,这个变量被声明为extern int errno;所以我的问题是,在一些调用后检查errno值或在多线程代码中使用perror()是否安全。这是线程安全的变量吗?如果不是,那么有什么替代方案?

我在x86架构上使用gcc linux。


当前回答

是的,正如errno手册页和其他回复中解释的那样,errno是一个线程局部变量。

然而,有一个很容易被遗忘的愚蠢细节。程序应该在执行系统调用的任何信号处理程序上保存和恢复errno。这是因为信号将由一个进程线程处理,该进程线程可以覆盖其值。

因此,信号处理程序应该保存并恢复errno。喜欢的东西:

void sig_alarm(int signo)
{
 int errno_save;

 errno_save = errno;

 //whatever with a system call

 errno = errno_save;
}

其他回答

这是来自<sys/errno.h>在我的Mac:

#include <sys/cdefs.h>
__BEGIN_DECLS
extern int * __error(void);
#define errno (*__error())
__END_DECLS

所以errno现在是一个__error()函数。函数的实现是线程安全的。

在errno.h中,这个变量被声明为extern int errno;

C标准是这么说的:

宏errno不必是对象的标识符。它可以扩展为由函数调用产生的可修改的左值(例如,*errno())。

通常,errno是一个宏,它调用一个函数,返回当前线程的错误编号的地址,然后解除引用。

这是我在Linux上的/usr/include/bits/errno.h:

/* 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

最后,它生成这样的代码:

> cat essai.c
#include <errno.h>

int
main(void)
{
    errno = 0;

    return 0;
}
> gcc -c -Wall -Wextra -pedantic essai.c
> objdump -d -M intel essai.o

essai.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
   0: 55                    push   ebp
   1: 89 e5                 mov    ebp,esp
   3: 83 e4 f0              and    esp,0xfffffff0
   6: e8 fc ff ff ff        call   7 <main+0x7>  ; get address of errno in EAX
   b: c7 00 00 00 00 00     mov    DWORD PTR [eax],0x0  ; store 0 in errno
  11: b8 00 00 00 00        mov    eax,0x0
  16: 89 ec                 mov    esp,ebp
  18: 5d                    pop    ebp
  19: c3                    ret

是的,正如errno手册页和其他回复中解释的那样,errno是一个线程局部变量。

然而,有一个很容易被遗忘的愚蠢细节。程序应该在执行系统调用的任何信号处理程序上保存和恢复errno。这是因为信号将由一个进程线程处理,该进程线程可以覆盖其值。

因此,信号处理程序应该保存并恢复errno。喜欢的东西:

void sig_alarm(int signo)
{
 int errno_save;

 errno_save = errno;

 //whatever with a system call

 errno = errno_save;
}

是的,它是线程安全的。在Linux上,全局errno变量是特定于线程的。POSIX要求errno是线程安全的。

参见http://www.unix.org/whitepapers/reentrant.html

In POSIX.1, errno is defined as an external global variable. But this definition is unacceptable in a multithreaded environment, because its use can result in nondeterministic results. The problem is that two or more threads can encounter errors, all causing the same errno to be set. Under these circumstances, a thread might end up checking errno after it has already been updated by another thread. To circumvent the resulting nondeterminism, POSIX.1c redefines errno as a service that can access the per-thread error number as follows (ISO/IEC 9945:1-1996, §2.4): Some functions may provide the error number in a variable accessed through the symbol errno. The symbol errno is defined by including the header , as specified by the C Standard ... For each thread of a process, the value of errno shall not be affected by function calls or assignments to errno by other threads.

参见http://linux.die.net/man/3/errno

Errno是线程本地的;在一个线程中设置它不会影响它在其他线程中的值。

Yes


Errno不再是一个简单的变量,它是一个复杂的幕后变量,特别是为了它是线程安全的。

参见$ man 3 errno:

ERRNO(3)                   Linux Programmer’s Manual                  ERRNO(3)

NAME
       errno - number of last error

SYNOPSIS
       #include <errno.h>

DESCRIPTION

      ...
       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.

我们可以反复检查:

$ cat > test.c
#include <errno.h>
f() { g(errno); }
$ cc -E test.c | grep ^f
f() { g((*__errno_location ())); }
$