Beruflich Dokumente
Kultur Dokumente
TEMA 2
GESTIÓN DE PROCESOS
1. Introducción ...................................................................................................................................... 2
2. Procesos y procesadores ................................................................................................................... 3
2.1. Recursos ............................................................................................................................... 3
2.2. Ciclo de vida de un proceso.................................................................................................. 4
2.3. Interbloqueo (deadlock)........................................................................................................ 4
3. Planificación de procesos.................................................................................................................. 6
3.1. El contexto de los procesos .................................................................................................. 6
3.2. El reloj y las interrupciones .................................................................................................. 7
3.3. Peticiones de servicio ........................................................................................................... 9
3.4. Estrategias de planificación ................................................................................................ 10
3.4.1. Colas de tareas...................................................................................................... 11
3.5. Representación de cronogramas ......................................................................................... 12
4. Protección y comunicación............................................................................................................. 15
4.1. Protección........................................................................................................................... 15
4.2. Comunicación y sincronización.......................................................................................... 15
5. Procesos e hilos (threads) ............................................................................................................... 17
6. Sistemas operativos monolíticos y microkernel............................................................................ 18
1. Introducción
Ya hemos visto que la mayor parte de sistemas operativos actuales son multiproceso, es decir, son
capaces que ejecutar varios programas simultáneamente, sin necesidad de que termine la ejecución de uno de
ellos para comenzar con el siguiente.
Pero normalmente los ordenadores sólo disponen de una CPU, por lo que sólo pueden ejecutar una
instrucción de un programa en un momento dado. A veces tenemos ordenadores con varias CPU, capaces de
ejecutar a la vez tantas instrucciones como CPU tengan. Pero en cualquier caso, el sistema operativo
multiproceso logra que se ejecuten de forma simultánea muchos programas (más que el número de
procesadores). La forma de lograrlo es compartiendo el uso de la CPU entre los distintos programas. El
sistema operativo hace que cada cierto tiempo (del orden de centésimas de segundo) se ejecuten unas cuantas
instrucciones de cada programa. Podemos hacer un símil con dos bombillas y un interruptor conmutador con
dos posiciones, de manera que en cada posición se enciente una de las bombillas y se apaga la contraria,
como muestran los esquemas de la Figura 1.
Si accionamos el conmutador rápidamente, dará la impresión de que las dos bombilla están
encendidas constantemente, aunque en realidad, en un instante dado, sólo una de las bombillas lo está (Figura
2). Como cada bombilla sólo recibe corriente la mitad del tiempo, también alumbrará la mitad, pero dará la
impresión de estar siempre encendida. Así, si dos programas se ejecutan en la misma CPU, y cada uno de
ellos se le permite ejecutar instrucciones alternativamente durante un breve instante de tiempo, cada uno de
ellos tardará el doble de tiempo en ejecutarse, pero no tendremos la impresión de que la ejecución se detiene
En este tema aprenderemos cómo se organiza un sistema operativo para lograr el multiproceso.
Comenzaremos por estudiar algunas definiciones que nos ayudarán a comprender el resto del tema.
2. Procesos y procesadores
Denominaremos proceso o tarea a una actividad a realizar por el ordenador, es decir, un programa.
Aunque en realidad suele distinguirse entre los términos proceso y tarea, por el momento utilizaremos ambos
indistintamente.
La CPU (procesador) del ordenador tarda un cierto tiempo en ejecutar las instrucciones de los
procesos. Decimos que un proceso requiere un cierto tiempo de CPU. Cuando tenemos varios procesos que
se deben ejecutar simultáneamente en la misma CPU, decimos que esos procesos deben compartir la CPU, o
lo que es lo mismo, compartir el tiempo de CPU. También decimos que los procesos se ejecutan en tiempo
compartido (time-sharing), y que a cada proceso le corresponde una rodaja de tiempo, cuantum o ciclo de
CPU. Si varios procesos se ejecutan en tiempo compartido (o a la vez, uno en cada CPU) decimos que esos
procesos son concurrentes o paralelos (o que se ejecutan concurrentemente).
Cuando un proceso se está ejecutando en un procesador decimos que
procesador, o también que ese procesador está asignado a ese proceso ese proceso ha
tomado ese procesador.
Aunque nuestro ordenador disponga de más de un procesador, lo normal es que el número de procesos
que hay que atender sea mucho mayor. Así, varios procesos se ejecutarán al mismo tiempo (de forma
simultánea realmente), uno en cada procesador, pero para atender al resto habrá que compartir el tiempo de
CPU.
Un proceso sólo puede ejecutarse en un procesador en un instante dado (no puede ejecutarse en dos
procesadores al mismo tiempo), ya que las instrucciones que forman el programa deben ejecutarse de forma
secuencial (una detrás de otra). Si en un momento dado no hay ningún proceso que ejecutar, o hay más
procesadores que procesos, uno o más procesadores no tendrán que ejecutar ninguna instrucción de ninguna
ociosos.
Algunos procesos se deben ejecutar múltiples veces, en instantes de tiempo periódicos (con un cierto
periodo, cada cierto tiempo). Otros se deben ejecutar en instantes indefinidos (una o varias veces). El primer
tipo de procesos se denominan periódicos y el segundo esporádicos.
2.1. Recursos
Para poder ejecutarse un programa, éste necesita disponer de un conjunto de recursos. Los recursos
pueden ser hardware (dispositivos, información almacenada en dispositivos, etc.) o software (una condición
provocada por otro programa, una señal, etc.), y suelen ser compartidos por todos los procesos que existen en
el ordenador. Muchos de esos recursos compartidos son de uso exclusivo. Esto quiere decir que si están
siendo empleados por un proceso (decimos que los recursos están ocupados, reservados, bloqueados), otro
proceso no podrá utilizarlos. Si un proceso no dispone de todos los recursos que necesita, no puede ejecutarse
y debe esperar a que se liberen por parte del proceso que los reservó.
Un claro ejemplo de recurso es una impresora. Si un proceso está enviando datos a la impresora,
cualquier otro proceso debe esperar a que termine el primero, ya que de lo contrario se mezclarían los datos
enviados y se imprimiría una mezcla de los trabajos.
Los recursos más importantes que necesita cualquier proceso son:
• Memoria: los programas están en el disco. Para poder ejecutarse, primero deben copiarse a la
memoria principal del ordenador (cargarse en memoria). Además, los programas necesitan espacio
adicional en memoria para sus cálculos intermedios. La memoria del ordenador el limitada (y
generalmente pequeña), por lo que se puede agotar si muchos programas intentan ejecutarse al
mismo tiempo. Si se agota, no se podrán ejecutar nuevos programas hasta que terminen los
programas actuales, y liberen la memoria que ocupan.
• CPU: suele ser el recurso más valioso y escaso. Mientras se ejecuta un proceso, ningún otro se
puede ejecutar en la misma CPU.
Los recursos pueden ser únicos, o existir varias unidades de cada uno. Por ejemplo, existen muchas
unidades de memoria, y pueden existir varias CPU, y varias impresoras. A veces, aunque existan varias
unidades de un determinado recurso, un proceso puede requerir una unidad en concreto (por ejemplo una
impresora de unas determinadas características). El sistema operativo es quien se encarga de administrar y
repartir lo más equitativamente posible los recursos de que consta el ordenador, evitando al máximo que los
recursos se desaprovechen.
Inexistente
crear
Creación
recursos
no recurso
Preparado Bloqueado
recurso
fin
no o o
CPU urs urs fin
CPU o rec rec
n
Ejecución Terminado
fin
La Figura 3 muestra un esquema del ciclo de vida de un proceso. Al principio, el proceso no existe.
Una orden del usuario hará que el sistema operativo se prepare para ejecutar un nuevo proceso, y entonces
reserva los recursos necesario para ello. Cuando un proceso cuenta con todos los recursos menos la CPU,
decimos que está preparado. Cuando el sistema operativo le asigna el procesador, el proceso comienza a
ejecutarse. Cuando termina su rodaja de tiempo, el proceso vuelve a estado de preparado y procesador se
asigna a otra tarea. Si, durante la ejecución, el proceso solicita un recurso no disponible, quedará suspendido
o bloqueado hasta que el recurso se libere. En determinadas circunstancias un proceso preparado (pero no
ejecutándose) puede perder algún recurso que posee, pasando a estar bloqueado.
Un proceso en ejecución puede llegar al final del programa, pasando a un estado de terminación
previo a la desaparición, durante el cual se liberan todos los recursos mantenidos. Además, un proceso
preparado o bloqueado puede ser forzado a terminar a causa de una condición provocada por otro proceso o
por el propio sistema operativo (por ejemplo, la detección de un problema o error).
ningún proceso pueda avanzar y terminar con la situación. Por ejemplo, supongamos la existencia de dos
procesos, A y B, y dos recursos R1 y R2. Tanto A como B necesitan reservar los dos recursos al mismo
tiempo para poder realizar su función, y que A reserva R1 antes que R2, y B lo hace al contrario. A y B se
ejecutan concurrentemente (p.e. en tiempo compartido), de manera que se puede dar la siguiente secuencia de
sucesos:
• A intenta reservar R1. Está disponible, luego lo reserva y continúa, pero agota su cuantum y pierde
el procesador a favor de B.
• B intenta reservar R1. No está disponible (lo mantiene A), por lo que queda bloqueada, perdiendo el
procesador a favor de A.
• A intenta reservar R2. No está disponible (lo mantiene b), por lo que queda bloqueada, perdiendo el
procesador.
• Tanto A como B están bloqueadas en espera de un recurso, luego no pueden continuar. A espera por
el recurso que mantiene B, y viceversa. Ninguna tarea puede terminar y liberar el recurso que
mantiene, por lo que la otra tampoco podrá hacerlo. A y B esperan cíclicamente una por la otra,
luego están bloqueadas entre sí; se ha producido un interbloqueo.
En una situación de interbloqueo dos o más tareas no pueden terminar su operación, luego fracasan. El
sistema operativo debe velar para que esto no se produzca. Si, en el ejemplo anterior, el sistema operativo
detecta el interbloqueo y aborta una de las tareas implicadas, por ejemplo la A, los recursos que ésta mantiene
y que causan el interbloqueo quedan automáticamente liberados, con lo que la otra tarea podría continuar.
Por lo tanto, el sistema operativo debe definir alguna estrategia que evite el interbloqueo. Podemos
establecer 3 grandes modos de hacerlo:
1. Evitar la situación: ofrecer una política de asignación de recursos que evite el interbloqueo. Por
ejemplo, si se fuerza a que un proceso reserve de una sola vez todos los recursos que necesita, y
luego los libere también de golpe, no se podrá producir la situación anterior. Este es un método
eficaz, pero resulta poco flexible y complica la programación de las tareas.
2. Reparar el interbloqueo: detectar los posibles casos de interbloqueo, y en caso de que se
produzca, abortar alguna de las tareas. Permite mayor flexibilidad a la hora de programar las
tareas, pero proporcionará un sistema de poca calidad en el que algunas tareas pueden fracasar sin
que tengan ningún problema de diseño, simplemente por culpa de otras tareas que se ejecuten
concurrentemente.
3. No hacer nada: detectar las situaciones de interbloqueo es costoso. Es posible que ocupemos
mucho tiempo de CPU en calcular sin situación es un interbloqueo o no, tiempo que no
emplearemos en ejecutar el código de las tareas, lo cual no es deseable. Si la probabilidad de que
se produzca un interbloqueo es baja, el tiempo de cálculo invertido será un desperdicio. Es posible
que sea mejor dejar que se produzcan interbloqueos, y si se producen, que sea el usuario quien lo
detecte y resuelva manualmente (solicitando el aborto de alguna tarea). Este método se denomina
.
3. Planificación de procesos
El sistema operativo es el encargado de la creación y destrucción de los procesos, y además es el que
decide qué proceso se asigna al procesador (o a cada procesador, si hay más de uno) en cada instante. La
decisión de qué proceso ejecutar en cada momento se denomina . El planificador en la parte del
sistema operativo que se encarga de la planificación de procesos, y constituye una de las partes más
complejas e importantes del mismo. De lo bien o mal que esté diseñado y construido dependerá gran parte
del rendimiento que se pueda obtener del hardware subyacente.
Antes de continuar estudiando la planificación de los sistemas operativos, debemos tomar tres
consideraciones importantes:
• El tiempo de CPU debe repartirse entre los distintos procesadores de manera equitativa, de manera
que todos tengan la oportunidad de ejecutarse en un tiempo finito.
• El sistema operativo constituye un proceso más: las operaciones que deben realizarse para decidir
qué proceso ejecutar y efectuar las operaciones necesarias para que esto ocurra consume tiempo de
CPU, aunque normalmente no se tiene en cuenta.
• Hay procesos más importantes que otros. Por ejemplo, las operaciones del propio sistema operativo
son más importantes que los procesos del usuario. Así, los sistemas operativos pueden establecer
niveles de prioridad a la hora de decidir la planificación.
• PID. El PID es un número "identificador de proceso", que sirve para referirse de forma unívoca a
un proceso. A cada proceso que se crea se le asigna un número diferente.
• Registros. Los registros son unos almacenes (memorias muy pequeñas, para almacenar números)
que utiliza el procesador para interpretar las instrucciones máquina de un programa. Guardan
información vital sobre el programa que ejecutan, como por ejemplo la posición en la memoria de
la instrucción actual (por dónde vamos ejecutando), la posición en memoria de los resultados y
datos de cálculo y del resto del programa, etc. y almacenan los operadores y operandos que
constituyen la instrucción que se ejecuta en un instante dado. Cuando un proceso pierde el
procesador, el sistema operativo debe guardar la información actual de los registros para volver a
ponerlos igual la próxima vez que le toque tiempo de procesador.
• Prioridad. Cada proceso puede tener un nivel de prioridad distinto, en función del cual el sistema
operativo decidirá a qué proceso le asigna el procesador, de manera que los procesos con mayor
nivel de prioridad (mayor importancia) tomarán el procesador con mayor probabilidad que los
menos prioritarios.
• Contabilidad. El sistema operativo debe mantener una contabilidad de los recursos que consume
cada proceso, especialmente del tiempo de CPU que lleva gastado, la cantidad de accesos a
recursos valiosos, etc. Esta información se suele utilizar, entre otras cosas, para modificar sobre la
marcha el nivel de prioridad del proceso. Por ejemplo, si un proceso tarda mucho en terminar, se le
baja la prioridad para que tengan más oportunidades otros procesos que pueden ser más cortos.
• Estado. Se debe anotar el estado actual del proceso: si está preparado o bloqueado en espera de un
recurso, y qué recurso o recursos está esperando.
• Recursos que mantiene. También hay que guardar información sobre los recursos que mantiene,
de manera que al finalizar el proceso se puedan liberar automáticamente todos los recursos
bloqueados. Esta información también es útil para detectar y resolver situaciones de interbloqueo.
Entre los recursos anotados, destacan la memoria, los archivos, acceso a dispositivos, etc.
Toda esta información concerniente a un proceso se denomina contexto del proceso o bloque de
control de proceso (BCP). Cada vez que un proceso toma el procesador, parte de su contexto debe
transmitirse a los registros de la CPU para que prosiga la ejecución. Igualmente, cuando el proceso pierde la
CPU, el valor de los registros debe copiarse al contexto para que no se pierda al entrar el siguiente proceso.
Este traspaso de información se denomina cambio de contexto, y se produce cada vez que un nuevo
proceso debe tomar la CPU. El cambio de contexto es una operación relativamente complicada y costosa, en
cuanto a la cantidad de tiempo que requiere. Cuando un proceso deja la CPU, se guarda su contexto y se
cargan en los registros los valores adecuados para ejecutar el código del planificador (la parte del sistema
operativo que decide qué proceso ejecutar a continuación). Luego entra en juego el dispatcher, otro elemento
del sistema operativo. Su función es recoger la información del contexto del proceso seleccionado por el
planificador, volcarla en los registros, y ceder el control de la CPU al proceso.
A B C D
E F G H
Figura 4. Mecanismo de interrupción y llamada al sistema
1
Esta denominación también procede del hecho de que las llamadas al sistema se materialicen con el uso de instrucciones máquina de
interrupción (trap), unas instrucciones que tienen un funcionamiento semejante al que se produce en la CPU cuando se eleva una
Las operación de E/S suelen ser muy lentas. Una lectura de disco puede tener un retardo del orden de
milisegundos, mientras que la ejecución de una instrucción del ordenador tiene un retardo del orden de
microsegundos (incluso nanosegundos). En el tiempo que un proceso espera la lectura de un dato de disco, el
ordenador podría haber ejecutado decenas de miles de instrucciones de otras tareas. Esta cantidad se dispara
en el caso de dispositivos más lentos (discos flexibles, cintas, etc.).
Así, cuando un proceso realiza una llamada al sistema de E/S, el sistema operativo prepara y envía la
solicitud al manejador del dispositivo afectado, y a continuación invoca al planificador y al dispatcher, no sin
antes haber marcado el proceso como bloqueado (en espera de la respuesta del recurso solicitado), de manera
que otro proceso se ejecuta mientras se efectúa físicamente la petición del primer proceso.
Cuando el dispositivo termine la petición, enviará una señal de interrupción a la CPU indicando que la
petición está terminada. El sistema operativo marcará el proceso que solicitó el servicio como preparado, y
A B C
D E F
Figura 5. Petición de operación E/S
La Figura 5 ilustra el proceso. Un proceso de usuario realiza una llamada al sistema (A). La CPU pasa
al sistema operativo (B), que envía la petición correspondiente al hardware, marca el proceso como
bloqueado, selecciona un nuevo proceso entre la lista de preparados, e invoca al dispatcher, que hace que se
ejecute un nuevo proceso (C). Mientras éste (o algún otro) se ejecuta, el hardware termina de realizar la
operación solicitada y envía la correspondiente interrupción a la CPU (D), que hace saltar a la rutina de
servicio correspondiente en el sistema operativo (E). Ésta se dará cuenta de que la información proporcionada
por el hardware estaba siendo esperada por el proceso bloqueado, así que le enviará la información solicitada,
lo marcará como preparado, e invocará al planificador, que lo seleccionará para ejecución. Tras esto,
dispatcher que pondrá a ejecutar el proceso.
El planificador del sistema operativo debe proporcionar un servicio óptimo y equitativo, de manera
que las tareas más urgentes tengan prioridad, pero todas (urgentes y no urgentes) tengan la oportunidad de
completarse en un plazo de tiempo razonable. Para ello, hay que diseñar una estrategia de selección de tareas
adecuada. El problema se asemeja al comportamiento de las personas en la cola de un supermercado.
Supongamos un supermercado con una única caja, a la que acuden los clientes de todo tipo: con carros
grandes y llenos, con cestas pequeñas, con prisa, etc. Como gerentes del establecimiento, nuestra tarea es que
lo clientes queden lo más satisfechos posibles con el funcionamiento de la cola de caja. Nos podemos
plantear las siguientes cuestiones:
• ¿Dejamos pasar antes a los clientes con cestas? A un cliente con un gran carro lleno no le importará
"colar" a alguien con un cesta casi vacía, ya que acabará enseguida, y el cliente de la cesta estará
contento porque no ha tenido que esperar un largo rato a que acabara el del carro. Pero, ¿y si hay
muchos clientes con cestas casi vacías? El cliente del carro no estará dispuesto a colar todas las
cestas.
• ¿Dejamos pasar antes a los clientes con prisa? El problema es parecido al anterior. Colar a un
cliente con prisa no importa, pero si son muchos, el cliente sin prisa esperará demasiado tiempo.
Debemos observar el hecho de que a medida que pasa el tiempo, el cliente sin prisa que espera
mientras otros con más prisa son colados, va teniendo cada vez más prisa ya que su tiempo se
agota.
• ¿Cómo haríamos para "colar" a los clientes con cesta si no supiéramos la cantidad de artículos que
hay en las cestas hasta que dejaran de ponerse en la cinta transportadora de la caja? Supongamos la
siguiente situación: una pareja va al supermercado, llenan medio carro y van a la caja. Mientras el
marido coloca productos del carro en la cinta de la caja, la mujer va añadiendo productos de las
estanterías al carro. Después de media hora en esta situación, el cajero podría detener
momentáneamente la cuenta de la pareja y atender al siguiente cliente, que lleva una cesta pequeña,
acabara enseguida con él, y volver con la cuenta de la pareja. Pero, ¿y si el cliente de la cesta
hiciera lo mismo que la pareja del carro?
Cada supermercado adoptará el criterio que mejor le parezca; algunos colarán siempre a los clientes
con prisa o cestas pequeñas (aunque algún cliente se pueda enfadar mucho), y otros seguirán el estricto orden
de llegada (aunque algún cliente con prisa tenga que esperar). El supermercado debería hacer un estudio del
tipo de clientes más habituales para satisfacer lo mejor posible a esos clientes; o quizá debería tener contentos
a los que llenan más sus carros, aunque sean pocos. No existe un criterio fijo que satisfaga a todos los
clientes.
Esta comparación nos sirve para comprender el funcionamiento de una estrategia de planificación. La
caja representa la CPU y los clientes son las tareas. Normalmente la CPU o el sistema operativo no pueden
saber cuánto tiempo va a tardar una tarea hasta que ésta no ha acabado. Si damos paso a la CPU a las tareas
más prioritarias, corremos el riesgo de que las menos prioritarias no se ejecuten nunca2. Si respetamos el
orden de llegada, algunas tareas urgentes e importantes se podrían realizar demasiado tarde.
Ante todo, señalar que todos los sistemas multiproceso aprovechan los tiempos de atención de
peticiones de E/S de una tarea para ejecutar otras tareas preparadas.
Normalmente todos los sistemas tienen en cuenta distintos niveles de prioridad entre tareas, de manera
que las más prioritarias se ejecutan siempre antes que las menos prioritarias. El sistema operativo se encarga
de asignar las prioridades: las más altas a procesos del propio sistema operativo y las más bajas a tareas del
usuario. Las tareas del sistema operativo se suponen suficientemente cortas como para no llegar a impedir
indefinidamente la ejecución de procesos de usuario; pero no se puede afirmar lo mismo si se permiten
distintos niveles de prioridad entre tareas de usuario. Una forma de solucionar este problema es asignar
prioridades dinámicas: tras una asignación inicial de prioridades, cada cierto tiempo (p.e. cada tick de reloj)
se recalculan las prioridades, de manera que las tareas que llevan más tiempo ejecutándose, pierden prioridad
(son penalizadas con pérdida de prioridad). Así, una tarea con una prioridad muy alta impedirá que otra de
menor prioridad se ejecute hasta que llegue un momento en que la penalización iguale las prioridades.
También se puede aumentar la prioridad de las tareas que lleven mucho tiempo preparadas pero sin
ejecutarse. Si no tenemos prioridades dinámicas, decimos que las prioridades son .
Cuando tenemos prioridades dinámicas, la penalización o compensación de la prioridad en cada tick
se puede hacer de manera que no se sobrepasen determinados límites. Por ejemplo, una tarea de usuario
nunca debería alcanzar mayor prioridad de una del sistema.
Ante dos o más tareas de la misma prioridad se suele aplicar una estrategia de asignación cíclica
(round-robin); cada vez que una tarea pierde el procesador, se toma la siguiente. Normalmente se aprovecha
el tick de reloj para expulsar a una tarea del procesador y tomar la siguiente (lo más habitual), aunque no
tiene por qué ser así, y los sistemas pueden hacer que la tarea de mayor prioridad espere a que la que tiene la
CPU termine o agote su cuantum. La primera estrategia se denomina expulsora frente a la segunda no
expulsora.
Un determinado planificador funcionará siguiendo una determinada combinación de las posibilidades
antes descritas, quizá con algunas variantes. Por ejemplo, lo más habitual será tener un sistema expulsor de
asignación cíclica basado en prioridades dinámicas en las que las tareas del sistema siempre prevalezcan ante
las de usuario, y se recalculen las prioridades cada cierto número de ticks de reloj, y se planifique en cada
tick y en cada llamada al sistema o interrupción hardware. También es posible que un determinado sistema
permita modificar la estrategia de planificación en cualquier momento (mediante una llamada al sistema
2
A esta situación, en la que una tarea nunca se ejecuta por que otras más prioritarias se anteponen, la denominamos "muerte por
inanición", starvation.
Cuando llega una nueva tarea, se le asigna una prioridad y se coloca al final de la cola correspondiente
(también puede ser al principio). Cuando hay que seleccionar una tarea, se toma la que esté al frente de la
cola de mayor prioridad y se elimina de la cola. Se ejecuta esa tarea hasta que finaliza, acaba el cuantum,
queda bloqueada por falta de un recurso (por ejemplo una operación E/S no completada) o es expulsada por
una tarea de mayor prioridad que pasa a estar preparada. Si la tarea termina, desaparece del planificador; si
queda bloqueada, se añade a la cola de tareas bloqueadas; y en el resto de casos, se añade al final de la cola
correspondiente. Si es necesario, se recalculan las prioridades de las tareas de cada cola, pasándose una tarea
a otra cola en caso de que cambie su prioridad (por ejemplo se pone al final de la cola a la que pasa).
Entonces se toma la nueva tarea al principio de la cola de mayor prioridad.
Cuando una tarea bloqueada recibe el recurso por el que esperaba, se coloca de nuevo en la cola
correspondiente a su prioridad (al principio o al final, dependiendo de cada sistema). Si la estrategia utilizada
es no expulsora, no se tomará un nuevo proceso y se continuará con el que se estaba ejecutando en el
momento de asignarse el recurso esperado.
Bloqueadas G E J
Prioridad 0 B C A I
Prioridad 1 D H
Prioridad 2
Prioridad 3 F
Tarea Prioridad Tiempo3 Llegada4 Peticiones E/S que realiza (instante y duración)
A 0 4 5 Al final de su tercer ciclo, y tarda 2 ciclos en servirse.
B 2 5 2 Ninguna
C 2 2 4 Ninguna
D 4 7 0 Ninguna
E 4 2 7 Al final de su primer ciclo, y tarda 3 ciclos en servirse.
Suponiendo que las prioridades se asignan estáticamente y que las tareas nuevas y las que dejan de
estar bloqueadas se ponen al principio de la cola correspondiente a su prioridad, el cronograma representativo
es el de la Figura 7, explicado paso a paso en la Tabla 2. Los criterios de representación son los siguientes:
E
tCPU
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Figura 7. Cronograma para el primer caso
3
Tiempo mide la cantidad de tiempo necesaria para terminar la ejecución del proceso (medido en ciclos de CPU).
4
Llegada mide el instante de llegada (medido en ciclos de CPU).
4. Protección y comunicación
4.1. Protección
Uno de los objetivos de un sistema operativo multiproceso es proporcionar a los procesos un entorno
de ejecución de tiempo compartido que ofrezca la ilusión de poner a su disposición todos los recursos del
hardware. Una de las implicaciones que tiene esta afirmación es que unos procesos no deben ser conscientes
de la existencia de otros, es decir, no deben verse afectados por la ejecución de otros. Por otra parte, el
sistema operativo debe poner los recursos a disposición de los procesos, de forma ordenada y sin conflictos,
lo que implica que todo acceso a recursos debe hacerse por medio de llamadas al sistema, y nunca
directamente.
La memoria del ordenador es un espacio común que podemos representar como una lista de
direcciones, y en cada dirección hay un dato o una instrucción. El sistema operativo asigna fragmentos de esa
memoria a los procesos para que se guarden las instrucciones, los datos y los cálculos intermedios. Un
proceso puede consultar y modificar el contenido de su fragmento de memoria, pero no puede hacerlo en el
fragmento de memoria de otros procesos, con lo que se asegura la no agresión de unos procesos a otros.
El sistema operativo también dispone de un fragmento de memoria para sus instrucciones y datos,
pero a diferencia de los procesos, el sistema operativo necesita poder manipular el contenido de la memoria
de otros procesos. Por esta razón, los computadores disponen de varios nieves de privilegio durante el
funcionamiento. Al menos dos son necesarios, modo usuario y modo supervisor, aunque se pueden
establecer otros intermedios. En modo usuario, las instrucciones que se ejecutan no pueden manipular zonas
de memoria de otros procesos; en modo supervisor, sí. Además, el modo usuario restringe otras capacidades
de ejecución, como el acceso a los dispositivos hardware. De esta manera se asegura que los programas de
usuario no puedas acceder directamente a los dispositivos, si no es por medio de peticiones al sistema
operativo, que es el único programa que funciona en modo supervisor.
El cambio de modo supervisor a modo usuario se efectúa automáticamente durante los cambios de
contexto, de forma que al entrar un programa de usuario, se cambia a modo usuario. Cualquier intento de
acceder a operaciones restringidas, provocará un error que causará la finalización fulminante de la tarea por
parte del sistema operativo.
recibir un mensaje que no se ha enviado aún, el proceso quedará bloqueado hasta que esté disponible, como
si se hubiera solicitado un recurso cualquiera. Así, el paso de mensajes sirve también como método de
sincronización: cuando se desbloquea el proceso que recibe, se dice que se ha producido la cita.
En algunos sistemas, es posible hacer que tanto el que envía como el que recibe se queden bloqueados
hasta que la otra parte alcance la cita. A veces también ocurre lo contrario, que ninguna de las partes se queda
bloqueada.
Para sincronización de procesos existe otro mecanismo más potente: los semáforos. Los semáforos se
utilizan cuando dos o más procesos deben competir por la utilización de un recurso que sólo puede ser
utilizado por un número limitado de procesos. Normalmente el número máximo es 1, y se utiliza la palabra
para referirnos al mecanismo.
Cuando dos o más procesos compiten por un recurso al que no se puede acceder de forma concurrente
(a la vez), decimos que ese recurso es una región crítica y se debe utilizar en exclusión mutua. Normalmente
los semáforos se utilizan para controlar el acceso a regiones críticas, y más concretamente a fragmentos de
código compartidos por varios procesos y que no pueden ser ejecutados de forma concurrente, sino que hasta
que un proceso no lo termina, no lo puede empezar a ejecutar otro5.
El semáforo consiste en una posición de memoria en la que se anota un número. Cuando un proceso
quiere entrar en la región crítica, decrementa el número en 1, y al salir, lo vuelve a incrementar. Sólo es
posible entrar si el valor del semáforo es mayor o igual que 1 (si es 0, no puede decrementar ni entrar). Al
principio, el valor del semáforo es 1 (mútex), o el número máximo de procesos que pueden compartir el
recurso). Si un proceso no puede entrar porque el semáforo está «cerrado», entonces queda bloqueado, como
si hubiera solicitado un recurso no disponible, hasta que otro proceso salga de la región crítica.
Como los procesos no pueden compartir memoria, para poder acceder a un semáforo compartido por
varios procesos, necesitamos utilizar los servicios del sistema operativo. Dispondremos de un conjunto de
llamadas al sistema que nos permiten crear un semáforo (dale el valor inicial) solicitar la entrada a una región
crítica (decrementar el valor) y salir de ella (incrementarlo).
5
Ésta es una definición más precisa de lo que es una región crítica. Se trata de un fragmento de código que maneja datos que no pueden
ser accedidos simultáneamente por más de un proceso, de manera que si un proceso comienza a ejecutar instrucciones del fragmento
(entrada a la región crítica), ningún otro proceso podrá comenzar hasta que el primero termine (salga).
Por estas dos razones, entre oras, surge la necesidad de definir un nuevo nivel de multiprocesamiento:
los hilos (hebras, threads). Los hilos son algo semejante a las tareas, es decir, flujos de instrucciones máquina
independientes que se ejecutan concurrentemente compartiendo el tiempo de CPU. La diferencia está en la
organización interna de los hilos: las tareas son creadas y planificadas por el sistema operativo, mientras que
los hilos son manipulados por el propio programa de usuario. Una parte del programa de usuario se encarga
de planificar los hilos, dentro del tiempo de CPU asignado al proceso.
Todos los hilos de un proceso comparten los recursos del proceso, incluida la memoria, por lo que
pueden comunicarse y sincronizarse sin necesidad de utilizar las llamadas al sistema que proporciona el
sistema operativo. Además, el cambio de contexto que se necesita realizar entre hilos es notablemente más
simple que el que se realiza entre procesos y el sistema operativo. Así, conseguimos solucionar los dos
problemas que planteamos al principio.
La Figura 8 ilustra la organización del software desde el punto de vista del sistema operativo, las
tareas y los hilos. En la capa inferior se encuentra el sistema operativo, y sobre él están las tareas. A su vez,
las tareas se pueden desdoblar en hilos, pero todos los hilos forman parte de la misma tarea, compartiendo los
recursos que ésta mantenga. El planificador del sistema operativo se encarga de planificar las distintas tareas,
asignándoles tiempo de CPU siguiendo algún criterio. Dentro del tiempo de CPU que le corresponde a la
tarea, un planificador de hilos reparte el tiempo entre los distintos hilos.
hilo 1
hilo 2
hilo 3
Sistema Operativo
En algunos sistemas, la gestión de hilos es tarea del propio sistema operativo. Entonces, las
deferencias entre hilos y procesos se diluyen. En cualquier caso, los hilos pertenecientes a una misma tarea
comparten el espacio de memoria y otros recursos, por lo que los cambios de contexto, así como otras
operaciones de comunicación y sincronización entre ellos seguirán siendo más simples, rápidas y eficientes
que sus equivalentes entre procesos.
La escuela monolítica defiende que es mejor que todos los servicios se presten como partes de una
gran tarea que es el núcleo del sistema operativo, como ilustra la Figura 9. En esta estructura, cada parte del
núcleo se puede comunicar y sincronizar con las demás directamente, compartiendo posiciones de memoria.
Esto le proporciona una gran velocidad de ejecución. Sin embargo, esta posibilidad hace que las distintas
partes del sistema tengan una compleja interrelación que lo hace difícil de construir, modificar, reparar, etc.
En el extremo contrario, la escuela microkernel defiende que es mejor que el núcleo sea muy
pequeño y simple, y preste un conjunto mínimo de servicios, mientras el resto se servicios se prestan
mediante tareas independientes (semejantes a las tareas de usuario). Como las distintas tareas del sistema no
pueden comunicarse directamente, necesitan utilizar los servicios básicos de comunicación del núcleo, lo que
produce un funcionamiento más lento. Sin embargo, las tareas están perfectamente delimitadas y
estructuradas, de forma que fácil de construir, modificar y reparar.
Planificador
y Dispatcher
Núcleo
del S.O.