Procesos,Hilos y Recursos: Introdución: Procesos/Hilos: Máquina abstracta: es una simulación de una máquina física real. El sistema operativo asigna a cada programa una máquina abstracta sobre la cuál ejecutarse, es decir, simula varias máquinas abstractas al mismo tiempo. El programa binario se carga en un proceso de la máquina abstracta. Para tomar nota del progreso del proceso la abstracción del proceso reúne los recursos que el programa necesitará al ejecutarse e incluye un motor de ejecución. El proceso es el entorno computacional que incluye el programa, los datos , los archivos y demás recursos. También incluye una abstracción del SO que llamaremos un motor de ejecución. El motor de ejecución representa la parte del proceso que utiliza el SO para implementar la multiprogramación. Éste incluye las estructuras de datos internas del SO utilizadas para representar el estado actual de ejecución del proceso en la máquina abstracta y una copia de la pila de ejecución del proceso.(pila contiene los parámetros, variables locales ...de los procedimientos ) Proceso clásico representa un programa en ejecución sobre un computador de von Neumann. A partir de los 90 comenzaron a surgir nuevas máquinas abstractas (Mach C,OSF DCE y Windows) que dan lugar al proceso moderno y el hilo. Proceso moderno equivale al entorno computacional (Ejm:estudio musical) Hilo equivale ejecución del código dentro del entorno(Ejm:como un músico utiliza ese estudio para hacer música) Procesos Clásico: Cada proceso tiene un estudio privado ó en un proceso moderno sólo hay un hilo ejecutándose en él. En los procesos modernos varios músicos pueden compartir un estudio para trabajar juntos en una pieza musical. En la computación multihilo dos hilos utilizan el mismo programa y los mismos datos globales, pero ejecutan el programa a su propio ritmo. Y como pueden llamar a diferentes procedimientos en momentos distintos , cada hilo tendrá su propia pila. Multihilo significa que si el programa fuera ejecutado en un sistema con multiprogramación, dos o más hilos podrían estar compartiendo el procesador por multiplexado de tiempo con otro, incluso aunque estuvieran utilizando el mismo programa y los mismos datos. Además, si tal cómputo multihilo se ejecutara en un computador con más de un procesador , sería posible que dos o más hilos se ejecutasen en paralelo. Recursos: Cuando se ejecuta un proceso , necesita de algunos recursos del computador, al menos procesador y memoria. También necesita almacenar/recuperar datos en los dispositivos de E/S, visualizar información por pantalla etc... todos estos son recursos. El SO es el responsable de gestionar dichos recursos: -Cuando un proceso/hilo está en ejecución, deberá solicitar un recurso del SO antes de utilizarlo. -Una vez que el hilo ha solicitado un recurso, suspende su funcionamiento hasta que se le asigna el recurso. Un proceso solicita implícitamente el procesador y memoria suficiente para ejecutar el programa, y explícitamente la mayoría de los demás recursos(acceso a ficheros, etc...) Una vez que un hilo obtiene un recurso ,por ejemplo un archivo con open(), todos los demás hilos que se ejecutan en ese proceso también podrán usar dicho recurso. Cuando todos los hilos en un proceso han acabado de utilizar el recurso , entonces uno de los hilos debe liberar el recurso de vuelta al S.O, en caso de un archivo se libera con la llamada a close(). En unix , todos los dispositivos y conductos (recursos) se tratan como si de un archivo se tratara, por lo que todo dispositivo implementa en su manejador (driver) las operaciones de open,close,read,write, seek ... Procesos e hilos Un cómputo es la combinación de un proceso/hilo y una colección de recursos. El componente proceso clásico se forma a partir de : Estado Archivos Pila Motor de Ejecución Programa binario Datos Otros Recursos Proceso Un programa binario (ó código objeto) a ejecutar. Los Datos con los ejecutará el programa(obtenidos de un archivo o interactivamente del usuario del proceso) Los Recursos necesarios para la ejecución, incluyendo archivos, que contienen datos con los que operará el programa. El proceso es una marco de trabajo de la máquina abstracta en el cual puede tener lugar la computación, y el motor de ejecución es el elemento activo de la misma. El componente proceso moderno se forma a partir de : Estado Archivos Pila Motores de Ejecución Programa binario Datos Proceso Otros Recursos En los Sistemas Operativos modernos (Windows,Mac y Linux 2.2 en adelante) los procesos pueden contener múltiples motores de ejecución. Cada componente motor de ejecución se denomina hilo (ó proceso ligero). Donde la parte hilo se forma a partir de: Datos privados del hilo, pila del hilo. El estado del hilo, que es una estructura de datos del SO que almacena todas las propiedades que son únicas para el hilo. Por ejemplo, el estado incluye la dirección de la siguiente instrucción a ejecutar , los datos sobre si el hilo está o no bloqueado esperando un recurso, información sobre el recurso por el que el hilo está bloqueado esperando, y otras. En un cómputo multihilo en el cual los diferentes hilos están ejecutándose en un proceso. Utilizan el mismo programa, datos globales del proceso, archivos y otros recursos, pero cada uno tiene sus propios datos locales y estado. El motivo de evolucionar del modelo clásico al moderno fue crear una abstracción simple del SO donde pudieran existir múltiples entidades ejecutando el mismo programa y utilizando los mismos archivos y dispositivos. Creación de Procesos Clásicos:Modelo UNIX Estado Archivos Segmento de Pila Segmento de Texto Segmento de Datos Motor de Ejecución Otros Recursos Proceso El comportamiento de un proceso UNIX se define por su segmento de texto, segmento de datos y segmento de pila. El segmento de texto contiene las instrucciones binarias compiladas, el segmento de datos contiene las variables estáticas y el segmento pila alberga la pila de ejecución utilizada para almacenar las variables temporales. Cuando se crea un proceso, el SO crea una instancia de una estructura de datos denominada descriptor de proceso para guardar todos los detalles sobre la gestión del proceso. Un proceso tiene un único identificador de proceso, un PID, que es una referencia a la estructura de datos del SO con la descripción del proceso. En UNIX, el PID en realidad es un índice entero a una tabla del SO de descriptores de procesos. Cuando un proceso referencia a otro en una llamada al sistema, proporciona el PID del proceso objetivo. La orden UNIX para crear un proceso nuevo es la llamada al sistema fork().Cuando llamamos a fork, el kernel realiza las siguientes operaciones: Busca una entrada libre en la tabla de procesos y la reserva para el proceso hijo Asigna un identificador de proceso para el proceso hijo, es invariante mientras viva, es la clave para controlarlo. Se crea un nuevo proceso hijo con sus propias copias del texto del programa,datos y segmentos de pila del padre, incluyendo UIDs y GIDs reales y efectivos, archivos abiertos y segmentos de memoria compartida (no hereda las alarmas programadas por alarm, ni las señales pendientes) Retorna al proceso padre el PID del proceso hijo, y al proceso hijo le devuelve el valor de 0. Entonces el padre puede utilizar el PID como una referencia al proceso hijo (en las posteriores interacciones con el SO) Los procesos padre e hijo se ejecutan concurrentemente, pero poseen espacios de direcciones separados. Esto significa que incluso aunque tengan acceso a la misma información, cuando se crea el hijo, el padre y el hijo referencian sus propias copias de información. No se comparten ninguna parte del espacio de direcciones de ambos procesos. En concreto, el padre y el hijo no se pueden comunicarse referenciando las variables almacenadas en las mismas direcciones de sus respectivos espacios de direcciones. En UNIX, la única cosa que pueden referenciar en común dos procesos son los archivos abiertos (Comunicación interprocesos) Una vez que se ha creado el hijo, ambos procesos padre e hijo están listos para utilizar el procesador; esto es, ambos tendrán su propia máquina abstracta. En un computador con un procesador, sólo un proceso puede utilizar el procesador al tiempo. El programador no puede suponer cuál de los dos procesos padre e hijo será el siguiente proceso a ejecutarse una vez que se completa la llamadafork. El SO puede elegir cualquiera, e incluso a otro proceso, para ser el siguiente en utilizar el procesador. En UNIX, el proceso hijo comienza ejecutando su copia del programa exactamente en el mismo punto que el proceso padre. Esto es, en el fragmento de código retpid=fork(); printf(“Mi PID es %d\n”, retpid); ...; el padre ejecuta la llamada fork(), y después la sentencia printf. Y la primera sentencia que ejecuta el hijo es también la sentencia printf. Según se mencionó antes, en el proceso padre el valor de retpid será el PID del proceso hijo, pero el valor de retpid del hijo será 0. Si queremos que el hijo ejecute un código distinto al padre: ret=fork() if (ret==-1) {printf(“ERROR”);} else if (ret==0) {printf(“HIJO”);} else printf(“PADRE”) Existe otra llamada al sistema para invocar dinámicamente al cargador y redefinir las áreas texto, datos y pila: familia de funciones exec. Los sistemas UNIX proporcionan distintas formas de llamada al sistema exec. Aquí va una de ellas: int execve (char *path , char *argv[],char *envp[]); Esta llamada produce el reemplazo del texto, datos y pila actualmente en ejecución por el programa binario almacenado en el archivo cuyo camino se da en path. Después de que haya completado la llamada execve, el programa que la invocó ya no estará cargado. De hecho, el SO no retorna de la llamada execve. Tras haber cargado el nuevo programa, la pila se limpia, y las variables se inicializan, por lo que el proceso comienza a ejecutar en el punto de entrada principal del nuevo programa. Al nuevo programa se le pasa la lista de argumentos, argv, y el proceso utiliza un nuevo conjunto de variables de entorno,envp. UNIX también proporciona una llamada al sistema, wait (y una variante suya utilizada con frecuencia, waitpid), para posibilitar que un proceso padre pueda detectar que un proceso hijo ha terminado. Los detalles del estado de la terminación del hijo pueden ser o bien retornados al padre vía un parámetro pasado a wait, o bien ignorados por el padre. La variante waitpid permite que el padre espere por la terminación de un proceso hijo concreto (basándose en su PID), mientras que wait no distingue entre los procesos hijo. Cuando un proceso termina, se liberan sus recursos, incluyendo el descriptor del proceso SO. El SO señala al padre que el hijo ha terminado, pero no liberará las estructuras de datos del SO, que contienen el estado completo del hijo hasta que el padre haya recibido la señal. El padre ejecuta la llamada wait para reconocer la terminación de un proceso hijo, produciendo que el SO libere las estructuras de datos relevantes. #include <stdio.h> /*man printf,scanf*/ #include <stdlib.h> /*man exit*/ #include <unistd.h> /*man fork,exec */ #include <sys/types.h> /*man 2 wait */ #include <sys/wait.h> /*man 2 wait */ int main() { int pid,status; pid=fork(); if (pid == 0) { /* Proceso Hijo */ if (execl("esclavo","esclavo", "nombre", "-a", NULL) == -1) { printf("Error al ejecutar execl\n"); exit(1);} } else { /* Proceso Padre */ wait(&status); printf("\nEl proceso hijo finalizo con el estado %d\n", status); exit(0);} } esclavo.c: #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i = 0; for (i = 0; i < argc; i++) printf("\nArgumento [%d]: %s", i, argv[i]); exit(0);} gcc -o miexec.exe miexec.c gcc -o esclavo esclavo.c ./miexec.exe Argumento [0]: esclavo Argumento [1]: nombre Argumento [2]: -a El proceso hijo finalizo con el estado 0