¿Errno es seguro para subprocesos?
En errno.h
, esta variable se declara así extern int errno;
, mi pregunta es: ¿es seguro verificar errno
el 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.
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.
Sí
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 ())); }
$
sí , 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;
}
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
errno
sea el identificador de un objeto. Podría expandirse a un valor l modificable resultante de una llamada a función (por ejemplo,*errno()
).
Generalmente, errno
es 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