¿Cómo comprobar el valor de errno?

8 minutos de lectura

avatar de usuario
hana

Estoy usando una llamada al sistema y, en caso de que falle, necesito hacer cosas diferentes para diferentes errnos.

Necesito escribir un código que se vea así:

int res;
res = systemCall();
if (res == -1)
{
    if (errno == ENOMSG)
    {
        doSomething();
    }
    else
    {
        doSomethingElse();
    }
}

perror no ayuda, porque solo imprime el valor.

En cuanto a strerro, si es lo que necesito, no estoy seguro de cómo usarlo, porque aquí dice que la cadena real no es la misma que el error. Cita de la página del manual: “(Por ejemplo, si errnum es EINVAL, la descripción devuelta será “Argumento no válido”)”.

Estoy usando Linux. Llamadas al sistema: msgsend y msgrcv (https://linux.die.net/man/2/msgrcv). No estoy seguro de qué bibliotecas C está preguntando.

Veo que no me expliqué bien.

¿Es válida la afirmación if (errno == ENOMSG)? ¿Existe tal variable errno? Básicamente mi pregunta es: ¿Qué debe ir en el if declaración para probar el errno?

  • ¿En qué sistema operativo? ¿Qué llamada del sistema exacta? ¿Qué biblioteca C? Por favor edita tu pregunta para mejorarla

    – Basile Starynkevitch

    02/09/2017 a las 11:51

  • Cada llamada al sistema tiene una lista de posibles valores de errno (vea su man página), esto le permitirá saber contra qué debe realizar la prueba.

    – mística

    2 de septiembre de 2017 a las 11:58

  • Lo que has escrito está bien. Recuerde, no hay conjuntos de funciones de biblioteca errno a cero, y no debe probarlo a menos que una función indique un error y esté documentada para establecer errno. Tenga en cuenta que muchas funciones de pthreads no se configuran errno, por ejemplo; en su lugar, devuelven el número de error. Además, las funciones pueden establecer errno a un valor distinto de cero incluso si tiene éxito. Por ejemplo, en Solaris, si la salida estándar no es un terminal, errno se puede establecer en ENOTTY aunque el printf() la llamada es exitosa.

    –Jonathan Leffler

    02/09/2017 a las 12:30


avatar de usuario
steve cumbre

Cómo comprobar el valor de errno:

  1. Necesitaras #include <errno.h>.
  2. Sí, definitivamente puedes decir cosas como if(errno == ENOENT) { ... }y esa es la forma común y recomendada de hacerlo.
  3. En general, haz no usar errno para determinar que se ha producido un error. Compruebe el valor de retorno de la función y, si el valor de retorno indica un error, compruebe errno para ver cual fue el error. (Más sobre esto a continuación).
  4. errno parece una variable, pero en realidad no lo es. Esto no te concierne mientras digas cosas como if(errno == ENOENT) { ... }. Pero probablemente no deberías intentar hacer algo como int errno_ptr = &errno;.
  5. Puedes usar funciones como perror() y strerror() para obtener cadenas de error legibles por humanos correspondientes a errno valores. Pero, sí, las cadenas que obtienes son generalmente cosas como “No existe tal archivo o directorio”. No hay una buena manera que yo sepa para convertir el valor errno ENOENT a la cuerda "ENOENT". (Anexo: consulte esta pregunta para obtener algunas respuestas).

Para decir un poco más sobre el #3. A veces es tentador decir algo como

errno = 0;
printf("Hello, world!\n");
if(errno != 0) {
    fprintf(stderr, "printf failed!\n");
}

Pero no hagas eso. en lugar de hacer

int retval = printf("Hello, world!\n");
if(retval < 0) {
    fprintf(stderr, "printf failed!\n");
}

La razón es que, en algún momento del proceso de hacer su trabajo, printf podría haber hecho algo que resultó en un error, algo que estableció errnopero printf podría haberse recuperado de ese error y continuar hasta completarse con éxito.

Hay muy pocas funciones de biblioteca que están garantizadas no tocar errno si no hubo un error (creo que un ejemplo podría ser atoi), pero en general, esto es algo con lo que hay que tener cuidado.

Para decir un poco más sobre el #4. errno parece una variable, y más específicamente, parece una variable global. Pero, por supuesto, las variables globales son malas. Pero errno ha existido desde siempre; hay decenas de millones de líneas de código que lo utilizan; sigue siendo básicamente bastante conveniente; es demasiado tarde para “arreglarlo”. Entonces, en cambio, si mira detrás de la cortina, encontrará que la mayoría de las implementaciones hacen algo como

extern int __errno_pointer;
#define errno (*__errno_pointer)

o

extern int *__errno_pointer_function();
#define errno (*__errno_function())

De esta manera, pueden organizar errno para que funcione razonablemente bien incluso en, digamos, código multihilo.

avatar de usuario
Basile Starynkevitch

Supongo que estás usando Linux, y supongo que no directamente use la llamada al sistema, pero algunos de los contenedores (simples) (de su biblioteca C) enumerados en llamadas al sistema(2). Tenga en cuenta que algunas llamadas extrañas al sistema no están empaquetadas por la biblioteca C (un ejemplo bien conocido de llamada al sistema sin empaquetar sería sigreturn(2) que probablemente nunca deberías usar). Muy a menudo, la biblioteca C es GNU glibcpero podría ser Musl-libc etc. Tenga en cuenta también que las llamadas al sistema sin procesar del núcleo tienen diferentes convenciones de llamadas que la función C ordinaria (por lo que en la práctica se requiere un envoltorio libc, y está a cargo de tratar con errno). Note también que error(3) es generalmente una macro (casi se comporta como una variable).

Él msgrcv(2) documentos de página man que errno podría ser uno de E2BIG, EACCES , EFAULTENOMSG, ENOSYS … (consulte esa página del manual para obtener la lista de todos los posibles errores).

Entonces codificarías algo como

ssize_t siz = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
if (siz<0) { // msgrcv failed and has set errno
  if (errno == ENOMSG) 
    dosomething();
  else if (errno == EAGAIN) 
    dosomethingelse();
  /// etc
  else {  
     syslog(LOG_DAEMON|LOG_ERR, "msgrcv failure with %s\n",
            strerror(errno)); 
     exit(EXIT_FAILURE); 
  };
};

es la declaración if (errno == ENOMSG) …. ¿válido?

Sí, lo es; quieres probar errno solo después de alguna falla en la llamada al sistema (por ejemplo, cuando siz<0).

¿Existe tal variable? errno?

Ya no. Por favor lea cuidadosamente error(3) documentación. no debes declarar extern int errno; (esto era posible en la década de 1980, no en 21S t siglo) pero deberías siempre #include <errno.h> y use errno como si fuera una variable, pero casi siempre es alguna macro (cuya definición aparece en /usr/include/bits/errno.h que está incluido por /usr/include/errno.h).

Por cierto, las instalaciones de estilo SysV tienden a volverse obsoletas y no siempre están disponibles. Recomiendo usar las instalaciones de colas de mensajes POSIX, lea mq_resumen(7).

Es posible que desee leer el descargable gratuito Programación avanzada de Linux (un libro antiguo; puede comprar algo mejor y más nuevo) y/o todas las páginas man accesibles desde introducción(2) & llamadas al sistema(2) & introducción(3).

  • El enlace a la descarga de Programación avanzada de Linux puede estar roto o desactualizado. No parece estar funcionando para mí.

    -Matthew Plemmons

    25 de enero de 2018 a las 5:59

  • Corregido, con algún enlace aleatorio funcionando hoy. En realidad, mejor lea un libro más nuevo si puede permitírselo.

    – Basile Starynkevitch

    25 de enero de 2018 a las 6:03

  • ¡Gracias por el aviso! Buscaré algo más nuevo. Estaría encantado de escuchar sugerencias si algo viene a la mente.

    -Matthew Plemmons

    25 de enero de 2018 a las 6:25

  • Di un enlace a una copia de ALP que hoy se puede descargar. Se le permite redistribuirlo (pero IANAL)

    – Basile Starynkevitch

    25 de enero de 2018 a las 7:49


  • Recomiendo encarecidamente la interfaz de programación de Linux (man7.org/tlpi). También sugiera mirar warn(3) y err(3). Muy a menudo, cuando ocurre un error, todo lo que desea hacer es enviar un mensaje sobre el error, con algo de contexto (por ejemplo, un nombre de archivo) y posiblemente simplemente salir.

    – James K. Lowden

    18 dic 2021 a las 18:59

avatar de usuario
0___________

Incluir errno.h

Algunos ejemplos:

// Error codes
#define EPERM        1  /* Operation not permitted */
#define ENOENT       2  /* No such file or directory */
#define ESRCH        3  /* No such process */
#define EINTR        4  /* Interrupted system call */
#define EIO          5  /* I/O error */
#define ENXIO        6  /* No such device or address */
#define E2BIG        7  /* Argument list too long */
#define ENOEXEC      8  /* Exec format error */
#define EBADF        9  /* Bad file number */
#define ECHILD      10  /* No child processes */
#define EAGAIN      11  /* Try again */
#define ENOMEM      12  /* Out of memory */
#define EACCES      13  /* Permission denied */
#define EFAULT      14  /* Bad address */
#define ENOTBLK     15  /* Block device required */
#define EBUSY       16  /* Device or resource busy */
#define EEXIST      17  /* File exists */
#define EXDEV       18  /* Cross-device link */
#define ENODEV      19  /* No such device */
#define ENOTDIR     20  /* Not a directory */
#define EISDIR      21  /* Is a directory */
#define EINVAL      22  /* Invalid argument */
#define ENFILE      23  /* File table overflow */
#define EMFILE      24  /* Too many open files */
#define ENOTTY      25  /* Not a typewriter */
#define ETXTBSY     26  /* Text file busy */
#define EFBIG       27  /* File too large */
#define ENOSPC      28  /* No space left on device */
#define ESPIPE      29  /* Illegal seek */
#define EROFS       30  /* Read-only file system */
#define EMLINK      31  /* Too many links */
#define EPIPE       32  /* Broken pipe */
#define EDOM        33  /* Math argument out of domain of func */
#define ERANGE      34  /* Math result not representable */

Su implementación puede tener más errno incluidos, como por ejemplo /usr/include/asm-generic/errno.h.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad