Estoy desarrollando un shell simple para una tarea. Leo un comando ingresado por el usuario, lo tokenizo, fork() , luego, en el proceso secundario, uso execvp() para ejecutar el comando en segundo plano.
El problema es que necesito implementar una función de historial que registre comandos válidos. La única forma que conozco de verificar si la cadena que ingresa el usuario es un comando válido es verificar si execvp() devuelve -1. Esta es una buena manera de comprobar si hay un comando no válido, pero dado que la llamada a execvp() ocurre en el proceso secundario y la estructura de datos que uso para mi historial se copia al elemento secundario en fork() en lugar de compartirse, no puedo t actualice el historial utilizando los resultados de execvp() dentro del elemento secundario (dado que la estructura del historial es una copia, cualquier cambio que realice no se reflejará en la copia de la estructura del elemento principal).
¿Hay alguna forma de verificar si execvp() devolvería -1 sin llamarlo (es decir, antes o después de la bifurcación)? Si pudiera encontrar una manera de hacerlo, podría verificar en los procesos principales si execvp() tendrá éxito o no y usaría esa información para actualizar la estructura de datos de mi historial correctamente.
Lo que está solicitando es una llamada al sistema que le permitiría implementar la clásica condición de carrera de verificación antes de hacer.
En ese error, un programa verifica si una acción es posible y luego realiza la acción, dejando abierta la posibilidad de que ocurra algún evento externo justo después de la verificación que hace que la acción sea ilegal.
Entonces la acción falla, aunque el programa verificó que era posible. Esto a menudo resulta en caos.
Debe evitar este antipatrón, y la API del sistema debería ayudarlo al no tentarlo con llamadas al sistema que solo usaría para meterse en problemas. En este caso, el sistema hace lo correcto; no existe tal API.
El proceso padre finalmente debe recuperar el estado de salida del hijo. Ese es el momento en el que necesitas actualizar (o no) el historial. Si un execvp fallido hace que el hijo salga() con un código de estado de falla, el padre notará la falla y podrá reaccionar al no agregar la línea de comando al historial.
Para recuperar el código de estado del proceso hijo, el padre llamará a wait o waitpid . Para la ejecución sincrónica, es probable que el padre lo haga de inmediato; para la ejecución asincrónica, el padre lo hará cuando reciba una señal SIGCHLD . Pero es imperativo que el padre haga esto, para evitar procesos zombies.
En el caso de ejecución asíncrona, no es posible utilizar esta estrategia para evitar colocar comandos no válidos en el historial, ya que los comandos asíncronos deben registrarse en el historial cuando se inician. Por una razón similar, los shells de Posix también cuentan como exitosa la ejecución asíncrona de un comando, incluso si el comando no es válido.
Si bien este ejercicio sin duda tiene valor pedagógico (como espero que demuestre esta respuesta), en realidad es una forma terrible de hacer historia de shell. Si bien los usuarios de shell ocasionalmente usan el historial para recuperar y volver a ejecutar comandos correctos, la función de historial es mucho más útil para recuperar y editar un comando fallido. Es muy molesto no poder hacer correcciones desde una función de historial. (Muchas aplicaciones de Android exhiben precisamente este molesto defecto con el historial de búsqueda: después de una búsqueda que le da resultados no deseados, puede recuperar la búsqueda incorrecta y volver a ejecutarla, pero no modificarla. Me alegra decir que las cosas han mejorado desde mi primera Androide.)