Un proceso no es
más que una instancia de un programa en ejecución, incluyendo los valores
actuales del contador de programa, los registros y las variables
Creación de un proceso
Hay cuatro eventos principales
que provocan la creación de procesos:
1. El arranque del sistema.
2. La ejecución, desde un
proceso, de una llamada al sistema para creación de procesos.
3. Una petición de usuario para
crear un proceso.
4. El inicio de un trabajo por lotes.
Terminación de procesos
Una vez que se
crea un proceso, empieza a ejecutarse y realiza el trabajo al que está
destinado. Sin embargo, nada dura para siempre, ni siquiera los procesos. Tarde
o temprano el nuevo proceso terminará, por lo general debido a una de las
siguientes condiciones:
1. Salida normal (voluntaria).
2. Salida por error (voluntaria).
3. Error fatal (involuntaria).
4. Eliminado por otro proceso (involuntaria).
Estados de un proceso
En ejecución (en realidad está
usando la CPU en ese instante).
Listo (ejecutable; se detuvo
temporalmente para dejar que se ejecute otro proceso).
Bloqueado (no puede ejecutarse sino hasta que ocurra
cierto evento externo).
Hilos
En los sistemas operativos tradicionales, cada proceso tiene un espacio
de direcciones y un solo hilo de control. De hecho, ésa es casi la definición
de un proceso. Sin embargo, con frecuencia hay situaciones en las que es
conveniente tener varios hilos de control en el mismo espacio de direcciones.
Tener un tipo de proceso dentro de otro proceso, es lo que comúnmente llamamos
hilos.
Dormir y despertar
Una de las más simples primitivas de comunicación entre procesos son sleep (dormir) y wakeup (despertar). Sleep es una
llamada al sistema que hace que el proceso que llama se bloquee o desactive, es
decir, que se suspenda hasta que otro proceso lo despierte. La llamada wakeup tiene un
parámetro, el proceso que se va a despertar o activar. De manera alternativa,
tanto sleep como wakeup tienen un
parámetro, una dirección de memoria que se utiliza para asociar las llamadas a sleep con las
llamadas a wakeup.
Semáforos
E. W. Dijkstra (1965) sugirió el uso de una variable entera para contar
el número de señales de despertar, guardadas para un uso futuro. En su
propuesta introdujo un nuevo tipo de variable, al cual él le llamó semáforo.
Un semáforo podría tener el valor 0, indicando que no se guardaron señales de
despertar o algún valor positivo si estuvieran pendientes una o más señales de
despertar.
Propuso que se tuvieran dos operaciones, down y up (generalizaciones de sleep y wakeup,
respectivamente). La operación down en un semáforo comprueba si el valor es mayor que 0. De ser así,
disminuye el valor (es decir, utiliza una señal de despertar almacenada) y sólo
continúa. Si el valor es 0, el proceso se pone a dormir sin completar la
operación down por el momento.
Mutexes
Cuando no se necesita la habilidad del semáforo de contar, algunas veces
se utiliza una versión simplificada, llamada mutex. Los mutexes son
buenos sólo para administrar la exclusión mutua para cierto recurso compartido
o pieza de código. Se implementan con facilidad y eficiencia, lo cual hace que
sean especialmente útiles en paquetes de hilos que se implementan en su
totalidad en espacio de usuario.
Un mutex es una variable que puede estar en uno de dos estados:
abierto (desbloqueado) o cerrado (bloqueado). En consecuencia, se requiere sólo
1 bit para representarla, pero en la práctica se utiliza con frecuencia un
entero, en donde 0 indica que está abierto y todos los demás valores indican
que está cerrado. Se utilizan dos procedimientos con los mutexes. Cuando un
hilo (o proceso) necesita acceso a una región crítica, llama a mutex_lock.
Si el mutex está actualmente abierto (lo que significa que la región crítica
está disponible), la llamada tiene éxito y entonces el hilo llamador puede
entrar a la región crítica.
Por otro lado, si el mutex ya se encuentra cerrado, el hilo que hizo la
llamada se bloquea hasta que el hilo que está en la región crítica termine y
llame a mutex_unlock. Si se bloquean varios hilos por el mutex, se
selecciona uno de ellos al azar y se permite que adquiera el mutex.
Monitores
Para facilitar la escritura de programas correctos, Brinch Hansen (1973)
y Hoare (1974) propusieron una primitiva de sincronización de mayor nivel,
conocida como monitor, que es una colección de procedimientos, variables
y estructuras de datos que se agrupan en un tipo especial de módulo o paquete.
Los procesos pueden llamar a los procedimientos en un monitor cada vez que lo
desean, pero no pueden acceder de manera directa a las estructuras de datos
internas del monitor desde procedimientos declarados fuera de éste. Los
monitores tienen una importante propiedad que los hace útiles para lograr la
exclusión mutua: sólo puede haber un proceso activo en un monitor en cualquier
instante.
Pasaje (transmisión) de mensajes
Este
método de comunicación entre procesos utiliza dos primitivas (send y receive) que, al
igual que los semáforos y a diferencia de los monitores, son llamadas al
sistema en vez de construcciones del lenguaje.
Barreras
Este mecanismo de sincronización está destinado a los grupos de
procesos, en vez de las situaciones de tipo productor-consumidor de dos
procesos. Algunas aplicaciones se dividen en fases y tienen la regla de que
ningún proceso puede continuar a la siguiente fase sino hasta que todos los
procesos estén listos para hacerlo. Para lograr este comportamiento, se coloca
una barrera al final de cada fase. Cuando un proceso llega a la barrera,
se bloquea hasta que todos los procesos han llegado a ella.