Ano, je to vlákno bezpečné. V Linuxu je globální proměnná errno specifická pro vlákno. POSIX vyžaduje, aby errno bylo bezpečné pro vlákna.
Viz http://www.unix.org/whitepapers/reentrant.html
V POSIX.1 je errno definováno jako externí globální proměnná. Ale tato definice je nepřijatelná v prostředí s více vlákny, protože její použití může vést k nedeterministickým výsledkům. Problém je v tom, že dvě nebo více vláken může narazit na chyby, které způsobí nastavení stejného errno. Za těchto okolností může vlákno skončit kontrolou errno poté, co již bylo aktualizováno jiným vláknem.
Aby se předešlo výslednému nedeterminismu, POSIX.1c předefinuje jako službu, která má přístup k číslu chyby pro vlákno následovně (ISO/IEC 9945:1-1996, §2.4):
Některé funkce mohou poskytnout číslo chyby v proměnné přístupné prostřednictvím symbolu errno. Symbolerrno je definováno zahrnutím záhlaví, jak je specifikováno standardem C... Pro každé vlákno procesu nesmí být hodnota errno ovlivněna voláním funkcí nebo přiřazením k errno jinými vlákny.
Viz také http://linux.die.net/man/3/errno
errno je místní vlákno; jeho nastavení v jednom vlákně neovlivní jeho hodnotu v žádném jiném vlákně.
Ano
Errno už není jednoduchá proměnná, je to něco složitého v zákulisí, konkrétně proto, aby byla bezpečná pro vlákna.
Viz $ 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.
Můžeme znovu zkontrolovat:
$ cat > test.c
#include <errno.h>
f() { g(errno); }
$ cc -E test.c | grep ^f
f() { g((*__errno_location ())); }
$
V errno.h je tato proměnná deklarována jako extern int errno;
Zde je to, co říká standard C:
Makro
errno
nemusí být identifikátorem objektu. Může se rozšířit na modifikovatelnou lvalue vyplývající z volání funkce (například*errno()
).
Obecně errno
je makro, které volá funkci vracející adresu čísla chyby pro aktuální vlákno a poté ji dereferencuje.
Zde je to, co mám na Linuxu, v /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
Nakonec vygeneruje tento druh kódu:
> 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