¿Errno es seguro para subprocesos?

Resuelto Vinit Dhatrak asked hace 15 años • 8 respuestas

En errno.h, esta variable se declara así extern int errno;, mi pregunta es: ¿es seguro verificar errnoel valor después de algunas llamadas o usar perror() en código multiproceso? ¿Es esta una variable segura para subprocesos? Si no, ¿cuál es la alternativa?

Estoy usando Linux con gcc en arquitectura x86.

Vinit Dhatrak avatar Nov 08 '09 02:11 Vinit Dhatrak
Aceptado

Sí, es seguro para subprocesos. En Linux, la variable errno global es específica del subproceso. POSIX requiere que errno sea seguro para subprocesos.

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

En POSIX.1, errno se define como una variable global externa. Pero esta definición es inaceptable en un entorno multiproceso, porque su uso puede dar lugar a resultados no deterministas. El problema es que dos o más subprocesos pueden encontrar errores y todos provocan que se establezca el mismo error. En estas circunstancias, un hilo podría terminar marcando errno después de que otro hilo ya lo haya actualizado.

Para evitar el no determinismo resultante, POSIX.1c redefine errno como un servicio que puede acceder al número de error por subproceso de la siguiente manera (ISO/IEC 9945:1-1996, §2.4):

Algunas funciones pueden proporcionar el número de error en una variable a la que se accede mediante el símbolo errno. El símbolo errno se define incluyendo el encabezado, como lo especifica el estándar C... Para cada subproceso de un proceso, el valor de errno no se verá afectado por llamadas a funciones o asignaciones a errno por parte de otros subprocesos.

Consulte también http://linux.die.net/man/3/errno

errno es subproceso local; establecerlo en un hilo no afecta su valor en ningún otro hilo.

Charles Salvia avatar Nov 07 '2009 19:11 Charles Salvia


Errno ya no es una variable simple, es algo complejo detrás de escena, específicamente para que sea seguro para subprocesos.

Ver $ 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.

Podemos volver a verificar:

$ cat > test.c
#include <errno.h>
f() { g(errno); }
$ cc -E test.c | grep ^f
f() { g((*__errno_location ())); }
$ 
DigitalRoss avatar Nov 07 '2009 19:11 DigitalRoss

, como se explica en la página de manual de errno y las otras respuestas, errno es una variable local del hilo.

Sin embargo , hay un detalle tonto que podría olvidarse fácilmente. Los programas deben guardar y restaurar el error en cualquier controlador de señal que ejecute una llamada al sistema. Esto se debe a que la señal será manejada por uno de los subprocesos del proceso que podría sobrescribir su valor.

Por lo tanto, los manejadores de señales deben guardar y restaurar errno. Algo como:

void sig_alarm(int signo)
{
 int errno_save;

 errno_save = errno;

 //whatever with a system call

 errno = errno_save;
}
marcmagransdeabril avatar Mar 06 '2013 08:03 marcmagransdeabril

En errno.h, esta variable se declara como extern int errno;

Esto es lo que dice el estándar C:

No es necesario que la macro errnosea el identificador de un objeto. Podría expandirse a un valor l modificable resultante de una llamada a función (por ejemplo, *errno()).

Generalmente, errnoes una macro que llama a una función que devuelve la dirección del número de error para el hilo actual y luego lo desreferencia.

Esto es lo que tengo en Linux, en /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

Al final, genera este tipo de código:

> 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
Bastien Léonard avatar Nov 07 '2009 20:11 Bastien Léonard