Ja, es ist threadsicher. Unter Linux ist die globale errno-Variable Thread-spezifisch. POSIX erfordert, dass errno threadsicher ist.
Siehe http://www.unix.org/whitepapers/reentrant.html
In POSIX.1 ist errno als externe globale Variable definiert. Aber diese Definition ist in einer Umgebung mit mehreren Threads nicht akzeptabel, da ihre Verwendung zu nicht deterministischen Ergebnissen führen kann. Das Problem besteht darin, dass zwei oder mehr Threads auf Fehler stoßen können, die alle dazu führen, dass dieselbe Fehlernummer gesetzt wird. Unter diesen Umständen kann es vorkommen, dass ein Thread die Fehlernummer überprüft, nachdem er bereits von einem anderen Thread aktualisiert wurde.
Um den daraus resultierenden Nichtdeterminismus zu umgehen, definiert POSIX.1c serrno als Dienst neu, der wie folgt auf die Fehlernummer pro Thread zugreifen kann (ISO/IEC 9945:1-1996, §2.4):
Einige Funktionen können die Fehlernummer in einer Variablen bereitstellen, auf die über das Symbol errno zugegriffen wird. Das Symbol errno wird definiert, indem der Header eingeschlossen wird, wie vom C-Standard spezifiziert ... Für jeden Thread eines Prozesses soll der Wert von errno nicht durch Funktionsaufrufe oder Zuweisungen an errno durch andere Threads beeinflusst werden.
Siehe auch http://linux.die.net/man/3/errno
errno ist Thread-lokal; Wenn Sie es in einem Thread festlegen, wirkt sich dies nicht auf seinen Wert in einem anderen Thread aus.
Ja
Errno ist keine einfache Variable mehr, es ist etwas Komplexes hinter den Kulissen, speziell um Thread-sicher zu sein.
Siehe $ 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.
Wir können Folgendes überprüfen:
$ cat > test.c
#include <errno.h>
f() { g(errno); }
$ cc -E test.c | grep ^f
f() { g((*__errno_location ())); }
$
In errno.h ist diese Variable als extern int errno;
deklariertHier ist, was der C-Standard sagt:
Das Makro errno
muss nicht der Bezeichner eines Objekts sein. Es kann zu einem änderbaren Lvalue erweitert werden, der sich aus einem Funktionsaufruf ergibt (z. B. *errno()
).
Im Allgemeinen errno
ist ein Makro, das eine Funktion aufruft, die die Adresse der Fehlernummer für den aktuellen Thread zurückgibt, und sie dann dereferenziert.
Folgendes habe ich unter Linux in /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
Am Ende generiert es diese Art von Code:
> 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