Est errno thread-safe?
dans errno.h
, cette variable est déclarée comme extern int errno;
donc ma question Est, est-il sûr de vérifier la valeur errno
après quelques appels ou utiliser perror() en code multi-threadé. Est-ce une variable thread safe? Si ce n'est pas le cas, quelle est l'alternative ?
j'utilise linux avec gcc sur l'architecture x86.
8 réponses
Oui, c'est sans fil. Sous Linux, la variable globale errno est spécifique au thread. POSIX exige que errno soit threadsafe.
voir http://www.unix.org/whitepapers/reentrant.html
in POSIX.1, errno est défini comme un variable globale externe. Mais ce la définition est inacceptable dans un environnement multithread, parce que ses utilisation peut entraîner la non-déterministes résultat. Le problème est que deux ou plusieurs threads peuvent rencontrer des erreurs, tous les provoquant le même errno à être réglé. Dans ces circonstances, un fil pourriez vérifier errno après il a déjà été mis à jour par un autre fil.
pour contourner le résultat non-déterminisme, POSIX.1c redéfinit errno comme un service qui peut accéder à la nombre d'erreurs par thread comme suit: (ISO / CEI 9945:1-1996, §2.4):
Certaines fonctions peuvent fournir le numéro d'erreur dans un variable accessible par le symbole errno. Symbole errno est défini en incluant l'en-tête , comme spécifié par le C Standard ... Pour chaque thread d'un processus, la valeur de errno est pas être affecté par des appels de fonction ou assignations à errno par d'autres threads.
Voir aussi http://linux.die.net/man/3/errno
errno est thread-local; le réglage dans un thread n'affecte pas sa valeur dans tout autre fil.
Oui
Errno n'est pas une simple variable de plus, c'est quelque chose de complexe de derrière les coulisses, spécifiquement pour être thread-safe.
voir $ 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.
On peut double-check:
$ cat > test.c
#include <errno.h>
f() { g(errno); }
$ cc -E test.c | grep ^f
f() { g((*__errno_location ())); }
$
Dans errno.h, cette variable est déclarée comme extern int errno;
voici ce que dit la norme C:
la macro
errno
ne doit pas nécessairement être l'identificateur d'un objet. Il peut s'étendre à une valeur l modifiable résultant d'un appel de fonction (par exemple,*errno()
).
généralement, errno
est une macro qui appelle une fonction retournant l'adresse de le numéro d'erreur pour le thread courant, puis le déréfère.
voici ce que J'ai sur Linux, dans /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
à la fin, il génère ce genre de 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
sur de nombreux systèmes Unix, compiler avec -D_REENTRANT
garantit que errno
est thread-safe.
par exemple:
#if defined(_REENTRANT) || _POSIX_C_SOURCE - 0 >= 199506L
extern int *___errno();
#define errno (*(___errno()))
#else
extern int errno;
/* ANSI C++ requires that errno be a macro */
#if __cplusplus >= 199711L
#define errno errno
#endif
#endif /* defined(_REENTRANT) */
c'est de <sys/errno.h>
sur mon Mac:
#include <sys/cdefs.h>
__BEGIN_DECLS
extern int * __error(void);
#define errno (*__error())
__END_DECLS
Donc errno
est maintenant une fonction __error()
. La fonction est implémentée de manière à être sans fil.
je pense que la réponse est "ça dépend". Les bibliothèques d'exécution de Thread-safe C implémentent habituellement errno comme appel de fonction (macro expansion vers une fonction) si vous construisez du code threadé avec les bons drapeaux.
yes , comme il est expliqué dans la errno man page et les autres réponses, errno est une variable locale de thread.
cependant , il y a un détail stupide qui pourrait être facilement oublié. Les programmes doivent sauvegarder et restaurer l'errno sur n'importe quel gestionnaire de signal exécutant un appel système. C'est parce que le signal sera manipulé par l'un des fils de processus qui pourrait écraser sa valeur.
par conséquent, les gestionnaires de signaux doivent sauvegarder et restaurer errno. Quelque chose comme:
void sig_alarm(int signo)
{
int errno_save;
errno_save = errno;
//whatever with a system call
errno = errno_save;
}
We can check by running a simple program on a machine.
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#define NTHREADS 5
void *thread_function(void *);
int
main()
{
pthread_t thread_id[NTHREADS];
int i, j;
for(i=0; i < NTHREADS; i++)
{
pthread_create( &thread_id[i], NULL, thread_function, NULL );
}
for(j=0; j < NTHREADS; j++)
{
pthread_join( thread_id[j], NULL);
}
return 0;
}
void *thread_function(void *dummyPtr)
{
printf("Thread number %ld addr(errno):%p\n", pthread_self(), &errno);
}
Running this program and you can see different addresses for errno in each thread. The output of a run on my machine looked like:-
Thread number 140672336922368 addr(errno):0x7ff0d4ac0698
Thread number 140672345315072 addr(errno):0x7ff0d52c1698
Thread number 140672328529664 addr(errno):0x7ff0d42bf698
Thread number 140672320136960 addr(errno):0x7ff0d3abe698
Thread number 140672311744256 addr(errno):0x7ff0d32bd698
Notice that address is different for all threads.