Práctica 1. Presentación de la práctica continua: Implementación de un intérprete de comandos. Grupos Fecha de Realización Fecha de Entrega/Evaluación Lunes 19 y 26 de Febrero y 5 de Marzo 12 de Marzo Martes 20 y 27 de Febrero y 6 de Marzo. 13 de Marzo Miércoles 21 y 28 de Marzo y 7 de Marzo. 14 de Marzo Jueves 22 de Marzo y 1 y 8 de Marzo. 15 de Marzo Viernes 23 de Marzo y 2 y 9 de Marzo. 16 de Marzo Planteamiento Esta primera práctica tiene como cometido familiarizar al alumno con el manejo de múltiples procesos y las funciones de entrada/salida. Para ello realizaremos la primera de una serie de prácticas con el objetivo de crear de forma sucesiva un pequeño intérprete de comandos apto para LINUX. El intérprete de comandos no es más que un programa encargado de interaccionar con el usuario y servir de enlace entre éste y el sistema operativo. De esta manera, cuando escribimos mediante el teclado cualquier instrucción, el intérprete de comandos se encarga, en el caso más sencillo, de leerla del teclado, escribirla en pantalla y llamar al sistema operativo para que ejecute la instrucción que se escribió. El sistema operativo LINUX incluye de serie varios tipos de intérprete de comandos pero nosotros vamos a implementar otro que consistirá en un intérprete y gestor de comandos de acuerdo a los requisitos que veremos mas adelante. El interprete de comandos Nuestro interprete de comandos, msh desde ahora, tendrá dos modos de funcionamiento, en el primero de ellos se arrancará sin parámetros en cuyo caso leerá de teclado las instrucciones a ejecutar. En el segundo caso se le pasará un nombre de fichero de script como parámetro siendo de este fichero de donde leerá las instrucciones a ejecutar 1. Las instrucciones que se deben ejecutar, tanto si se lee de fichero como si se hace de teclado, estarán escritas en el formato: comando [arg1... argn] [&][;][comando arg1... argn & ;]... Donde los caracteres & y ; tienen el significado, normal en cualquier interprete de comandos, de ejecutar en background y empezar un nuevo comando. El comando podrá ser cualquier ejecutable. Por ejemplo si el usuario escribe: ls la & ; cd /home el msh deberá ejecutar en background el comando ls la y a continuación el comando cd /home. el msh deberá, al iniciarse, leer un fichero de configuración, por ejemplo.msprofile, donde estarán escritas las variables de entorno y sus valores. Estas variables de entorno tendrán una función similar a las variables de entorno del sistema, sirviendo para almacenar valores que podrán ser accedidos o modificados por cualquier proceso ejecutado desde el msh. En este fichero también podrá haber comandos que se 1 Véase esquema del apéndice A. 2007. Escuela Politécnica. Universidad Autónoma de Madrid. (1/8)
ejecutaran nada mas arrancarse el msh. El fichero se puede considerar dividido en dos partes. La primera parte contendrá sólo definiciones de variables mientras que la segunda puede contener comandos o definiciones de variables. La primera línea en blanco actuará como separador entre las dos partes. La primera parte de este fichero será sobrescrita al finalizar la shell como se explica mas adelante. El formato de los comandos será el mismo que el anteriormente descrito mientras que las variables estarán definidas de la forma: Nombre_variable=valor 2 Donde valor podrá ser una cadena o un array de cadenas dependiendo de la sintaxis que se explica mas adelante. En este fichero habrá dos variables de entorno MAX_NUM_PROC y MAX_NUM_VAR que indicarán el número máximo de procesos que se podrán ejecutar simultáneamente y el número máximo de variables que se podrán definir simultáneamente. En caso de que se intente exceder alguno de esos límites el msh mostrará el mensaje de error pertinente. También existirá la variable de entorno PROMPT que tendrá la misma función que la variable PS1 del sistema por lo que una vez iniciado el msh el prompt del sistema deberá cambiar para indicar que estamos trabajando con ella. Además de los comandos internos típicos de un intérprete de comandos, como por ejemplo cd, nuestro msh deberá implementar los siguientes: exit msps evar nombre_variable avar nombre_variable valor o Nombre_variable=valor bvar nombre_variable Se usará para salir del msh. Se deberán cerrar todos los ficheros abiertos y liberar la memoria usada antes de salir. Mostrará todos los procesos que se están ejecutando en ese momento bajo el msh dando la siguiente información de ellos: pid, ppid, tiempo de ejecución en formato HHMMSS y línea de comando con la que fue invocado. Se mostrará un proceso por línea con los campos separados por espacios o tabuladores. Muestra por pantalla: 0 si la variable no existe N si existe, donde N es el número de elementos que tiene el array o 1 si es una cadena. Asigna a la variable nombre_variable el valor especificado. Si el valor es de la manera {campo1, campo2, campo3, campo4,, campon} la variable se considerará un array con N elementos. Si la variable no existe se creará. Borra la variable nombre_variable. Si no existe dicha variable no hace nada. 2 Obsérvese que no hay espacios entre el nombre de la variable, el signo igual y el valor de la variable. 2007. Escuela Politécnica. Universidad Autónoma de Madrid. (2/8)
rvar nombre_variable {1, 2,,N} o $Nombre_variable{1, 2,, N} Entorno Muestra el valor de la variable nombre_variable. Si la variable es un array se mostrará completo, separando los campos por comas, a menos que se indique una lista de campos a mostrar entre llaves. Si la variable no existe no mostrará nada. Muestra todas las variables definidas junto con sus valores en el formato: nombre_variable=valor Donde valor puede ser un array separando los campos por comas. Consideraciones de diseño Se definirán, al menos, dos áreas memoria donde se guardarán la tabla de procesos y las variables de entorno. En la tabla de procesos deberá guardarse la información sobre los procesos que sea necesaria consultar por otros programas lanzados desde msh, como, por ejemplo, msps. En la zona de variables de entorno se guardará toda la información sobre las variables de entorno propias del msh. Ahí se guardarán, por ejemplo, el nombre y el valor de cada una de las variables definidas para que sea consultado por programas como rvar. Los procesos que se lancen desde el msh deberán lanzarse usando fork() o execve() y no mediante llamadas directas al sistema. Si desde un msh se lanza un nuevo msh este heredará los valores de las variables de entorno tal y como estén en ese momento pero no la tabla de procesos. Esto es muy importante pues quiere decir que la memoria de las variables no debe ser creada de nuevo. El programa se ha de modularizar encapsulando las llamadas al sistema operativo. Esta es una forma de facilitar la portabilidad si se hace una migración del programa a otros sistemas operativos. El msh deberá avisar al usuario cuando haya finalizado un proceso que estaba ejecutándose en background mostrando un mensaje por pantalla. Cuando el msh finalice mediante el comando exit deberá comprobar que no quedan procesos ejecutándose antes de salirse por lo que deberá informar de ello al usuario. Ayudas Se recomienda a los alumnos repasar las prácticas de informática general de primer curso, la práctica 0 publicada en la página web de la asignatura y leer la ayuda del sistema mediante el comando info libc. Además de las funciones para el manejo de ficheros (ver práctica 0), en esta práctica será de especial importancia la función fork(), llamada del sistema que sirve para crear un nuevo proceso hijo como copia del proceso actual, y wait(), que sirve (entre otras cosas) para esperar a que un hijo termine. Es de fundamental importancia comprender el 2007. Escuela Politécnica. Universidad Autónoma de Madrid. (3/8)
funcionamiento de estas llamadas, para lo cual se recomienda emplear la ayuda man de UNIX. Para buscar identificadores de proceso puede utilizarse la familia de funciones de getpid(). Tanto estas funciones como fork() y wait() están en la librería unistd.h en el caso de LINUX. Por último, resulta muy recomendable un tratamiento consistente de los errores utilizando el entero global errno. En el fichero de cabeceras errno.h se definen los códigos de error devueltos. Recuérdese que si se usa errno debe hacerse de forma sistemática, es decir que sólo podrá interpretarse la información contenida en errno cuando la llamada de sistema devuelve -1. También pueden usarse las variables globales sys_errlist y sys_nerr para generar mensajes de error de forma consistente, teniendo siempre en cuenta que errno debe constrastarse frente a sys_nerr antes de indexar sys_errlist para sacar el mensaje de error correspondiente. Se recomienda construir las funciones relacionadas con el tratamiento de errores de tal forma que resulte sencillo reutilizarlas en prácticas posteriores. No obstante, el único tratamiento de errores que se exige en esta práctica es el relacionado con la creación y manejo de procesos y ficheros, sin ser estrictamente necesario realizar un tratamiento exhaustivo de los errores generados durante el proceso de cálculo ni los debidos al formato del fichero de entrada, etc. 2007. Escuela Politécnica. Universidad Autónoma de Madrid. (4/8)
Trabajo a realizar Puntuación - En esta primera práctica se pide al usuario que implemente un intérprete de comandos, msh, además de los comandos internos necesarios para el control de variables de entorno y procesos. Todo ello de acuerdo con las especificaciones explicadas anteriormente. - Diseñar la practica en su totalidad, utilizando, además de las nuevas instrucciones comentadas anteriormente, todo lo conocido hasta ahora. Dejar el programa lo suficientemente abierto como para ser modificado con posterioridad en la siguiente práctica. - Diseñar una memoria completa donde, además de lo especificado en las normas de la asignatura, debe de hacerse especial hincapié en los siguientes puntos: - Estrategia seguida en el diseño. Análisis. - Descripción exhaustiva de las estructuras de datos utilizadas, sobre todo para el control de la memoria usada. - Diseño descendente y modularización. - Instrucciones utilizadas en el control de acceso a los recursos del sistema. - Pruebas realizadas y resultados obtenidos. - Posibles ampliaciones y mejoras. Se recuerda que el código representa el 60% de la nota mientras que la documentación entrega es el restante 40%. Durante la realización de la práctica se entregará a los alumnos una herramienta para testar su programa. Dicha herramienta proporcionará también una estimación de la nota que podrá obtener gracias al código si la entregasen en ese momento 3. El correcto funcionamiento de los requisitos anteriores es imprescindible pero no suficiente para obtener el aprobado ya que se tendrán en cuenta otras consideraciones como el estilo de programación, modularización, errores en el código, etc. Para obtener más puntuación de base, y asegurarse el aprobado, se deben implementar, en el orden que deseen, algunas de las siguientes funcionalidades: Interpretación en la línea de comando de los caracteres >,<'. Interpretación en la línea de comandos del carácter ' '. Para ello recomendamos, de nuevo, leer la ayuda al respecto que hay en info libc. Implementar el comando export para notificar a otros msh la modificación de las variables de entorno. La comunicación entre los diferentes msh se haría mediante pipes. Cada vez que se lance un msh se deberá abrir un pipe entre él y su padre para comunicarse por este canal los cambios en las variables de entorno. Al finalizar el msh se deberá guardar el valor de las variables que tenga en ese momento definidas en memoria dentro de la primera parte del fichero de configuración (.msprofile). Esto es lo que se conoce como guardar el estado del intérprete de comandos 4. 3 4 Salvo error de la aplicación. Prestad atención a no borrar los comandos de inicio que pudieran existir en el fichero. 2007. Escuela Politécnica. Universidad Autónoma de Madrid. (5/8)
Implementación de bucles for con la sintaxis de la familia sh: for variable in [lista] do comandos... done Colocar realmente en el entorno las variables definidas en msh para poder ser usadas por comandos no internos de la shell. La sintaxis alternativa del $ en la evaluación de variables se implementará como en cualquier otro interprete de comandos. Se substituirá $Nombre_variable por su valor y la línea así resultante de ejecutará. Así, por ejemplo, si la variable var vale hola el comando 'ls $var' se transformará en 'ls hola' y entonces se ejecutará. En el caso de que la variable no haya sido definida se substituirá por la cadena vacía. 2007. Escuela Politécnica. Universidad Autónoma de Madrid. (6/8)
Apéndice A Esquema de funcionamiento de nuestro interprete de comandos, msh Leer configuración Crear recursos Leer comando Ejecutar comando El comando debe ejecutarse en background No Esperar 1 la finalización del comando Si Esperar 2 la finalización de los comandos que se ejecutan en background Actualizar recursos. 1 Espera bloqueada. 2 Espera sin bloqueo. 2007. Escuela Politécnica. Universidad Autónoma de Madrid. (7/8)