2010-09-13 17 views
23

¿Cuáles son las razones por las que un ejecutivo (execl, execlp, etc.) puede fallar? Si realiza una llamada al ejecutivo y ésta regresa, ¿hay alguna mejor práctica que no sea entrar en pánico y llamar a la salida?¿Qué puede hacer que falle el ejecutivo? ¿Qué pasa después?

+2

El pánico no suele ser la mejor práctica. – bta

+3

@bta: pero un buen pánico puede ser notablemente terapéutico, si no catártico. –

+0

Pregunta honesta, y no me refiero a que se tome mal ... ¿Cómo aprendiste a programar Linux? Sea cual sea el mecanismo que utilizó, debería haberle enseñado a mirar las páginas man para preguntas como esta ... –

Respuesta

18

Desde el exec(3) man page:

El execl(), execle(), execlp(), execvp() y execvP() funciones pueden fallar y asignar a errno cualquiera de los errores especificados para las funciones de biblioteca y execve(2)malloc(3).

La función execv() puede fallar y establecer errno para cualquiera de los errores especificados para la función de biblioteca execve(2).

y luego desde el execve(2) man page:

ERRORES

Execve() se producirá un error y volver al proceso de llamada si:

  • [E2BIG] - El número de bytes en el la lista de argumentos del nuevo proceso es más grande que el límite impuesto por el sistema. Este límite se especifica en la variable sysctl(3) MIB KERN_ARGMAX.
  • [EACCES] - Se deniega el permiso de búsqueda para un componente del prefijo de ruta.
  • [EACCES] - El nuevo archivo de proceso no es un archivo común.
  • [EACCES] - El nuevo modo de archivo de proceso deniega el permiso de ejecución.
  • [EACCES] - El nuevo archivo de proceso está en un sistema de archivos montado con la ejecución deshabilitada (MNT_NOEXEC en <sys/mount.h>).
  • [EFAULT] - El nuevo archivo de proceso no es tan largo como lo indican los valores de tamaño en su encabezado.
  • [EFAULT] - Ruta, argv o envp apuntan a una dirección ilegal.
  • [EIO] - Se produjo un error de E/S al leer desde el sistema de archivos.
  • [ELOOP] - Se encontraron demasiados enlaces simbólicos al traducir la ruta de acceso. Esto se toma como indicativo de un enlace simbólico de bucle.
  • [ENAMETOOLONG] - Un componente de una ruta de acceso excedió los {NAME_MAX} caracteres, o una ruta de acceso completa excedió los {PATH_MAX} caracteres.
  • [ENOENT] - El nuevo archivo de proceso no existe.
  • [ENOEXEC] - El nuevo archivo de proceso tiene el permiso de acceso apropiado, pero tiene un formato no reconocido (por ejemplo, un número mágico no válido en su encabezado).
  • [ENOMEM] - El nuevo proceso requiere más memoria virtual que la permitida por el máximo impuesto (getrlimit(2)).
  • [ENOTDIR] - Un componente del prefijo de ruta no es un directorio.
  • [ETXTBSY] - El nuevo archivo de proceso es un archivo de procedimiento puro (texto compartido) que actualmente está abierto para escritura o lectura por algún proceso.

malloc() es mucho menos complicado, y sólo se utiliza ENOMEM. Desde el malloc(3) man page:

Si tiene éxito, calloc(), malloc(), realloc(), reallocf() y valloc() devuelven un puntero a la memoria asignada. Si hay un error, devuelven un puntero NULL y establecen errno en ENOMEM.

+0

Además, consideraré agrupar los ERRNO por algún esquema para que no tenga que manejarlos todos individualmente. También puede ser útil realizar un seguimiento de los códigos que surgen durante las pruebas y la implementación para que sepa si el alcance de su manejo de errores es razonable. –

3

Ya sea que esté simplemente en pánico, podría tomar una decisión basada en el valor de errno.

8

Lo que se hace después de que los exec() devuelve la llamada depende del contexto - lo que se supone que el programa para hacerlo, lo que es el error, y lo que podría ser capaz de hacer para solucionar el problema.

Una fuente de problemas podría ser que haya especificado un nombre de programa simple en lugar de un nombre de ruta; quizás podría intentar de nuevo con execvp(), o convertir el comando en una invocación de sh -c 'what you originally specified'. Si alguno de estos es razonable depende de la aplicación. Si hay problemas de seguridad importantes involucrados, probablemente no intente de nuevo.

Si ha especificado una ruta de acceso y hay un problema con eso (ENOTDIR, ENOENT, EPERM), es posible que no tenga ninguna alternativa razonable, pero puede informar el error de manera significativa.

En los viejos tiempos (hace más de 10 años), algunos sistemas no admitían el '#!' notación shebang, y si no estaba seguro de si estaba ejecutando un ejecutable o un script de shell, lo intentó como un ejecutable y luego lo volvió a intentar como un script de shell. Eso podría funcionar o no si estuvieras ejecutando un script de Perl, pero en esos días, escribiste tus scripts Perl para detectar que estaban siendo ejecutados por un shell y para volver a ejecutar ellos mismos Perl. Afortunadamente, esos días han terminado.

En la medida de lo posible, es importante asegurarse de que el proceso informe el problema para que pueda rastrearse, escribiendo su mensaje en un archivo de registro o simplemente en stderr (o incluso syslog()), para que aquellos que tengan para resolver lo que salió mal, tenemos más información para ayudarlos que el desafortunado informe del usuario final "Intenté X y no funcionó". Es crucial que si nada funciona, el estado de salida no es 0 ya que eso indica éxito. Incluso eso podría ser ignorado, pero usted hizo lo que pudo.

32

El problema con la gestión de la falla exec es que generalmente exec se realiza en un proceso secundario, y desea realizar el control de errores en el proceso principal. Pero no puede simplemente exit(errno) porque (1) no sabe si los códigos de error caben en un código de salida, y (2) no puede distinguir entre exec y los códigos de error de salida del nuevo programa exec.

La mejor solución que conozco es el uso de tubos para comunicar el éxito o el fracaso de exec:

  1. Antes de que se bifurcan, abra una tubería en el proceso padre.
  2. Después de hornear, el padre cierra el extremo de escritura del conducto y lo lee desde el final de lectura.
  3. El elemento secundario cierra el final de lectura y establece el indicador de cierre de ejecución para el final de escritura.
  4. El niño llama a exec.
  5. Si falla la ejecución, el niño vuelve a escribir el código de error en el elemento primario utilizando la tubería y luego sale.
  6. El padre lee eof (una lectura de longitud cero) si el niño realizó con éxito exec, ya que close-on-exec hizo exitosamente exec cerrar el extremo de escritura del conducto. O bien, si exec falló, el padre lee el código de error y puede proceder en consecuencia. De cualquier manera, el padre bloquea hasta que el hijo llame al exec.
  7. El padre cierra el extremo de lectura de la tubería.
+0

Tengo algunos problemas cuando se redirigen las transmisiones estándar. El proceso principal se bloquea cuando se espera que se cierre el final de escritura. Todo funciona si no hay una redirección en curso. – user877329

+0

Normalmente, esto significa que el padre tiene su propio fd para el final de escritura de la tubería debido a que no se cierra después de la bifurcación. –

+0

¿Alguna otra razón? – user877329

1

Exec siempre debe tener éxito. (excepción de conchas, es decir, si el usuario ha introducido una orden falsa)

Si ejecutivo de falla, que indica:

  • un "fallo" con el programa (falta o componente defectuoso, ruta equivocada, mal memoria, ...), o
  • un error grave del sistema (sin memoria, demasiados procesos, fallos de discos, ...)

para cualquier error grave, el enfoque normal es escribir el error mensaje en stderr, luego salga con un código de falla. Casi todas las herramientas estándar hacen esto. Por exec:

execl("bork", "bork", NULL); 
perror("failed: exec"); 
exit(127); 

El shell hace eso, también (más o menos).

Normalmente, si un proceso secundario falla, el padre también ha fallado y debería salir. No importa si el niño falló en el ejecutivo o mientras ejecuta el programa. Si el ejecutivo falló, no importa por qué falló el ejecutivo. Si el proceso hijo falló por algún motivo, el proceso de llamada está en problemas y debe detenerse.

No pierda mucho tiempo tratando de anticipar todas las posibles condiciones de error. No escriba código que intente manejar cada código de error de la mejor manera posible. Simplemente hincharás el código e introducirás muchos errores nuevos. Si tu programa se rompe, o está siendo maltratado, simplemente debería fallar. Si lo obligas a continuar, un problema peor vendrá de eso.

Por ejemplo, si el sistema está agotado y no tiene memoria, no queremos pedalear una y otra vez intentando ejecutar un proceso; solo empeoraría la situación. Si obtenemos un error del sistema de archivos, no queremos seguir corriendo en ese sistema de archivos; podría empeorar la corrupción. Si el programa se instaló incorrectamente, o tiene un error, o tiene daños en la memoria, queremos detenerlo lo antes posible, antes de que ese programa dañado produzca algún daño real (como enviar un informe dañado a un cliente, descartar una base de datos,. ..).

Una posible alternativa: un proceso fallido puede requerir ayuda, pausarse (SIGSTOP) y luego volver a intentar la operación si se le indica que continúe. Esto podría ser útil cuando el sistema no tiene memoria, o los discos están llenos, o quizás incluso si hay un error en el programa. Pocas operaciones son tan caras e importantes que valdría la pena.

Si está haciendo un programa de interfaz gráfica de usuario interactivo, intente hacerlo como un envoltorio delgado sobre las herramientas de línea de comandos reutilizables (que se terminan si algo sale mal). Se debe poder acceder a todas las funciones de su programa a través de la GUI, a través de la línea de comandos y como una llamada a la función. Escribe tus funciones.Escriba algunas herramientas para crear una línea de mandatos y envoltorios de GUI para cualquier función. Use subprocesos también.

Si está haciendo un sistema verdaderamente crítico, como un controlador para una estación de energía nuclear, o un programa para predecir tsunamis, entonces ¿qué está haciendo leyendo mi estúpido consejo? Los sistemas críticos no deben depender completamente de computadoras o software. Es necesario que haya una 'anulación manual', con alguien que la maneje. Especialmente, no intente construir un sistema crítico en MS Windows, que es como construir castillos de arena bajo el agua.

Cuestiones relacionadas