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.

145
demandé sur unwind 2009-11-07 22:39:54

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.

152
répondu Charles Salvia 2009-11-07 19:50:11

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 ())); }
$ 
53
répondu DigitalRoss 2011-03-21 18:46:09

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
10
répondu Bastien Léonard 2009-11-07 20:24:55

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) */
9
répondu Jonathan Leffler 2009-11-07 19:48:37

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.

7
répondu vy32 2016-01-27 14:35:44

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.

6
répondu Timo Geusch 2009-11-07 19:45:04

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;
}
6
répondu marcmagransdeabril 2013-07-24 12:20:35
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.
0
répondu Rajat Paliwal 2018-09-15 11:21:37