it-swarm.com.de

Ist errno threadsicher?

In errno.h ist diese Variable als extern int errno; deklariert. Meine Frage lautet also: Ist es sicher, den errno-Wert nach einigen Aufrufen zu überprüfen oder perror () in Multithreadcode-Code zu verwenden? Ist dies eine Thread-sichere Variable? Wenn nicht, was ist dann die Alternative? 

Ich verwende Linux mit GCC auf X86-Architektur. 

150
vinit dhatrak

Ja, es ist fadensicher. Unter Linux ist die globale Variable errno Thread-spezifisch. POSIX setzt voraus, dass errno threadsicher ist.

Siehe http://www.unix.org/whitepapers/reentrant.html

In POSIX.1 ist errno als .__ definiert. externe globale Variable. Aber dieses Definition ist in einem .__ nicht akzeptabel. Multithread-Umgebung, weil es Verwendung kann zu nichtdeterministischem Ergebnis führen Ergebnisse. Das Problem ist, dass zwei oder mehr Threads können auf Fehler stoßen, alle bewirkt, dass das gleiche errno gesetzt wird . Unter diesen Umständen ein Thread Vielleicht endet errno danach wurde bereits von einem anderen .__ aktualisiert. Faden.

Um das resultierende .__ zu umgehen. Nichtdeterminismus, definiert POSIX.1c Errno als Dienst, der auf die .__ zugreifen kann. Pro-Thread-Fehlernummer wie folgt (ISO/IEC 9945: 1-1996, §2.4):

Einige Funktionen können die Fehlernummer in einer Variablen angeben, auf die .__ zugegriffen wird. durch das Symbol errno. Das Symbol errno wird durch das Einschließen von .__ definiert. Header, wie von der .__ angegeben. C Standard ... Für jeden Thread eines Prozess, der Wert von errno darf nicht von Funktionsaufrufen oder .__ betroffen sein. Zuweisungen von anderen Threads zu errno.

Siehe auch http://linux.die.net/man/3/errno

errno ist fadenlokal; Das Festlegen in einem Thread hat keinen Einfluss auf den Wert in einem anderen Thread. 

158
Charles Salvia

Ja


Errno ist keine einfache Variable mehr, es ist etwas komplexes hinter den Kulissen, speziell für den Thread.

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 überprüfen:

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

In errno.h ist diese Variable als extern int errno deklariert.

Das sagt der C-Standard:

Das Makro errno muss nicht der Bezeichner eines Objekts sein. Es kann sich auf einen modifizierbaren lvalue ausdehnen, der aus einem Funktionsaufruf resultiert (z. B. *errno()).

Im Allgemeinen ist errno ein Makro, das eine Funktion aufruft, die die Adresse der Fehlernummer für den aktuellen Thread zurückgibt, und sie 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
10

Auf vielen Unix-Systemen wird durch die Kompilierung mit -D_REENTRANT sichergestellt, dass errno threadsicher ist.

Zum Beispiel:

#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

Dies ist von <sys/errno.h> auf meinem Mac:

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

errno ist jetzt eine Funktion __error(). Die Funktion ist so implementiert, dass sie Thread-sicher ist.

8
vy32

yes, wie in der Manpage errno erläutert und in den anderen Antworten, errno ist eine lokale Thread-Variable. 

Jedoch, es gibt ein dummes Detail, das leicht vergessen werden kann. Programme sollten das Errno für jeden Signal-Handler speichern und wiederherstellen, der einen Systemaufruf ausführt. Dies liegt daran, dass das Signal von einem der Prozessthreads verarbeitet wird, der seinen Wert überschreiben könnte. 

Daher sollten die Signalhandler die Errno speichern und wiederherstellen. So etwas wie:

void sig_alarm(int signo)
{
 int errno_save;

 errno_save = errno;

 //whatever with a system call

 errno = errno_save;
}
6

Ich denke, die Antwort lautet "es kommt darauf an". Thread-sichere C-Laufzeitbibliotheken implementieren normalerweise errno als Funktionsaufruf (Makroerweiterung zu einer Funktion), wenn Sie Threadcode mit den richtigen Flags erstellen.

6
Timo Geusch

Wir können dies überprüfen, indem Sie ein einfaches Programm auf einer Maschine ausführen.

#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);                                                                                       
}

Wenn Sie dieses Programm ausführen, können Sie in jedem Thread unterschiedliche Adressen für errno sehen. Die Ausgabe eines Laufs auf meiner Maschine sah folgendermaßen aus:

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 

Beachten Sie, dass die Adresse für alle Threads unterschiedlich ist.

1
Rajat Paliwal