Sei sulla pagina 1di 72

*HVWLyQGH3URFHVRV

Como se vio en la Introducción, en los sistemas operativos actuales se puede


disponer de varios programas en memoria que se pueden ejecutar, dependiendo de
la política de planificación, de una forma más o menos simultánea.

En este capítulo nos vamos a ocupar de cómo el sistema operativo se encarga de


controlar todos los procesos cargados en memoria, de la asignación de la CPU a
cada uno de los procesos, y, cuando se trate de procesos cooperantes, de la
comunicación y sincronización entre ellos.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

,QWURGXFFLyQ
Sabemos que la CPU realiza ciertas actividades. En un sistema de tratamiento por
lotes (EDWFK), se ejecutan trabajos; en un entorno de tiempo compartido hay
programas de usuarios, o tareas; incluso en un ordenador personal, hoy día un
usuario puede ejecutar varios programas simultáneamente, uno interactivo, y otros
en segundo plano (EDFNJURXQG . La cuestión es cómo llamar a todas estas
actividades que realiza la CPU. Pues bien, a estas actividades las denominaremos
SURFHVRV.

 &RQFHSWRGH3URFHVR
Se han oído muchas definiciones de proceso, pero, sin duda, la más popular y
acertada es la que dice que “XQSURFHVRHVXQSURJUDPDHQHMHFXFLyQ”.

Ya que no es nada fácil dar una definición autoexplicativa de lo que es un proceso,


vamos a tratar de explicarlo mediante ideas y ejemplos. Ante todo, se debe tener
muy presente que un proceso asocia programa+actividad.

Cuando decimos que un proceso es un programa en ejecución, nos referimos al


hecho de llevar a cabo o realizar las instrucciones que contiene el programa en el
orden establecido. Un programa es una lista de instrucciones escritas en un papel,
un fichero en disquete, disco duro, memoria RAM o cualquier otro soporte, pero el
simple hecho de que estas instrucciones estén escritas no implica que se estén
llevando a cabo. Pues bien, cuando se leen estas instrucciones y se hacen ejecutar,
entonces ya tenemos programa+actividad, es decir, un proceso.

Hacemos hincapié en DFWLYLGDG para diferenciarlo bien de un mero programa, ya que


una característica básica de los programas es que son estáticos. Ya hemos dicho
que un programa es una secuencia de órdenes que, por mucho que la miremos, no
varía nunca. Las variables no tienen valores, las rutinas no tienen dirección, las
condiciones están sin evaluar. Un programa es simplemente XQ DOJRULWPR D
HMHFXWDU

En cambio, un proceso es dinámico. Tiene vector de estado indicando el momento y


estado de su ejecución, las variables tienen valores, las rutinas se encuentran en
alguna dirección de memoria, y las condiciones son evaluables. El proceso es OD
HMHFXFLyQGHODOJRULWPR

Un ejemplo que puede describir la relación programa-proceso puede ser el manual


de montaje de un aeromodelo que viene despiezado. Las instrucciones de montaje
que vienen en la caja son algo estático (por eso aunque dejemos mucho tiempo las
instrucciones en la caja, el aeromodelo no aparece construido al cabo de un tiempo.
Sin embargo, cuando cogemos las instrucciones y empezamos a hacer lo que
indican, es cuando empieza la actividad, entonces comienza el SURFHVR de montaje.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

,QWURGXFFLyQ &RQFHSWRGH3URFHVR
¢4XpHVXQ3URFHVR"

(VXQ3URJUDPDHQ(MHFXFLyQ

8Q3URJUDPD (VHVWiWLFR1RYDUtDQDGD
1RUHTXLHUH
1RWLHQHYHFWRUGHHVWDGR→
SURFHVDGRU
9DULDEOHVVLQYDORUHV
5XWLQDVVLQGLUHFFLyQ
&RQGLFLRQHVVLQHYDOXDU
(VHODOJRULWPRDHMHFXWDU

8Q3URFHVR (VGLQiPLFR
1HFHVLWD
7LHQHYHFWRUGHHVWDGR→
SURFHVDGRU
/DVYDULDEOHVWLHQHQYDORUHV
/DVUXWLQDVHVWiQHQDOJXQDGLUHFFLyQ
/DVFRQGLFLRQHVVHSXHGHQHYDOXDU
(VODHMHFXFLyQGHODOJRULWPR

352&(62
3URJUDPD
(VWDGR  (VWDGR
3URFHVDGRU

6H 8Q&3
SRUSURFHVR
QHFHVLWD 8QiUHDGHWUDEDMR

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

Si en la construcción del aeromodelo, quien lleva a cabo las instrucciones del


manual de montaje es la persona, que lee las instrucciones y las va ejecutando; en
un ordenador, el ente que va leyendo las instrucciones del programa (que está en
memoria) y ejecutándolas, es la CPU. Así, tenemos que el tándem
programa+procesador es el que hace que el estado de desarrollo de un determinado
trabajo evolucione y cambie de un estado a otro.

Al igual que para la construcción del aeromodelo se necesita un área de trabajo (una
mesa o algo similar), para la ejecución de un programa, también se requiere un área
de trabajo, esto es, ODSLODdel proceso. La pila es la zona de memoria en la que se
guardan los parámetros, variables locales, direcciones de retorno de subprogramas
y, en general, los datos temporales del proceso. Un proceso también se caracteriza
porque en un momento dado de su ejecución dispone de ciertos valores en los
registros del procesador, entre ellos, el &RQWDGRU GH 3URJUDPD que contiene la
dirección de la instrucción que va a ejecutar el proceso a continuación. Así, el
Contador de Programa es el registro que indica el punto de ejecución del programa
en el que se encuentra el proceso.

Se debe tener en cuenta que la relación entre un programa y su proceso no es


biunívoca. Esto se debe al hecho de que en un momento dado puede haber varios
procesos correspondientes al mismo programa. Por ejemplo, en una clase de
trabajos manuales pueden estar escritas en la pizarra las instrucciones de montaje
de un aeromodelo (un programa) y todos los alumnos están montando un
aeromodelo siguiendo las mismas instrucciones (varios procesos). Un ejemplo más
informático puede ser el de varios alumnos que, en un momento dado, están
utilizando el editor de texto en distintos terminales de un sistema de tiempo
compartido. Hay un único programa editor de texto, pero puede haber varios
procesos simultáneos ejecutando tal programa.

 'HVFULSWRUGH3URFHVR
Como ya sabemos, en un sistema multiprogramado se pueden ejecutar
simultáneamente varios programas, es decir, en un momento dado puede haber
varios procesos. En un sistema con un único procesador no se puede decir que
haya varios programas ejecutándose estrictamente de una forma simultánea. En
realidad, la posesión del procesador se va repartiendo entre los distintos procesos,
dando lugar a una ejecución pseudoparalela. No obstante, por simplicidad, nos
referiremos, en general, a ejecución simultánea o paralela, tanto si es estricta
(sistema multiprocesador) como si no lo es (con un solo procesador).

El FRQWH[WR de un proceso se define como la información necesaria para especificar


completamente su estado actual. Esto incluye toda la información que ha de
salvarse cuando un proceso pierde la posesión de la CPU, y hay que restaurar
cuando se le vuelve a conceder la posesión del procesador.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

El contexto de un proceso, dependiendo del lugar donde reside la información, está


formado por tres tipos de información:

• Bloque de Control de Proceso (BCP)


• Contexto de Memoria
• Contexto del Procesador

El %ORTXH GH &RQWURO GH 3URFHVR (o Descriptor de Proceso) comprende la


información que siempre está en la memoria principal durante la existencia del
proceso. El BCP contiene la información relativa a un proceso que requiere el
sistema operativo para gestionarlo. Igual que en una empresa se dispone de las
fichas de los empleados, con los datos necesarios para la administración (Datos
personales, profesionales, salarios, horas extraordinarias, etc.), el sistema operativo
dispone de los BCP de cada uno de los procesos que tiene en cada momento.
Aunque el contenido de los descriptores de procesos varía de un sistema a otro, en
la Figura 2 se puede ver un ejemplo con algunos datos típicos que suelen contener.
A lo largo de este capítulo se irá viendo el significado y utilidad de estos datos del
BCP, así como las estructuras de datos que contiene el sistema operativo para
mantener los BCP’s de todos los procesos.

El &RQWH[WR GH 0HPRULD contiene lo que es el programa en sí, es decir, las


instrucciones y los datos del programa. Esta parte solamente necesita estar en
memoria principal cuando el proceso está realmente en ejecución, o sea, que tiene
la CPU asignada; el resto del tiempo, el contexto de memoria puede encontrarse en
disco, liberando así espacio de memoria principal.

El &RQWH[WR GHO 3URFHVDGRU es la parte del contexto del proceso almacenado en


los registros del procesador. En estos registros se mantienen datos tales como el
Contador de Programa, el estado de la CPU, el puntero de la pila utilizada por el
proceso, registros de control y registros de propósito general para mantener datos
de las variables o constantes del programa.

Como ya hemos dicho, el BCP es algo así como la ficha de control del proceso, por
esto, en él también se encuentra la información necesaria para poder acceder tanto
al contexto de memoria como al del procesador.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

,QWURGXFFLyQ 'HVFULSWRUGH3URFHVR
¢&yPR9HHO62DORV3URFHVRV"

0HGLDQWHVXYHFWRUGHHVWDGRHQXQ
'HVFULSWRUGH3URFHVR

(O'HVFULSWRUGH3URFHVRFRQWLHQHLQIRUPDFLyQ
GHO3URFHVR
GHVX0HPRULD &RQWH[WR
GHOSURFHVR
GH(VWDGRGHO3URFHVDGRU 5HJLVWURV

W\SH DESCRIPTOR_PROCESO LV
UHFRUG
Id_Proceso
Siguiente
Estado
Puntero_Pila
Memoria_Utilizada
Ficheros_Abiertos
Proceso_Padre
Procesos_Hijos
Dormido_Hasta
HQGUHFRUG;

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

6LVWHPDV0XOWLSURFHVR3DUDOHOLVPR
En la Introducción ya se vio, por encima, la utilidad de ejecutar varios programas de
forma simultánea para evitar el desaprovechamiento de los tiempos de espera de
CPU a las respuestas de los dispositivos periféricos en las operaciones de
entrada/salida que realiza un proceso.

Otra justificación de la ejecución paralela de programas viene dada por las leyes de
la física, pues según la Teoría de la Relatividad de Albert Einstein es imposible
transmitir señales eléctricas a más velocidad que la de la luz, esto es, unos 30cm
por nanosegundo. Esto quiere decir que el tiempo mínimo que puede tardar en
transmitirse una señal entre dos componentes de un ordenador separados 30cm es
1ns. O sea, que los ordenadores tendrían que empezar a fabricarse cada vez más
pequeños, pero claramente, sabemos que esto tiene ciertas limitaciones. Por otra
parte, algunos programas (cálculo científico, tiempo real, etc.) requieren, a menudo,
ciertas velocidades de ejecución, para que el resultado sea exitoso. Una forma de
conseguir que un programa se ejecute más rápidamente que ejecutando una
instrucción tras otra, consiste en dividir el programa en varias tareas o procesos y
ejecutándolos en paralelo. Un símil de la vida real puede ser el proceso de
fabricación de los automóviles. Las cadenas de montaje de los coches no fabrican
primero las ruedas, luego el motor, a continuación el chasis, después la carrocería,
luego los cristales, ...; sino que cada uno de estos elementos se fabrica por
separado de forma paralela y simultánea, y cuando están todos construidos, se
realiza el proceso de ensamblaje para formar el automóvil completo. De esta forma
se consigue construirlo en bastante menos tiempo.

Ya tenemos, por tanto, dos buenas razones para la multiprogramación:

• Los procesos cooperantes, para realizar un trabajo en conjunto.


• Mejorar el rendimiento del ordenador, aprovechando la CPU en sus tiempos de
espera por dispositivos de E/S, dando lugar a sistemas multiusuario o
simplemente de multiproceso.

No debemos confundir un sistema PXOWLSURFHVR con PXOWLXVXDULR. Un entorno


multiproceso es el que permite varios procesos simultáneos, por ejemplo, una
persona en un terminal con ventanas tal que en cada ventana tiene distintos
procesos en ejecución (un editor de texto, un reloj, un videoclip, etc.). En este
sistema es posible que no haya más puestos de trabajo o terminales, con lo que
sería un entorno multiproceso monousuario. Si se dispone de varios terminales en
un sistema, entonces, además de multiproceso, es multiusuario.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

6LVWHPDV0XOWLSURFHVR3DUDOHOLVPR

3UREOHPDVGHO3URFHVDPLHQWR6HFXHQFLDO
/DVOH\HVGHODItVLFD 1RVHDSURYHFKD
LPSRQHQOLPLWDFLRQHV
OD&38
GHYHORFLGDG

£62/8&,Ð1

352&(6$0,(1723$5$/(/2 RVHXGRSDUDOHOR
08/7,352*5$0$&,Ð1

0XOWLSOH[DFLyQGHOD
3URFHVRV &38HQWUHSURFHVRV
&RRSHUDQWHV FRQFXUUHQWHV
FRRSHUDQWHVRQR

0XOWLSURFHVR≠0XOWLXVXDULR

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

 6LVWHPDV0XOWLSURFHVDGRU0RQRSURFHVDGRU
0XOWLSOH[DFLyQGHOD&38
Sabemos que para que un programa se ejecute, hay que convertirlo en proceso, y
para que éste evolucione de un estado a otro se requiere un procesador. Si
hablamos de sistemas multiproceso, es decir, que hay varios programas
simultáneamente en memoria, lo más conveniente para la ejecución más rápida es
asignar un procesador a cada uno de los procesos, para que evolucionen todos de
una forma realmente paralela o simultánea. Este es el caso de los ordenadores con
múltiples procesadores, en los que en un momento dado se encuentran realmente
en ejecución varios programas. Si bien esta es la forma más rápida de ejecutar
múltiples programas, también resulta ser, obviamente, la más cara. No solamente
por la cantidad de hardware utilizado (cuando menos, varias CPU’s), sino también
por el hardware y software adicional necesario para poder sincronizar y gestionar
múltiples CPU’s, algo mucho más complejo que cuando solamente se dispone de un
único procesador. (Resultan mucho más sencillos los departamentos de personal,
marketing, fabricación, distribución etc. de una empresa de 2, 3 o 4 empleados, que
los de una multinacional de miles de empleados).

Ya que la mayoría de los ordenadores actuales de propósito general constan de un


sólo procesador, vamos a centrarnos aquí en los ordenadores monoprocesadores,
dejando los de múltiples procesadores para otras asignaturas de arquitecturas
avanzadas.

Si tenemos un sistema operativo multiproceso y contamos con un único procesador,


solamente tenemos una elección: +D\TXHUHSDUWLUHOSURFHVDGRUHQWUHORVSURFHVRV.
De esta manera, lo que conseguimos es simular una máquina multiprocesador, es
decir, tendremos un pseudoparalelismo en la ejecución de los procesos. Así, aunque
no obtengamos todas las ventajas de una ejecución puramente paralela, sí
aprovecharemos el hecho de que durante la ejecución de un programa hay muchos
momentos en que no se puede continuar su ejecución hasta que no se produzca
algún evento externo a la CPU, como por ejemplo la realización de una operación
solicitada a un dispositivo de E/S, la pulsación de un carácter en el teclado, la
llegada de un carácter por la línea de comunicaciones, una interrupción generada
por un detector, etc.

Quizás nos puedan servir como ejemplo las partidas de ajedrez simultáneas que
mantienen los maestros de ajedrez con 20 principiantes. Cuando el maestro realiza
una jugada con un principiante, éste tarda mucho tiempo hasta que decide mover;
mientras tanto, el maestro puede realizar jugadas con el resto de los aprendices, y
seguramente, le dará tiempo a hacer un movimiento con cada uno de los rivales
antes de que el primero de ellos se haya decidido a mover una figura.

En este ejemplo tan optimista, esta partida simultánea se realiza en 1/20 del tiempo
que se tardaría si el maestro primero se dedicase por entero a jugar una partida con
el primer aficionado, hasta finalizarla; luego comenzar otra partida con el segundo y
así sucesivamente hasta haber jugado las 20 partidas. En los casos reales de
nuestros ordenadores, el reparto de la CPU no suele resultar tan provechoso como
en el caso del maestro de ajedrez, pues a menudo, también hay procesos a los que

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

6LVWHPDV0XOWLSURFHVR
0iTXLQD0XOWLSURFHVDGRU
8Q3URFHVR 8Q3URFHVDGRU

0iTXLQD0RQRSURFHVDGRU
+D\TXHVLPXODUXQDPiTXLQDPXOWLSURFHVDGRU
6HXGRSDUDOHOLVPR

£ +D\TXHUHSDUWLUHOSURFHVDGRU
HQWUHORVSURFHVRV 
3URFHVR

3URFHVR

3URFHVR

0XOWLSURFHVDGRU 0RQRSURFHVDGRUPXOWLSOH[DGR

3URFHVRHQHMHFXFLyQ
3URFHVRHQHVSHUD

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

se les quita la posesión de la CPU antes de que ellos la liberen por una de sus
esperas a un evento externo, por lo que nos encontramos con procesos que están
parados cuando sí podrían ejecutarse de tener una CPU disponible. En el peor de
los casos, es decir cuando se disponga de varios procesos que no tienen esperas
de E/S, la diferencia entre ejecutarlos en un entorno multiprocesador o
monoprocesador es la que se muestra en el gráfico de la Figura 5, en la que se
aprecia que la CPU se reparte en porciones de tiempo entre los distintos procesos,
por lo que mientras uno se ejecuta, los demás deben esperar su turno de CPU. En
este caso decimos que la utilización de la CPU está PXOWLSOH[DGDHQHOWLHPSR.

 (VWDGRV\7UDQVLFLRQHVGHORV3URFHVRV
Anteriormente hemos visto que un ejemplo del FLFORGHYLGD de un proceso podría
estar caracterizado por esta secuencia:

1. Se está ejecutando (posee la CPU).


2. Realiza una operación de E/S y se pone a esperar la respuesta (abandona la
CPU).
3. Cuando recibe la respuesta de la operación de E/S desea continuar la
ejecución (necesita otra vez la CPU).

También hemos visto con anterioridad que en otras ocasiones, el tiempo de CPU se
reparte en porciones entre todos los procesos que lo solicitan, por lo que un proceso
puede ver cómo pierde la posesión del procesador, de forma involuntaria, cuando se
le acaba la porción de tiempo que tenía asignada.

Este ciclo de vida de un proceso lo podemos ver en la Figura 6. En esta figura, los
círculos son recursos o condiciones deseadas por un proceso, mientras que los
rectángulos representan las colas de procesos que esperan por tales recursos. Así,
vemos que un proceso recién creado inmediatamente quiere ejecutarse, para lo cual
necesita la CPU, por lo que pasa a la cola de los procesos que esperan por el
procesador (cola de procesos SUHSDUDGRV). Una vez que le llega el turno, ejecuta
las instrucciones, hasta que por uno de los motivos mencionados (operación de E/S,
fin de la porción de tiempo, etc.) pierde la posesión de la CPU y pasa a la cola del
recurso solicitado (Disco 1, Unidad de Cinta 3, esperar a las 2:00 de la mañana). Si
la posesión del procesador se ha perdido por finalizar la porción de tiempo asignado
o bien porque un proceso de mayor prioridad requiere el procesador, el recurso que
necesita el proceso expulsado (o expropiado) es la propia CPU, por lo que pasa a la
cola correspondiente (la de los procesos preparados).

Este ciclo se repite una y otra vez hasta que el proceso ejecuta su última instrucción,
o sea, que finaliza el trabajo, con lo que voluntariamente cede el control de la CPU y
da por concluida su existencia como proceso.

Así, llegamos ya al diagrama básico de estados de un proceso, que lo vemos


representado en la parte superior de la Figura 7. En él tenemos que un proceso está
ejecutando instrucciones ((Q (MHFXFLyQ), esperando a que le concedan la CPU

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

para ejecutar instrucciones (3UHSDUDGR), o esperando a que se produzca algún


evento externo ((Q(VSHUDoEORTXHDGR).

6LVWHPDV0XOWLSURFHVR &LFORGH9LGDGHXQSURFHVR

&UHDFLyQ 7HUPLQDFLyQ
 35(3$5$'2 &38

load
store ([SXOVLyQ
add 3HULRGR
store &38
read fichero
3HULRGR 'LVFR ',6&2
(VSHUD(6
(6 
store
3HULRGR
increment
&38
write fichero
3HULRGR (VOD 'RUPLGRKDVWD

ODV
(VSHUD(6
(6 KRUD
load
store
3HULRGR
add
&38
store &LQWD &,17$
read fichero 
3HULRGR
(VSHUD(6
(6


6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

Vamos a ver cómo se producen las transiciones entre estos tres estados.

&UHDFLyQ→3UHSDUDGR
Inicialmente, al crear un proceso, no puede pasar directamente al estado de
Espera, pues no se ha ejecutado ninguna instrucción que así se lo indique; por
lo tanto, expresa su necesidad de ejecución entrando en la cola de procesos
Preparados.

3UHSDUDGR→(Q(MHFXFLyQ
Una vez que el proceso está preparado, solamente tiene que esperar a que le
llegue el turno. Los turnos del procesador se establecen en función de la
política de planificación de la CPU. Cuando le llegue el turno tomará posesión
del procesador y empezará a ejecutar instrucciones.

Como podemos ver en el gráfico, desde el estado de Ejecución se puede pasar


a cualquiera de los otros dos estados (Espera y Preparado).

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

6LVWHPDV0XOWLSURFHVR (VWDGRVGHO3URFHVR

(Q 7HUPLQDFLyQ
(MHFXFLyQ

&UHDFLyQ (Q
3UHSDUDGR (VSHUD

MAX_PROCESOS : constant 100;


type ID_PROCESOS is INTEGER range 0..MAX_PROCESOS;
type&2/$B352&(626is
record
Primero : ID_PROCESOS := 0;
Ultimo : ID_PROCESOS := 0;
end record;

3URFHVRV : array [1..MAX_PROCESOS] of DESCRIPTOR_PROCESOS;

(QB(MHFXFLyQ : ID_PROCESOS;
3UHSDUDGRV : COLA_PROCESOS;
(QB(VSHUD : COLA_PROCESOS;

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

(Q(MHFXFLyQ→3UHSDUDGR
Pasará a Preparado si abandona la CPU involuntariamente, es decir si se le
expulsa (o expropia), bien porque haya terminado su porción de tiempo, o
porque haya pasado a Preparado otro proceso de mayor prioridad.

(Q(MHFXFLyQ→(VSHUD
En cambio, pasa al estado de Espera si cede voluntariamente el procesador,
por tener que esperar a que se produzca un evento externo a la CPU.

(VSHUD→3UHSDUDGR
Desde el estado de Espera no se puede volver directamente a Ejecución, sino
que cuando se haya cumplido el evento externo, para recuperar la posesión del
procesador hay que pasar de nuevo por la cola de los procesos preparados.
Así tenemos que para conseguir la posesión de la CPU siempre hay que pasar
por su cola correspondiente (como un recurso más que es).

(Q(MHFXFLyQ→7HUPLQDFLyQ
Como ya hemos dicho antes, el proceso termina cuando ejecuta su última
instrucción, luego siempre pasará al estado de Terminado desde Ejecución.

Como hemos visto, a medida que progresa su ejecución, un proceso va cambiando


de estado. A excepción de los estados obvios de &UHDFLyQ y 7HUPLQDFLyQ, un
proceso tiene dos estados básicos: $FWLYR y (Q(VSHUD

$FWLYR El proceso no depende de ninguna condición externa al procesador


para poder continuar su ejecución. O bien se está ejecutando, o se
encuentra esperando en la cola de procesos Preparados a que le
concedan la posesión de la CPU.

(Q(VSHUD El proceso no puede continuar la ejecución. Está bloqueado, a la


espera de que se cumpla un evento externo a la CPU, tal como una
respuesta de un dispositivo de E/S, que llegue una hora determinada,
etc.

Vistos los estados que puede tener un proceso y las transiciones entre ellos, nos
interesa saber FyPR HO VLVWHPD RSHUDWLYR OOHYD FRQWURO GH WRGRV ORV SURFHVRV
FUHDGRV, cómo se realizan estas colas de procesos y cómo se sabe qué proceso
está en ejecución.

En un apartado anterior hemos visto que hay una estructura de datos que contiene
toda la información de control que requiere el sistema operativo para ocuparse de la
gestión o administración del proceso. Nos estamos refiriendo al Descriptor de
Proceso o Bloque de Control de Proceso (BCP). Pues bien, entonces lo que el
sistema operativo necesita es mantener la colección de todos los BCP’s de los
procesos existentes en cada momento. Esta colección de BCP’s se puede organizar
de distintas maneras: mediante una cola estática, con una cola dinámica, o
simplemente utilizando una tabla, es decir, un vector o array de BCP’s. Así vemos

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

6LVWHPDV0XOWLSURFHVR (VWDGRVGHO3URFHVR

(1B(-(&8&,Ð1

35(3$5$'26 %&3 %&3

3ULPHUR
ÓOWLPR

'LVFR
3ULPHUR
ÓOWLPR

&LQWD %&3 %&3 %&3

3ULPHUR
ÓOWLPR

7HUPLQDO %&3

3ULPHUR
ÓOWLPR

        
7DEOD 6LJQLO 6LJQLO6LJ 6LJ 6LJQLO6LJQLO 6LJ6LJQLO6LJQLO
GH%&3V         

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

en el recuadro inferior de la Figura 7 la declaración de Procesos como un array de


tamaño Max_Procesos y formado por elementos de tipo Descriptor_Procesos.
De esta manera, el sistema operativo puede consultar el BCP de cada proceso
accediendo a la tabla Procesos utilizando el identificador del proceso como índice.

También se puede apreciar que la variable En_Ejecucion contiene el identificador


del proceso que actualmente se encuentra en posesión de la CPU. Por último,
podemos observar la implementación del estado de Preparado, o sea, una variable
de tipo Cola_Procesos (que hace de cabecera de una cola), que no es más que
un registro con dos campos que indican, respectivamente, cuál es el primero y el
último elemento de una cola de procesos. Cada elemento de la cola de procesos es
un BCP, y si nos fijamos en su declaración de la Figura 3 podemos ver que gracias
al campo Siguiente, que es el enlace, resulta fácil formar dicha cola. El campo
Siguiente contiene un identificador de proceso (el siguiente en la cola) o bien el
valor NIL si es el último de la cola.

Habiendo visto cómo se construye la cola de Preparados, resulta fácil imaginar que
las colas de procesos para cada uno de los recursos por los que se puede esperar
(dispositivos de E/S, etc.) se implementan de igual forma.

En la Figura 8 se muestra un ejemplo del estado de los procesos en un momento


determinado. Por una parte tenemos que el proceso en Ejecución es el 6, y que la
cola de los procesos Preparados está formada por los procesos 7 y 2. No hay
ningún proceso esperando para acceder al Disco_1, mientras que por la Cinta_3
están esperando los procesos 3, 4 y 1. Por último, tenemos al proceso 5 que está
esperando por el Terminal_1. En la parte inferior de esta Figura 8 tenemos la tabla
de BCP’s en la que se pueden observar los valores que toman los campos
Siguiente para formar las colas de procesos de este ejemplo.

 &DPELRGH3URFHVRHQ(MHFXFLyQ&DPELRGH&RQWH[WR
Cuando un proceso en ejecución pierde la posesión de la CPU, se debe a uno de
los siguientes motivos:
• Llamada al sistema
• Interrupción
• Fin del proceso

Está claro que ante ciertas llamadas al sistema (una petición de E/S, dormir, etc.) el
proceso en cuestión queda bloqueado, sin poder continuar la ejecución de
instrucciones, hasta que se atienda el servicio solicitado; por esto, cede la posesión
del procesador para que pueda ejecutar instrucciones otro proceso que esté
preparado.

También puede ocurrir que una interrupción anuncie el final de la actual posesión
del procesador. Esto será así si la interrupción es la generada por el reloj del
sistema y se detecta que se ha consumido la porción de tiempo asignada (en un

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

&DPELRGH3URFHVR
6LVWHPDV0XOWLSURFHVR  HQHMHFXFLyQ

3HWLFLyQGH(6
+//$0$'$$/6,67(0$
'RUPLU

)LQGHODURGDMDGHWLHPSR
0RWLYRV
+,17(5583&,Ð1
GHO&DPELR 6HSUHSDUDXQSURFHVRGH
PD\RUSULRULGDG

+),1'(/352&(62

&DPELRHQ'RV)DVHV

¨6HOHFFLRQDUHO6LJXLHQWH3URFHVR
3/$1,),&$'25

©&DPELDUHO&RQWH[WR GH(MHFXFLyQ
',63$7&+(5

£(O&DPELRGH3URFHVR&RQVXPH7LHPSR

0LQLPL]DUORV&DPELRVGH3URFHVRV

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

sistema de tiempo compartido); o bien la interrupción la ha generado algún


dispositivo de E/S indicando el fin de una operación previamente solicitada, con lo
que el proceso que la había requerido sale de su situación de espera y pasa al
estado de Preparado. En un entorno de planificación de CPU por prioridades, si este
proceso que acaba de pasar a Preparado tiene mayor prioridad que el que está en
Ejecución, se le quita a éste último el procesador para asignárselo al proceso de
mayor prioridad.

Por último, el motivo trivial de pérdida de procesador se debe a que el proceso en


Ejecución llega a su fin. Detectado esto, el sistema operativo asigna la CPU a algún
proceso preparado.

Si un proceso pierde el control de la CPU ¡habrá que asignárselo a otro proceso!


Efectivamente, y hay que realizarlo en dos pasos:

1º Seleccionar el siguiente proceso


2º Realizar el cambio de contexto

De realizar el primer paso se encarga el SODQLILFDGRU (o VFKHGXOHU), que utilizando la


política establecida de planificación a corto plazo selecciona un proceso de la cola
de Preparados. Esta política de SODQLILFDFLyQDFRUWRSOD]Restablece la asignación
de la CPU a uno de los procesos preparados, mientras que la política de
SODQLILFDFLyQDODUJRSOD]R se utiliza para elegir uno de los trabajos que están en el
disco duro esperando para ser cargados en memoria y comenzar su ejecución.

Una vez conocido el identificador del proceso que va a apropiarse de la CPU, hay
que cederle el control del procesador, pero no antes de realizar el cambio de
contexto. Para esto, se da control al 'LVSDWFKHU, que es la parte del sistema
operativo que se encarga de terminar de salvar el contexto del programa que
abandona la CPU (o perderlo definitivamente si se debe a una terminación del
proceso) y establecer el contexto del proceso seleccionado. De hecho, el último de
los pasos de que consta el cambio completo del contexto, consiste en restaurar el
Contador de Programa del proceso elegido. Una vez que el registro Contador de
Programa es restaurado, la siguiente instrucción que se ejecuta es ya una
instrucción del proceso seleccionado.

La Figura 12 muestra una secuencia completa de dos cambios de contexto. El


primero se debe a la pérdida de la CPU del Proceso A en favor de Proceso B,
mientras que en el segundo cambio, el Proceso A recupera el procesador. Veamos
con cierto detalle un posible algoritmo que realiza este cambio de contexto. No
obstante, este grado de detalle dista bastante de ser completo, ya que para ello
habría que particularizar las operaciones el cambio de contexto para un procesador
concreto y un conjunto dado de Llamadas al Sistema.

• Llamada al sistema o interrupción de un dispositivo. En cualquier caso, esto


implica automáticamente una copia del REGISTRO DE ESTADO y del
CONTADOR DE PROGRAMA a la Pila del proceso en ejecución, al que
llamaremos Proceso A.
• Se comienza a realizar el servicio solicitado o el tratamiento de la interrupción.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

&DPELRGH3URFHVR (O3ODQLILFDGRU

6HOHFFLRQDHO6LJXLHQWH3URFHVRD(MHFXWDU
6HJ~QOD3ROtWLFDGH3ODQLILFDFLyQ

12(;38/625(6 (;38/625(6
3),)2 35RXQG5RELQ
3(OPiVFRUWRHOSULPHUR 33ULRULGDGHV
33ULRULGDGHV

£ ),1'(/$52'$-$
'(7,(032 
-- Actualizar el final de la cola Preparados.
Procesos(Preparados.Ultimo).Siguiente := En_Ejecución;
Preparados.Ultimo := En_Ejecución;

-- Seleccionar un proceso.
Proceso_Seleccionado := Preparados.Primero;

-- Actualizar el principio de la cola Preparados.


Preparados.Primero := Procesos(Preparados.Primero).Siguiente;

-- Cambiar el contexto del procesador.


Dispatcher;

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

• Se salvan todos los registros del procesador en la Pila del proceso actualmente
en ejecución (el A). A excepción del REGISTRO DE ESTADO (RE), CONTADOR
DE PROGRAMA (CP) y PUNTERO DE PILA (PP).
• Tras decidir que el proceso en ejecución debe abandonar la CPU, el Planificador
elige el proceso que pasará a Ejecución, al que nos referiremos como Proceso
B.
• Se llama al 'LVSDWFKHU y comienza el cambio de contexto. Se producen las
siguientes acciones:
1. El Puntero de Pila del proceso A se guarda en su Descriptor de Proceso.
2. Se carga el Puntero de Pila a partir del PP guardado en el Descriptor del
Proceso B. Ahora la Pila de trabajo es la del proceso B
3. Se cargan todos los registros generales del procesador a partir de los datos
contenidos en la Pila, a excepción del RE, el CP y el PP.
4. El valor que queda en la cima de la Pila es la dirección de vuelta del último
punto en el que se llamó al Dispatcher cuando el proceso B perdió el control de
la CPU.
5. Se actualiza la variable En_Ejecución con Proceso_Seleccionado.
6. El Dispatcher termina. Al ejecutar la instrucción de RETORNO DE SUBRUTINA,
se devuelve el control al punto en el que se había llamado al Dispatcher para
que el proceso B perdiese el control del procesador.
7. El sistema operativo termina de realizar el servicio que el Proceso B le había
solicitado, por lo que ejecuta la instrucción máquina RETORNO DE
INTERRUPCIÓN. Esta instrucción origina que se cargue el REGISTRO DE
ESTADO y el CONTADOR DE PROGRAMA con los dos datos que se sacan de la
cima de la Pila.
8. Con el nuevo CONTADOR DE PROGRAMA, la instrucción que se ejecuta es la
siguiente al punto en el que el proceso B realizó una Llamada al Sistema (lo
que supuso su paso a Espera).

Ya hemos visto que el cambio de CPU de un proceso a otro requiere una serie de
acciones que se encargan de realizarlas el Planificador y el 'LVSDWFKHU. Pues bien,
estas acciones se ejecutan en un tiempo que, desde luego, no es nulo. La elección
del proceso a ejecutar suele ser muy simple y no supone una pérdida de tiempo
considerable; pero el cambio de contexto sí puede acarrear una gran cantidad de
instrucciones, incluidas operaciones de E/S si los contextos de memoria de los
procesos que intervienen hay que salvarlos y traerlos, respectivamente, del disco
duro, cosa que suele realizarse en los actuales sistemas con Memoria Virtual.

No es deseable, en absoluto, que el tiempo dedicado al cambio de proceso sea


considerable. Pensemos en un caso exagerado : si el 20% de las instrucciones
ejecutadas en la CPU son las dedicadas a realizar el cambio de ejecución de dos
procesos, esto quiere decir que a las instrucciones de los programas del usuario

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

& D P E LR G H 3 U R F H V R ( O' L V S D W F K H U

( V H O( Q F D U J D G R G H & H G H U
H O& R Q W U R OG H  OD & 3 8 D O3 U R F H V R
6 H OH F F LR Q D G R S R U H O3 OD Q L I LF D G R U

6 X V  $ F F L R Q H V  % i V L F D V 6 R Q 

7 H U P LQ D U G H V D OY D U H OF R Q W H [ W R G H O
ž 3 U R F H V R $ F W X D OP H Q W H H Q ( M H F X F L y Q

( V W D E OH F H U H O3 X Q W H U R  G H 3 LOD G H O 1 X H Y R
Ÿ 3 U R F H V R T X H 3 D V D D ( M H F X F Ly Q

5 H V W D X U D U OR V 5 H J L V W U R V G H  OD & 3 8 D
  3 D U W L U G H  OD  1 X H Y D 3 L OD  H [ F H S W R  & 3 \ 5 (

 5 H V W D X U D 5 ( P R G R X V X D U L R
¡ 57(
 5 H V W D X U D & 3

6 LV W H P D V 2 S H U D W LY R V , * H V W Ly Q G H 3 U R F H V R V  

solamente se le dedica el 80% del tiempo de CPU, mientras que, claramente, lo más
deseable es que se acercara lo más posible al 100%.

De la observación anterior se deduce que conviene:


- minimizar el número de cambios de proceso,
- o minimizar las operaciones del cambio de contexto.

Esta conclusión la tendremos presente en los siguientes apartados, en los que


vamos a tratar más a fondo las acciones concretas del Dispatcher y algunas de las
políticas más comunes de planificación de CPU.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

6LVWHPDV0XOWLSURFHVR &DPELRGH3URFHVR
352&(62 6,67(0$ 352&(62
$ 23(5$7,92 %

all
HQHMHFXFLyQ ys_c
int. o
S
SUHSDUDGR

6DOYDU
5HJLVWURV3B$
HQHVSHUD 


6HOHFFLRQDU
3URFHVR

&DUJDU
5HJLVWURV3B%

HQHMHFXFLyQ

SUHSDUDGR int. o
S ys_ca
ll

6DOYDU
5HJLVWURV3B%
 HQHVSHUDR

 SUHSDUDGR
6HOHFFLRQDU
3URFHVR

&DUJDU
5HJLVWURV3B$

HQHMHFXFLyQ

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

 3URFHVR2FLRVR
Veamos ahora una situación, a primera vista comprometida, que se puede dar en un
sistema multiproceso. Sabemos que cuando un proceso cede voluntariamente el
control de la CPU, el Planificador tiene que elegir otro proceso de usuario que esté
preparado, pero

¡Qué pasa si no hay ningún proceso Preparado?

Si no hay ningún proceso que quiera ejecutar instrucciones ¡qué hace la CPU?
Esta situación no solamente es perfectamente posible, sino que se da con mucha
frecuencia. En un sistema con cinco usuarios ¿qué impide que en un instante
determinado cuatro de los procesos estén esperando una respuesta del disco duro,
la llegada de un carácter por una línea de comunicaciones, o que llegue una hora
determinada? La respuesta es: nada. Pues si en este momento el proceso del
quinto usuario también realiza una petición a un dispositivo de E/S, también pasa a
Espera. En este momento el Planificador tendría que seleccionar un proceso de la
cola de Preparados, para darle el control, pero ¡ya no hay más procesos! ¿qué va a
hacer la CPU mientras todos los procesos están en Espera?

Hemos mencionado solamente los procesos de usuario, pero también hay procesos
del sistema operativo que pueden estar realizando servicios que se les haya
solicitado con anterioridad, por ejemplo, imprimiendo un fichero. De cualquier forma,
tampoco hay nada que impida que en un momento dado no haya ningún proceso
con necesidad de ejecutar instrucciones.

Para estos casos, en todos los sistemas suele haber un proceso del sistema
denominado 3URFHVR2FLRVR. Este proceso consta de un bucle infinito ejecutando
un algoritmo muy sencillo (puede ser incluso la instrucción 1R2SHUDFLyQ) en el que
no figura ninguna instrucción que pueda generar un paso a Espera. El proceso
ocioso puede estar continuamente realizando estudios estadísticos de utilización del
sistema, calculando decimales del numero π, o cualquier trabajo infinito de la menor
prioridad. De esta forma, siempre tendremos un proceso Activo.

Diremos que la CPU está ociosa cuando esté ejecutando el Proceso Ocioso.

Los procesadores suelen disponer de la instrucción HALT (o WAIT), la cual hace


que se detenga la ejecución de instrucciones en la CPU, es decir, se deja de extraer
instrucciones de la memoria, y la CPU pasa al estado 'HWHQLGR, en el que
permanece hasta que se produce una interrupción, pues en ese momento el
procesador la atiende y comienza a ejecutar las instrucciones de su rutina de
tratamiento.

 3ODQLILFDFLyQGH3URFHVRV
Habiendo visto el ciclo de vida de los procesos, sabemos que un proceso que se
está ejecutando, eventualmente, por diversos motivos que se han comentado,
abandona la posesión del procesador voluntaria o involuntariamente, por lo que hay

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

que cederle la CPU a un proceso que sea lógicamente ejecutable, es decir, que esté
Preparado. Si hay más de un proceso Preparado, el sistema operativo debe decidir
cuál de ellos se ejecutará primero. La parte del sistema operativo que le
corresponde tomar tal decisión es el 3ODQLILFDGRU, que seleccionará un proceso
basado en una política o algoritmo de planificación.

El estado de Preparado se implementa como una cola de procesos dispuestos a


ejecutar instrucciones con ayuda de la CPU. Ahora bien, debemos a aclarar que
esta cola no tiene por que ser una cola FIFO (Primero en Entrar, Primero en Salir),
también podría ser una cola ordenada por prioridades, un árbol o, simplemente, una
lista desordenada. El Planificador debe seleccionar uno de los procesos de la lista,
pero no basándose necesariamente en la propia estructura de la lista. La estructura
de la lista únicamente debe ayudar al Planificador a aplicar el algoritmo de
planificación.

 &ULWHULRV
Antes de ver algoritmos concretos de planificación debemos recordar, que la
elección que el Planificador va a realizar debe hacerse persiguiendo “el bien del
sistema”, pues es uno de los cometidos del sistema operativo. No obstante, distintas
políticas de planificación tienen diferentes propiedades, y favorecen más a un tipo
de procesos que a otros. Antes de elegir el algoritmo a utilizar en una situación
concreta, debemos considerar las propiedades de varios algoritmos.
Se han sugerido muchos criterios para comparar algoritmos de planificación de
CPU. Decidir las características que se van a utilizar en la comparación es
fundamental para la elección del algoritmo más apropiado. Veamos los criterios de
comparación más comúnmente utilizados:

• -XVWLFLD. Cada proceso debe conseguir su porción correspondiente de CPU en


un tiempo finito.
• (ILFLHQFLD. Se debe intentar mantener la CPU ocupada el mayor tiempo posible.
Al decir “ocupada” queremos decir ejecutando cualquier proceso que no sea el
Proceso Ocioso. En un sistema real, el porcentaje de utilización de CPU suele
estar en el rango 40-90%. Una utilización mayor del 90% significaría que el
Planificador siempre encuentra procesos en la cola Preparados, o sea, que dicha
cola suele contener un número considerable de procesos. En un sistema
interactivo, esto puede suponer que los usuarios quizás se están impacientando
por obtener las respuestas.
• 7LHPSRGH5HWRUQR (WXUQDURXQGWLPH). Desde el punto de vista de un proceso, el
criterio más importante es cuánto tiempo se va a necesitar para ejecutarse
completamente. El Tiempo de Retorno es la suma de los periodos que se pasan
esperando a cargarse en memoria, esperando en la cola de Preparados,
ejecutándose en la CPU, y esperando por operaciones de E/S. Así pues, se debe
minimizar el tiempo de retorno de un proceso. Afecta principalmente a los
procesos EDWFK.
• 7LHPSRGH(VSHUD El algoritmo de planificación de CPU no afecta a la cantidad
de tiempo que un proceso se pasa realizando operaciones de E/S, solamente

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

6LVWHPDV0XOWLSURFHVR 3ODQLILFDFLyQGH3URFHVRV
(O3ODQLILFDGRUVH(QFDUJDGH6HOHFFLRQDU
HO3URFHVRTXH3DVDD(MHFXFLyQ

\'HFLGH6HJ~QXQD3ROtWLFD
%DVDGDHQ$OJXQRV&ULWHULRV

-XVWLFLD 7LHPSRGH5HVSXHVWD

(ILFLHQFLD 7LHPSRGH(VSHUD

7LHPSRGH5HWRUQR 5HQGLPLHQWR

3ROtWLFDVGH3ODQLILFDFLyQ

1R([SXOVRUDV ([SXOVRUDV
EDWFK PXOWLXVXDULR\
WLHPSRUHDO
3(O3ULPHURHQ/OHJDU 37LHPSR&RPSDUWLGR
5RXQG5RELQ
3(O0iV&RUWR3ULPHUR
33RU3ULRULGDGHV
33RU3ULRULGDGHV
30~OWLSOHV&RODV
30~OWLSOHV&RODV
5HDOLPHQWDGDV

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

afecta al tiempo que un proceso se pasa en la cola Preparados. El Tiempo de


Espera es la suma de todos los momentos que un proceso pasa en la cola de los
procesos preparados.
• 7LHPSR GH 5HVSXHVWD. En un sistema interactivo, el Tiempo de Retorno puede
no ser un buen criterio. A menudo, un proceso puede producir algunos resultados
al principio, y puede continuar calculando nuevos resultados mientras los
anteriores se le muestran al usuario. Así, tenemos que otra medida es el tiempo
que transcurre desde que se le hace una petición al sistema hasta que empieza a
responder, sin tener en cuenta el tiempo que se tarda en mostrar la respuesta
completa.
• 5HQGLPLHQWR. Se debe maximizar el número de trabajos procesados por unidad
de tiempo.

Es deseable maximizar la utilización de la CPU y minimizar los tiempos de retorno,


de espera y de respuesta, todo ello con la mayor justicia para todos los procesos.
Sin embargo, es fácil observar que algunos de estos criterios son contradictorios.
Por ejemplo, para que en los procesos interactivos el tiempo de respuesta sea
bueno, se puede impedir que se ejecuten procesos batch por el día, reservando
para éstos las horas nocturnas en las que no suele haber usuarios en los terminales.
Esto no les sentará muy bien a los usuarios que han encargado trabajos en batch,
pues ven que el tiempo de retorno se incrementa. Como en otros aspectos de la
vida, lo que beneficia a unos, perjudica a otros; así que, en cualquier caso, no habrá
que olvidarse nunca del criterio que hace referencia a la justicia.

En pro de la justicia, en la mayoría de los casos se suelen optimizar los valores


medios, no obstante, bajo algunas circunstancias, es deseable optimizar los
mínimos o los máximos valores, en lugar de la media. Por ejemplo, para garantizar
que todos los usuarios obtienen un buen servicio, lo que se desea es minimizar el
tiempo máximo de respuesta.

También se ha sugerido que, en los sistemas interactivos, es más importante


minimizar la varianza en el tiempo de respuesta, que minimizar su valor medio. Es
preferible un sistema con un tiempo de respuesta razonable y predecible, que otro
que aunque por término medio resulte más rápido, sea altamente variable.

 3ROtWLFDVGH3ODQLILFDFLyQ
En los diagramas de estados de los procesos, vimos que cuando un proceso en
Ejecución abandona tal estado pasa a Espera o a Preparado, dependiendo si deja el
procesador voluntaria o involuntariamente. Si los procesos de un sistema nunca
dejan la CPU de forma involuntaria, se dice que la política de planificación de CPU
es QRH[SXOVRUD o no expropiativa (QRQSUHHPSWLYHVFKHGXOLQJ). Por el contrario, si
pueden perder la posesión del procesador sin solicitarlo, nos encontramos con una
planificación H[SXOVRUD o expropiativa (SUHHPSWLYHVFKHGXOLQJ).

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

Las políticas no expulsoras suelen aplicarse en sistemas batch o en pequeños


entornos monousuario, como el sistema operativo MS-DOS o el entorno
Windows-95/98, ambos de Microsoft. Algunos algoritmos que se emplean en esta
planificación son SULPHUR HQ OOHJDU  SULPHUR HQ VHUYLU, HO PiV FRUWR SULPHUR, y SRU
SULRULGDGHV.

Por otra parte, los algoritmos expropiativos se utilizan en sistemas de control


industrial y entornos multiusuario. Los algoritmos más utilizados aquí incluyen
5RXQG5RELQ, SRUSULRULGDGHV, de P~OWLSOHVFRODV y de P~OWLSOHVFRODVUHDOLPHQWDGDV
(como es el caso de Unix y Windows/NT).

Veamos a continuación algunos comentarios sobre estos algoritmos.

• 3ULPHURHQOOHJDU3ULPHURHQVHUYLU
Este algoritmo, cuyo nombre abreviaremos con FCFS ()LUVW&RPH)LUVW6HUYHG),
es, con mucho, el algoritmo más simple. Según este esquema, el primer proceso
que reclama la CPU, es el primero en conseguirla. En cuanto a la
implementación, no merece la pena dar muchas explicaciones, pues basta con
mantener, por ejemplo, una cola FIFO de los descriptores de los procesos que
van solicitando la CPU.

El tiempo medio de espera es muy variable y raramente es el mínimo. En el


gráfico superior de la Figura 14 se muestra un caso que según ese orden de
llegada, resulta un tiempo medio de espera de 17 ms. En cambio, si el orden de
llegada de los trabajos o procesos hubiera sido T2, T3, T1, se puede comprobar
que habría dado lugar a un tiempo medio de espera de 3 ms.

Otro problema que puede aparecer al aplicar este algoritmo se da en el siguiente


escenario. Supongamos un sistema con esta política FCFS en el que hay un
proceso (Proceso-CPU) que consume grandes porciones de CPU y realiza pocas
operaciones de E/S. Por el contrario, hay muchos otros procesos con un ciclo en
el que realizan frecuentes operaciones de E/S y ejecutan muy pocas
instrucciones sobre la CPU (Procesos-E/S). Mientras el Proceso-CPU se ejecuta,
los Procesos-E/S acabarán sus operaciones con los dispositivos periféricos y
pasarán a la cola Preparados, quedando libres los dispositivos de E/S. En algún
momento, el Proceso-CPU acabará por realizar una operación de E/S y pasará a
Espera. Los Procesos-E/S, que necesitan poca CPU se ejecutan rápidamente y
vuelven a las colas de espera de los periféricos de E/S. La CPU queda ociosa. El
Proceso-CPU finaliza la operación de E/S y vuelve a tomar control del
procesador. Otra vez, los Procesos-E/S terminarán sus operaciones de E/S y
pasarán a Preparados, esperando todos a que el Proceso-CPU realice otra
operación de E/S. Cuando se produce esta situación, en la que muchos procesos
que necesitan poca CPU esperan por uno que consume mucho tiempo de
procesador, se denomina HIHFWR FRQYR\. Este efecto provoca una menor
utilización de la CPU y de los periféricos de la que se podría conseguir si se
concediese la posesión de la CPU a los procesos que requieren poco tiempo de
procesador, en lugar de cedérselo a un proceso que va a hacer esperar mucho
tiempo a muchos procesos.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

6LVWHPDV0XOWLSURFHVR 3ROtWLFDVGH3ODQLILFDFLyQ
(O3ULPHURHQ/OHJDU3ULPHURHQ6HUYLU
J(VVLPSOH
L7LHPSRGHHVSHUDYDULDEOH5DUDPHQWHHOPtQLPR
L'HVDSURYHFKDORVGLVSRVLWLYRVGH(6
7UDEDMR 7LHPSRQHFHVDULR
 
 
 

7UDEDMR 7U 7U


   

7LHPSRPHGLRGHHVSHUD    

(O0iV&RUWRHO3ULPHUR
J2IUHFHVLHPSUHHOPtQLPRWLHPSRPHGLRGHHVSHUD

7UDEDMR 7LHPSRQHFHVDULR
 
 
 

7U 7U 7UDEDMR


   

7LHPSRPHGLRGHHVSHUD    

J(VySWLPR
L¢&XiOHVODQHFHVLGDGUHDOGHOWUDEDMR"
0iVXWLOL]DGRHQODSODQLILFDFLyQDODUJRSOD]R

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

• (OPiVFRUWRSULPHUR
Este algoritmo, también conocido como SJF (6KRUWHVW-RE)LUVW), concede la CPU
al proceso que durante menos tiempo necesita la CPU de forma ininterrumpida.
Debe quedar claro que se selecciona el que menor porción de tiempo de CPU
necesita en el momento de hacer la elección, no aquel cuyo tiempo total de CPU
es el menor de todos. Si dos procesos necesitan el procesador durante el mismo
tiempo, se elige uno mediante FCFS.

En la parte inferior de la Figura 14 tenemos la aplicación de este algoritmo a


nuestro ejemplo. Como se puede ver, consigue el menor tiempo de espera
posible. Poniendo a un proceso corto por delante de uno largo, se decrementa el
tiempo de espera del corto más de lo que se incrementa el del largo.
Consecuentemente, el tiempo medio decrece.

Este algoritmo, probablemente el óptimo, se comporta bien en la planificación a


largo plazo de los procesos batch, en los que se conocen los requisitos de tiempo
total de CPU indicados por el usuario; pero en la planificación de la CPU se
presenta el problema de que en el momento en que hay que asignar la CPU, no
hay forma de conocer a priori la porción de tiempo que necesita cada proceso,
por lo que es difícilmente implementable. Se puede intentar una aproximación al
SJF puro, haciendo una estimación de la porción necesaria basándose en
estadísticas de ejecuciones anteriores.

• 5RXQG5RELQ
Este algoritmo está especialmente diseñado para los sistemas multiusuario de
tiempo compartido. Es similar al FCFS, pero se le añade la expropiación de la
CPU cuando la posesión continuada de ésta sobrepasa un tiempo preestablecido
(SRUFLyQGHWLHPSR), que suele ser del orden de 10 a 100 milisegundos.

La implementación de este algoritmo se realiza con una cola FIFO para los
procesos Preparados. Los nuevos procesos se añaden al final de la cola. Cuando
el planificador tiene que seleccionar un proceso, elige el primero de la cola,
establece una temporización correspondiente a la porción de tiempo, y le cede el
control. Ahora pueden ocurrir dos cosas:

1ª) El proceso termina o realiza una operación de E/S antes de que se acabe su
porción de tiempo, en cuyo caso cede la CPU voluntariamente, pasa a espera
y el planificador selecciona el siguiente proceso de la cola Preparados.

2ª) El proceso se ejecuta hasta dar lugar a que venza la temporización


establecida. En este caso, el dispositivo temporizador genera una interrupción
que captura el sistema operativo, dando lugar a que se le expropie la CPU al
proceso en ejecución, que pasa al final de la cola Preparados, y a que el
planificador seleccione al primer proceso de la cola para pasar a ejecución.

Ya habíamos comentado anteriormente la falta de eficiencia que pueden causar


los cambios continuos del proceso en ejecución. En el caso de que la política de
planificación del procesador sea por Prioridades de los procesos, se nos hace
imposible establecer la frecuencia de los cambios, pues vendrá impuesta por los

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

6LVWHPDV0XOWLSURFHVR  3ROtWLFDVGH3ODQLILFDFLyQ
7LHPSR&RPSDUWLGR 5RXQG5RELQ

3URFHVR

3URFHVR

3URFHVR

3URFHVRHQHMHFXFLyQ
3URFHVRHQHVSHUD
7UDEDMR 7LHPSRQHFHVDULR
 
 
 

7U 7U7U 7U 7U 7U 7U

       

(O5HQGLPLHQWRGHO6LVWHPD
'HSHQGHGHOD3RUFLyQGH7LHPSR

3HTXHxD *UDQGH

£ 
6RORKD\
FDPELRVGHFRQWH[WR )&)6

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

fenómenos externos que afectan a los procesos correspondientes. En cambio, si


la planificación es por Tiempo Compartido, sí está en nuestra mano establecer
porciones de tiempo de CPU muy grandes, tal que en una unidad de tiempo se
produzcan muy pocos cambios del proceso en ejecución. Sin embargo, esto no
resulta tan fácil de decidir, pues si la porción de tiempo se hace muy grande, el
aprovechamiento de la CPU por parte del usuario se ve altamente incrementada,
pero, por contra, también ocurrirá que los usuarios de los terminales notarán que
“se les tarda mucho en atender”, pues se ha degenerado en una política FCFS.
Por el contrario, si la porción se hace muy pequeña, se les atiende enseguida,
pero durante muy poco tiempo, con la consiguiente degradación en el
aprovechamiento de la CPU por parte del usuario.

• 3RU3ULRULGDGHV
Uno de los factores a tener en cuenta en la planificación de los procesos es la
justicia, pero justicia no siempre significa un reparto a partes iguales, sino la
asignación más conveniente que aconsejen las circunstancias. Así, por ejemplo,
tenemos que en un programa de control y supervisión de una central nuclear,
compuesto por diversos procesos, no se debe esperar que todos los procesos
cuenten inexcusablemente con la misma porción de tiempo de CPU, ni según un
turno circular. Evidentemente, no es tan importante el proceso interactivo que se
encarga de mostrar a los visitantes un vídeo de la historia de la central y
responder a sus preguntas, como los procesos encargados de supervisar los
niveles de presión, temperatura, radioactividad, etc., y que en caso de que se
sobrepasen los límites de seguridad activan las válvulas y avisos
correspondientes.

Los criterios para establecer las prioridades pueden ser variados: por razones
físicas, como en el caso de los sistemas de tiempo real, por rangos o categorías
de los usuarios (sistemas multiusuarios), por factores políticos, etc. En sistemas
multiusuario, es muy común tener prioridades por grupos de trabajo o
departamentos, y a los procesos de la misma prioridad aplicarles una política
reparto de tiempo compartido.

Este algoritmo puede ser H[SXOVRU o QR H[SXOVRU. Cuando un proceso llega a la
cola Preparados, se compara su prioridad con la del proceso en ejecución. Con
un algoritmo expulsor por prioridades, si la prioridad del recién llegado es mayor
que la del proceso en ejecución, se le expropia la CPU a este último en favor del
recién llegado. Si la política es no expulsora, simplemente se coloca al nuevo
proceso a la cabeza de la cola. El algoritmo SJF es un caso particular de este
algoritmo con política no expulsora, donde la prioridad es la inversa del tiempo
requerido de CPU.

Un problema que se puede dar con este tipo de algoritmos de planificación es el


bloqueo indefinido o LQDQLFLyQ. Un proceso que está preparado, pero que no
adquiere la CPU, se puede decir que está bloqueado en espera de procesador.
Pues bien, con un algoritmo por prioridades puede darse el caso de dejar a un
proceso de baja prioridad esperando indefinidamente por el procesador si
siempre hay algún proceso de alta prioridad que puede “colarse”. (Existe el rumor
de que cuando en 1973 se paro el sistema IBM 7094 del Instituto Tecnológico de

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

Massachusetts para sustituirlo por otro, se encontró un proceso que llevaba en


espera de CPU desde que se había creado en 1967 y todavía no había podido
llegar a ejecutarse por primera vez.)

Una primera aproximación al problema de la inanición en sistemas con


prioridades, puede consistir en repartir la CPU entre los procesos de la misma
prioridad, es decir, tener una política de tiempo compartido dentro de cada cola
de prioridad.

Una técnica más refinada es la del HQYHMHFLPLHQWR. Con esta sistema, la


prioridad de los procesos preparados se incrementa gradualmente a medida que
esperan en la cola Preparados, donde permanecen hasta alcanzar,
inevitablemente, la prioridad necesaria para conseguir la CPU.

6 LV W H P D V 0 X OW LS U R F H V R  3 R OtW L F D V  G H 3 OD Q L I L F D F Ly Q
3 U L R U LG D G H V  H [ S X OV R U D V

+ / R V  3 U R F H V R V 7 L H Q H Q 3 U L R U LG D G H V

7 R P D H O & R Q W U R OG H OD & 3 8  L Q P H G L D W D P H Q W H


H O3 U R F H V R G H 0 D \ R U 3 U L R U LG D G 

$ 3/,&$ &,2 1 (6

* U X S R V  G H 3 H U V R Q D V 7 LH P S R  5 H D O
F D W H J R U tD V

& X L G D G R F R Q  OD
£ , Q D Q L F Ly Q 
6 2 /8 &,2 1 (6  3 5 R X Q G  5 R E LQ S R U S U L R U LG D G H V

 3  3 U LR U L G D G H V
G LQ i P L F D V
´ ( Q Y H M H F LP LH Q W R µ

6 LV W H P D V 2 S H U D W LY R V , * H V W Ly Q G H 3 U R F H V R V  

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

• 0~OWLSOHVFRODV
Existe otro tipo de algoritmos para situaciones en las que los procesos están
claramente clasificados en diferentes grupos. Por ejemplo, una división muy
común es la que se hace entre procesos interactivos de primer plano (IRUHJURXQG)
y los procesos batch que suelen ejecutarse en un segundo plano (EDFNJURXQG).
Estos dos tipos de procesos tiene distintos requisitos en sus tiempos de
respuesta, por lo que podrían tener distintos algoritmos de planificación cada uno.
Además, los procesos interactivos podrían tener mayor prioridad que los se
ejecutan en un segundo plano.

Para esto, la cola Preparados podría estar dividida en varias colas separadas,
una por cada tipo de proceso. Cada proceso siempre se encola en su cola
correspondiente. Así nos encontramos con que para elegir la cola de la que se va
a sacar un proceso se utiliza una política, mientras que para seleccionar qué
proceso se va a sacar de la cola elegida, se utiliza un algoritmo distinto. También
puede ocurrir que en dos colas distintas se utilice la misma política de
planificación, por ejemplo, tiempo compartido, pero que la porción de tiempo sea
distinta en una cola que en otra.

• 0~OWLSOHVFRODVUHDOLPHQWDGDV
Una mezcla de las técnicas de envejecimiento y de las múltiples colas, resulta en
las múltiples colas realimentadas.

Las distintas colas pueden tener políticas de tiempo compartido, pero las más
prioritarias poseen una mayor porción de tiempo. Cuando un proceso lleva mucho
tiempo en una cola de poca prioridad, se le pasa a otra cola de mayor prioridad,
consiguiendo así mayores porciones de tiempo.

 3ODQLILFDFLyQHQ6LVWHPDV0XOWLSURFHVDGRU
Hasta ahora hemos tratado con algoritmos de asignación de CPU en sistemas con
un solo procesador. Si se dispone de múltiples procesadores, la planificación se
vuelve bastante más compleja.

Al igual que en el caso de los sistemas monoprocesador, no existe la solución


óptima, sino que ésta depende de varios factores, y siempre hay unos procesos
beneficiados y otros perjudicados. Vamos a tratar simplemente algunos conceptos
generales sobre la planificación de múltiples CPU’s. Un estudio en profundidad de
este apartado puede encontrarse en literatura sobre arquitecturas avanzadas y
paralelas.

Cuando todos los procesadores de un sistema tienen la misma funcionalidad


(VLVWHPD KRPRJpQHR), cualquier procesador disponible puede utilizarse para
ejecutar un proceso preparado. Pero si los procesadores son distintos (VLVWHPD
KHWHURJpQHR), sólo los programas compilados para un procesador determinado
pueden ejecutarse en ese procesador. Este es el caso de los sistemas distribuidos.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

3ODQLILFDFLyQ  &RQ0~OWLSOHV3URFHVDGRUHV
/RVSURFHVDGRUHVSXHGHQVHU

',)(5(17(6 ,*8$/(6
6LVWHPD+HWHURJpQHR 6LVWHPD+RPRJpQHR

&DGDSURFHVDGRUWLHQHVX ¢9DULDVFRODV"
SURSLDFROD\VXSURSLD
SODQLILFDFLyQ
L£&DUJDGHVHTXLOLEUDGD

&DGDSURFHVRVHDVLJQDDO
WLSRGHSURFHVDGRUTXHOH
FRUUHVSRQGH 'HEHKDEHUXQD~QLFDFRODGH
SURFHVRV

$XWRSODQLILFDFLyQ 8QSURFHVDGRUKDFHGH
3ODQLILFDGRU\UHSDUWHORV
8QSURFHVDGRUOLEUHVDFD
SURFHVRVHQWUHORV
XQSURFHVRGHODFROD
SURFHVDGRUHV
£&8,'$'2

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

También puede haber algunas limitaciones en la asignación de CPU en los sistemas


homogéneos, como es el caso de un programa que haga uso de un dispositivo
especial de E/S conectado a un bus privado de un procesador concreto, en cuyo
caso solamente se le puede asignar tal CPU para ejecutarse.

En el caso de los sistemas heterogéneos, cada procesador dispone de su propia


cola Preparados y de su planificación particular, y cada proceso entra en la cola del
procesador que le corresponde.

En los sistemas homogéneos se podrían tener también varias colas, una por
procesador, pero esto podría dar lugar a una carga desequilibrada, con algunas
colas con muchos procesos, y otras colas vacías con su CPU ociosa. Para evitar
esto, suele ser mejor disponer de una única cola de procesos preparados. Hay dos
métodos para sacar los procesos de la cola.

• Uno es mediante DXWRSODQLILFDFLyQ, en el que cada procesador que queda libre


saca un proceso de la cola. En este método hay que controlar la posibilidad de
que dos procesadores intenten sacar simultáneamente al mismo proceso de la
cola.

• El otro sistema es dedicando un procesador a trabajar como planificador, siendo


él el único que saca procesos de la cola y los asigna entre los procesadores,
evitando así los conflictos del método anterior.

 /D3ODQLILFDFLyQGH$OJXQRV6LVWHPDV2SHUDWLYRV
Aunque los conceptos de sistemas operativos pueden estudiarse o considerarse en
términos puramente teóricos, resulta conveniente observar cómo se han
implementado algunos sistemas operativos comerciales. Así, ahora vamos a
comentar, aunque solo sea superficialmente, algunas de las decisiones de diseño
que se han tomado sobre la planificación de la CPU en Unix, Linux y Windows NT.

8QL[
La primera versión de Unix se desarrolló en 1969 por Ken Thompson en los
Laboratorios Bell. Muchas versiones y emulaciones se han hecho desde entonces.

Una de las últimas versiones de Unix es la 6\VWHP9UHOHDVH, aparecida en 1989.


No obstante, muchos otros fabricantes también han desarrollado sus propias
versiones de este popular sistema operativo. Así tenemos las versiones de IBM
(AIX), HP (HP-UX), Sun (Sun OS y Solaris), DEC (Ultrix) y algunas más. También se
han desarrollado versiones para PC’s, como la de Microsoft (Xenix), la de Santa
Cruz y la de Tanenbaum (Minix), y para las más variadas plataformas, desde el
Macintosh de Apple hasta supercomputadores como el Cray II.

Una de las versiones de Unix más populares ha sido la desarrollada en la


Universidad de Berkeley por el Departamento de Defensa norteamericano para

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

utilizarlo en instalaciones gubernamentales. Esta versión fue la 4BSD, siendo


concretamente la 4.3BSD (año 1986) una de las que tuvieron más auge de
utilización.

También merece especial mención Linux, que desde mediados de los años 90 ha
tenido una espectacular difusión, y al que le trataremos en un apartado particular.

Ahora vamos a comentar aquí la política de planificación del procesador utilizada por
Unix en su versión 4.3BSD.

Ya que se trata de un sistema operativo multiusuario, la planificación de Unix está


diseñada para favorecer a los procesos interactivos, para lo cual utiliza una
planificación por prioridades, cediendo la CPU en porciones de tiempo al proceso
más prioritario. El algoritmo concreto es el de “colas multinivel con realimentación
(mediante envejecimiento)”, de tal manera que para procesos con gran carga de
CPU el algoritmo de planificación se convierte en URXQGURELQ. Veámoslo con cierto
detalle.

Las porciones de tiempo son de 100 ms., por lo que cuando un proceso llega al
término de su porción sin haber cedido la CPU, se le quita la CPU para cedérsela al
proceso preparado con mayor prioridad. Las prioridades, no obstante, son
dinámicas, es decir, que la prioridad de un proceso va variando a lo largo de su
vida. Aunque las porciones de tiempo son de 100 ms. la reevaluación de las
prioridades se realiza una vez por segundo, para que no suponga una sobrecarga
considerable. Esto quiere decir que cada segundo se aplica el algoritmo que
establece la prioridad de cada proceso. El algoritmo en cuestión es el siguiente:

&38 (L − 1)
3 (L) = %DVH +
M M
M
+ QLFH M
2

8 (L) &38 (L − 1)
&38 = +
M M

M
2 2

donde

3 L 
M Prioridad del proceso M al comienzo del intervalo L. Cuanto menor
sea este número, mayor es la prioridad.

%DVH M Prioridad de base del proceso M.

&38  L = Media ponderada exponencial de la utilización de CPU del proceso M


M

en el intervalo L.

8M L = Utilización de CPU del proceso M en el intervalo L.

QLFH M = Factor de ajuste controlado por el usuario.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

El propósito de la prioridad base es establecer bandas fijas de prioridades, de tal


manera que a los distintos tipos de procesos se les asigna una prioridad base
predeterminada. Las bandas son, en orden de prioridad decreciente, para los
siguientes tipos de procesos:

• Procesos de intercambio a disco


• Procesos de dispositivos de E/S por bloques
• Procesos de gestión de archivos Prioridad
• Procesos de dispositivos de E/S de caracteres
• Procesos de usuario

Esta jerarquía tiende a proporcionar un aprovechamiento más eficiente de los


dispositivos de E/S. Obsérvese que dentro de los procesos de usuario, también se
tiende a penalizar a los procesos que consumen mucha CPU, pues cuanto más
procesador consume un proceso, menor se hace su prioridad, lo cual termina
favoreciendo a los procesos que más utilizan los dispositivos de E/S. Esto tiende a
evitar el “efecto convoy” comentado en la planificación FIFO. También debemos
observar que ya que se tiene en cuenta (y se penaliza) el consumo de CPU que ha
realizado cada proceso en su anterior posesión del procesador, este algoritmo
también se comporta como una aproximación a “el más corto - el primero”, el cual ya
vimos que ofrece los mejores tiempos medios de espera.

Ya que a todos los procesos de usuario se les asigna la misma prioridad base, para
este tipo de procesos el algoritmo se comporta como si fuera 5RXQG5RELQ (aunque
como ya hemos dicho, favoreciendo a los procesos con poca carga de CPU), no
obstante, se puede favorecer en cierta manera a algunos procesos de usuario
mediante el comando nice.

Debido a este algoritmo con envejecimiento, resulta muy difícil que un proceso
pueda apropiarse de forma exclusiva de la CPU evitando así la “inanición” de los
procesos.

/LQX[
Linux es otro sistema operativo “tipo Unix” en el que aunque la compatibilidad con
Unix era uno de los objetivos de diseño, tiene algunas particularidades que difieren
del Unix estándar, entre ellas la política de planificación de procesos, y dada la
popularidad que ha adquirido en estos últimos años, merece la pena comentarlo.

El desarrollo de Linux comenzó en 1991, cuando el estudiante finlandés Linus


Torvalds escribió un pequeño kernel o núcleo para el procesador 80386 de Intel y lo
puso a disposición de todo el mundo de forma gratuita a través de Internet. Este
núcleo implementaba un subconjunto de la funcionalidad de Unix, pero fue
creciendo hasta adquirir la funcionalidad completa.

Alrededor del único NHUQHO GH /LQX[ se han construido multitud de programas que
constituyen el VLVWHPD /LQX[. Alguno de estos programas se ha construido

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

expresamente para Linux, mientras que otros se han tomado prestados de otros
sistemas o proyectos de colaboración.

Una GLVWULEXFLyQGH/LQX[ consta de los componentes estándar de un sistema Linux,


más un conjunto de herramientas de administración que ayudan a simplificar la
instalación y mantenimiento del sistema. Aunque la primera distribución de Linux fue
la de SLS y la de Slackware posiblemente la más popular, hoy día hay numerosas
distribuciones, entre ellas: Red Hat, Debian, Caldera, Craftworks, SuSE, Unifix, etc.
No obstante, debe tenerse en cuenta que todas ellas comparten el mismo kernel, y
los paquetes o utilidades que ofrece cada una suelen ser compatibles con las
demás distribuciones.

En este apartado vamos a centrarnos en los aspectos de planificación del kernel de


Linux en su versión 2.0. Aunque este sistema operativo está preparado para
sistemas multiprocesador, aquí vamos a comentar únicamente la planificación con
un único procesador.

Linux dispone de dos algoritmos de planificación (dos clases de planificación). Uno


es el de tiempo compartido, para un reparto justo entre múltiples procesos; el otro
está diseñado para tareas de tiempo real, donde las prioridades son más
importantes que la justicia. Cada proceso puede acogerse a cualquiera de estas
políticas de planificación, y así consta en su BCP.

Para los procesos de tiempo compartido, se utiliza un algoritmo basado en créditos.


Cada proceso dispone de un cierto número de créditos, de tal manera que para
seleccionar la siguiente tarea a ejecutarse, se elige a la que tiene más créditos
acumulados. Cada vez que se produce una interrupción de reloj, el proceso en
ejecución pierde un crédito; cuando su número de créditos llega a cero, pierde la
CPU y se selecciona otro proceso.

Cuando no queda ningún proceso preparado con créditos, Linux vuelve a asignar
créditos a todos los procesos del sistema según la regla siguiente:

FUpGLWRV UHVWDQWHV
&UpGLWRV = + SULRULGDG
2

Este algoritmo considera dos factores: la historia del proceso y su prioridad. Se


mantiene la mitad de los créditos de los que el proceso conserva desde la última
asignación de créditos, recordando así parte de la historia del comportamiento más
reciente del proceso. Los procesos con mucha carga de CPU tienden a gastar sus
créditos rápidamente, mientras que los que suelen estar suspendidos en espera de
operaciones de E/S van acumulando créditos a lo largo de la historia, de tal manera
que cuando estén en espera de la CPU tendrán más prioridad para ejecutarse, lo
cual produce un tiempo de respuesta muy rápido para los procesos interactivos.

El factor prioridad en el cálculo de la asignación de créditos permite afinar la


prioridad real de los procesos. Al asignar una baja prioridad a algunos procesos en
EDFNJURXQG, automáticamente recibirán menos créditos que los procesos

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

interactivos, y también recibirán un menor porcentaje de CPU que trabajos similares


a los que se les haya asignado una mayor prioridad.

Gracias a este sistema de prioridades dinámicas, procesos de distintas prioridades


pueden competir por el procesador, evitando así el problema de la inanición.

Para tiempo real, Linux implementa los dos algoritmos expulsores o expropiativos
requeridos por POSIX: ),)2 y 5RXQG5RELQ. Esto quiere decir que la información de
cada proceso debe indicar si se acoge a planificación de tiempo compartido o de
tiempo real, y en caso de ser este último, debe especificar si utiliza el algoritmo ),)2
o 5RXQG5RELQ. Veamos cómo se comporta la planificación con cada uno de estos
dos algoritmos de tiempo real.

El algoritmo FIFO es el ya conocido para tiempo real, en el que la CPU siempre se


le concede al proceso con mayor prioridad, y no se le quita a no ser que pase a
preparado un proceso de mayor prioridad. En cambio, los procesos de la misma
prioridad con planificación 5RXQG5RELQ, se reparten la CPU en idénticas porciones
de tiempo por turno rotatorio.

Cuando se le solicita al planificador que elija un proceso para pasar a ejecución,


busca en la cola de los preparados, eligiendo siempre a un proceso de tiempo real
antes que a los de tiempo compartido.

Cuando finaliza una rodaja de tiempo, se comprueba la política de planificación del


proceso en ejecución. Si es de tiempo compartido se decrementa un crédito, y si ha
llegado a cero, se le quita la CPU, cediéndosela al proceso preparado con más
créditos. Si es GHWLHPSRUHDO),)2FRQWLQ~DFRQODHMHFXFLyQ6LHVGHWLHPSRUHDO
FRQ 5RXQG Robin y no hay más procesos preparados de la misma prioridad,
continua la ejecución, pero si hay otros procesos preparados de la misma prioridad,
se le quita la CPU, se le envía a la cola de preparados, y se le asigna la CPU al
siguiente proceso preparado de esa prioridad.

Ya que la planificación, en cualquier caso, es expulsora, si un proceso pasa a


preparado y es de mayor prioridad que el actual proceso en ejecución, se le asigna
la CPU inmediatamente al proceso de mayor prioridad.

:LQGRZV17
Este sistema operativo nació como consecuencia de que Microsoft decidió construir
un nuevo sistema operativo partiendo de cero y con una Nueva Tecnología, que
soportara las aplicaciones tanto para OS/2 como para POSIX, Windows y MS-DOS.

Hay dos versiones de Windows NT: ZRUNVWDWLRQ (puesto de trabajo para un usuario)
y VHUYHU (utilizado como servidor para aplicaciones cliente-servidor), sin embargo
ambas utilizan el mismo núcleo, por lo que las características de planificación que
comentaremos aquí se aplican a las dos versiones.

Aunque Windows NT dispone de un sistema de protección para múltiples usuarios,


no debe entenderse como un sistema multiusuario, pues no permite múltiples

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

usuarios conectados simultáneamente (aunque sí permita múltiples accesos


simultáneos como servidor).

La arquitectura de Windows NT está basada en un PLFURNHUQHO (como Mach), de tal


forma que facilita las actualizaciones de partes del sistema operativo sin afectar al
resto. Y como es típico en los sistemas basados en PLFURNHUQHOV, dispone de
procesos o tareas y de procesos ligeros o WKUHDGV. Las notas sobre planificación que
vamos a comentar se aplican tanto a los procesos como a los WKUHDGV de Windows
NT en su versión 4.0

Los objetivos de planificación de Windows NT son diferentes a los de Unix. Éste


está pensado para dar servicio equitativo y rápido a todos los usuarios del sistema,
mientras que Windows NT se diseñó para ser especialmente sensible a las
necesidades de un único usuario en un entorno muy interactivo o en el papel de un
servidor.

El planificador utiliza un esquema de 32 prioridades para determinar el orden de


ejecución. Las prioridades están divididas en dos clases: la FODVH YDULDEOH (con las
prioridades 0 a 15) y la FODVH GH WLHPSR UHDO (con las prioridades 16 a 31). Un
número más alto indica una mayor prioridad. Cuando se crea un proceso se le
asigna una “prioridad base”. Las prioridades de la clase variable se suelen utilizar
para los procesos de usuario, mientras que las de tiempo real se reservan para las
actividades derivadas del servidor.

Se utiliza una cola para cada una de estas prioridades, de tal manera que para
elegir el siguiente proceso que pasa a ejecución, se recorren todas las colas, de
mayor a menor prioridad hasta que se encuentra un proceso preparado. Si no hay
ningún proceso preparado, se le cede el control al WKUHDGRFLRVR.

La CPU siempre se asigna al proceso de mayor prioridad, aunque las prioridades se


gestionan de forma diferente en las dos clases. En la clase de tiempo real todos los
procesos mantienen su prioridad base, que no cambia nunca, y la CPU se reparte
por turno rotatorio entre todos los procesos de la máxima prioridad que tenga
procesos preparados. En la clase de prioridad variable, la prioridad de un proceso
parte de su prioridad base y puede subir o bajar (dentro de un cierto margen) a lo
largo de la vida del proceso. Así, por cada nivel de prioridad hay una cola FIFO, pero
un proceso puede pasar a la cola de otra prioridad dentro de las de prioridad
variable. La prioridad de un proceso no puede cambiar a la prioridad de otra clase
distinta a la que pertenece.

Cuando a un proceso en ejecución se le acaba su porción de tiempo, si pertenece a


la clase de tiempo real y hay más procesos preparados de la misma prioridad, se le
quita la CPU y pasa al final de la cola de su prioridad, cediendo la CPU al siguiente
proceso de esa cola. Si la prioridad del proceso pertenece a la clase variable, se le
disminuye la prioridad (hasta cierto límite). De esta manera se tiende a limitar el alto
consumo de CPU que realizan los procesos de cálculo (con poca E/S); por otra
parte, cuando un proceso que estaba en espera pasa a preparado, se le aumenta su
prioridad. Este aumento depende del motivo por el que estaba esperando. Los
procesos que esperan por una interrupción de teclado o de ratón son los que

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

consiguen un mayor aumento de prioridad, mientras que los que esperan por la
conclusión de una operación con el disco sólo consiguen un ligero aumento. Esta
estrategia tiende a ofrecer un buen tiempo de respuesta a los procesos interactivos
elevando la prioridad a los procesos que esperan por teclado o ratón, dejando que
los procesos con gran carga de procesador se ejecuten en tiempos muertos en
segundo plano.

Aunque esta planificación expulsora siempre le otorga el procesador al proceso


preparado con mayor prioridad, no se puede decir que Windows NT sea un sistema
operativo de tiempo real estricto, pues no garantiza el tiempo mínimo que se tarda
en darle control a un proceso de alta prioridad que pasa a preparado.

3ODQLILFDFLyQHQ6LVWHPDV0XOWLSURFHVDGRUHV
Cuando Windows NT se ejecuta con un único procesador, el proceso preparado de
mayor prioridad siempre está en ejecución, y si hay más de un proceso con la mayor
prioridad, la CPU se reparte por turno rotatorio entre todos los procesos preparados
de esa prioridad.

En un sistema multiprocesador con 1 procesadores, siempre se mantiene en


ejecución a los 1 procesos de mayor prioridad, ejecutándose de manera exclusiva
en los 1 procesadores. El resto de los procesos de menor prioridad comparten el
único procesador que queda.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

&RPXQLFDFLyQ\6LQFURQL]DFLyQHQWUH3URFHVRV
Normalmente, los procesos que cooperan en la realización de un trabajo,
necesitarán durante su ciclo de vida la sincronización o comunicación entre ellos o
con el proceso maestro que les haya creado. Se hace necesario algún sistema de
comunicación cuando se requiere el intercambio de información entre dos o más
procesos. Por ejemplo, un proceso puede estar produciendo una serie de datos que
sirven a su vez de entrada a otro proceso (como ocurre con el SLSH “|” de Unix). En
otros casos, como en los momentos en que se accede a estructuras de datos
comunes a varios procesos, se requiere algún mecanismo de sincronización para
que dos procesos no intenten acceder simultáneamente a dicha estructura para
realizar operaciones de lectura/escritura de forma incontrolada.

Así pues, en los siguientes apartados trataremos los problemas que pueden darse y
algunos mecanismos propuestos para comunicación y sincronización entre procesos
cooperantes que comparten el mismo espacio lógico de direcciones.

 &RQGLFLyQGH&DUUHUD
Como ya hemos dicho anteriormente, un sistema puede estar compuesto de
múltiples procesos que se pueden ejecutar en paralelo (simultáneamente) si se
dispone de múltiples procesadores. Cuando solamente se dispone de una CPU, el
efecto del procesamiento paralelo puede simularse si los procesos se ejecutan
turnándose en la posesión del procesador. En otras palabras, el procesador puede
compartirse entre varios procesos. Incluso simulando el procesamiento paralelo,
resulta útil ver cada proceso como si tuviera su propio procesador virtual dedicado.

Muchas de las dificultades que surgen en el verdadero procesamiento paralelo


también se producen en el caso simulado. Uno de estos problemas se muestra en el
ejemplo del programa superior de la Figura 18.

El primer proceso, el 2EVHUYDGRU, es el responsable de observar y contar eventos. El


segundo proceso, el 5HSRUWHUR, ocasionalmente imprime informes sobre el número
de eventos observados hasta el momento y seguidamente pone el &RQWDGRU de
eventos a cero. Cuando ambos procesos se ejecutan concurrentemente, puede
producirse la siguiente situación. Supongamos que el reportero acaba de imprimir un
10, y antes de que pueda poner el &RQWDGRU a cero se produce la interrupción de
tiempo y se concede el control de la CPU al 2EVHUYDGRU. Éste, entonces, incrementa
el &RQWDGRU a 11. A continuación, el 5HSRUWHUR vuelve a tomar posesión del
procesador y pone, por fin, el &RQWDGRU a cero, lo cual significa que un evento queda
sin ser contabilizado. En general, el 5HSRUWHUR puede fallar al informar de cualquier
número de eventos, ya que el incremento de la variable &RQWDGRU puede producirse
entre la impresión del valor, y su puesta a cero.

En este ejemplo se muestra que los resultados son impredecibles, ya que los
procesos se ejecutan con velocidades relativas igualmente impredecibles. Cuando el
resultado de un cálculo depende de las velocidades relativas de los procesos, se
dice que hay una FRQGLFLyQ GH FDUUHUD. Las condiciones de carrera se producen

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

cuando procesos paralelos comparten datos o recursos, como la variable &RQWDGRU


del ejemplo.

6LQFURQL]DFLyQGH3URFHVRV
SURJUDP Cuenta_Eventos;
Contador : INTEGER := 0;
SURFHVV Observador;
UHSHDW
Esperar_Evento;
Contador := Contador + 1;
IRUHYHU;

£ 6HSLHUGHQ

HQG Observador;

SURFHVV Reportero;
UHSHDW HYHQWRV
Imprimir (Contador);
Contador := o;
IRUHYHU;
HQG Reportero;
EHJLQ
Observador;
Reportero;
HQG Cuenta_Eventos;

6HSURGXFHXQD&RQGLFLyQGH&DUUHUD 62/8&,Ð1

(ODFFHVRDODYDULDEOHQRHVDWyPLFR
3URSRUFLRQDU
process Observador; ([FOXVLyQ0XWXD
repeat
Esperar_Evento; 5HJLyQ&UtWLFD
(QWUDUB5&;
Contador := Contador + 1;
6DOLUB5&;
forever;
end Observador;

process Reportero;
repeat
(QWUDUB5&;
Imprimir (Contador);
Contador := 0;
6DOLUB5&;
forever;
end Reportero;

6LVWHP DV2SHUDWLY RV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

 ([FOXVLyQ0XWXD\5HJLyQ&UtWLFD
¿Cómo evitar las condiciones de carrera? La clave para evitar problemas en las
situaciones en que se comparten objetos o recursos es encontrar algún modo de
prohibir que haya más de un proceso leyendo o escribiendo el dato compartido al
mismo tiempo. En otras palabras, lo que se necesita es H[FOXVLyQPXWXD, es decir,
asegurarse de que si un proceso está accediendo a un dato compartido, el otro o los
otros procesos estarán imposibilitados para poder hacerlo también.

El problema de nuestro ejemplo es que el proceso 2EVHUYDGRU puede acceder al


&RQWDGRU (lo incrementa) antes de que el 5HSRUWHUR haya acabado de realizar las
operaciones con la variable (le faltaba ponerla a cero), o sea, se accede a la variable
antes de que el proceso que la está modificando la deje en un HVWDGRFRQVLVWHQWH.
Y este problema se produce porque las operaciones de acceso a la variable
compartida no son indivisibles en su ejecución, es decir, porque no son DWyPLFDV o
ininterrumpibles.

En la vida de un proceso, parte del tiempo está ocupado con cálculos internos y otro
tipo de cosas que no conducen a una condición de carrera. Pero en otras ocasiones,
el proceso accede a variables o ficheros compartidos que sí puede conducir a la
condición de carrera. La parte del programa en la que se accede a memoria
compartida se denomina UHJLyQFUtWLFD. Si nos las pudiéramos apañar para que dos
procesos no estuvieran al mismo tiempo en su región crítica, evitaríamos las
condiciones de carrera.

 ,PSOHPHQWDFLyQGHOD([FOXVLyQ0XWXD
En la parte inferior de la Figura 18 tenemos nuestro ejemplo transformado de
acuerdo a la premisa del acceso a la región crítica. En este programa transformado
se muestra una solución abstracta al problema de la exclusión mutua. Podemos ver
que la región crítica de cada proceso está encerrada entre sendas sentencias de
entrada y salida de la región ((QWUDUB5& y 6DOLUB5&). Estaría bien que estas
primitivas de acceso a la región crítica se comportasen como “porteros”, tal que si un
proceso intentara entrar en la región crítica ocupada por otro proceso, el primero
quedara bloqueado por el portero hasta que el otro proceso saliese de ella.

 &RQGLFLRQHVSDUDHO6LQFURQLVPRGH3URFHVRV&RRSHUDQWHV
Aunque con la exclusión mutua se evitan las condiciones de carrera, no es condición
suficiente para que una serie de procesos paralelos cooperen correcta y
eficientemente utilizando datos compartidos. Se requieren las siguientes condiciones
para obtener una buena solución:

1. Dos procesos no pueden estar al mismo tiempo dentro de la misma región


crítica.
2. No se deben hacer suposiciones sobre el hardware, es decir, sobre la
velocidad o el número de procesadores que intervienen.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

3. No se deben hacer suposiciones sobre las velocidades relativas de los


procesos.
4. Ningún proceso fuera de su región crítica puede bloquear a otros procesos.
5. Ningún proceso deberá esperar eternamente a entrar en su región crítica.
6. No se debe posponer indefinidamente la decisión de cuál es el siguiente
proceso que debe entrar.

 0HFDQLVPRV
En este apartado examinaremos, tanto a nivel hardware como software, algunos de
los muy diversos métodos de sincronización que existen para resolver el problema
de la exclusión mutua. Estos son:

• Inhibición de interrupciones
• Espera activa
• Semáforos
• Monitores

Sobre las versiones originales de algunos de estos mecanismos (semáforos y


monitores) ha habido diversas modificaciones a lo largo de la historia, por ello aquí
vamos a comentar los conceptos básicos o el cometido fundamental que se busca
con cada uno de ellos, pues su estudio en profundidad sería más propio de un curso
de Programación Concurrente. No obstante, en la bibliografía se citan algunos libros
en los que se puede encontrar información adicional sobre este tema.

,QKLELFLyQGH,QWHUUXSFLRQHV
Si se pretende evitar que mientras un proceso está dentro de una región crítica, otro
proceso pueda entrar también, la forma más fácil de conseguirlo es evitando que el
proceso que se encuentra dentro de la región pierda el control del procesador antes
de abandonarla.

Un proceso pierde la posesión de la CPU solamente si realiza una operación de E/S


o, si debido a una interrupción, se detecta que se le acabó su porción de tiempo o
que otro proceso de mayor prioridad pasa a Preparado y seguidamente se apropia
de la CPU. Sabido esto, por una parte se puede hacer que un proceso que se
encuentra dentro de una región crítica no realice operaciones de E/S, y por otra
parte lo que falta por hacer es inhibir la aceptación de interrupciones por parte del
procesador mientras el proceso está dentro de la región. Es decir, que las
sentencias Entrar_RC y Salir_RC se podrían corresponder, respectivamente, con
las instrucciones máquina DISABLE (inhibir aceptación de interrupciones) y ENABLE
(aceptar interrupciones). Así, una vez que un proceso ha inhibido las interrupciones,
puede examinar y actualizar la memoria compartida sin miedo a que otro proceso
intervenga, pues la secuencia de instrucciones que componen la región crítica se
convierte en una única operación atómica o indivisible.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

6LQFURQL]DFLyQ ([FOXVLyQ0XWXD
5HTXLVLWRVSDUDVX,PSOHPHQWDFLyQ

'RVSURFHVRVQRSXHGHQHVWDUDOPLVPRWLHPSRGHQWUR
+ GHODPLVPDUHJLyQFUtWLFD

1RVHGHEHQKDFHUVXSRVLFLRQHVVREUHHOKDUGZDUH
+ YHORFLGDGRQžGHSURFHVDGRUHV 

1RVHGHEHQKDFHUVXSRVLFLRQHVVREUHODYHORFLGDGHV
+ UHODWLYDVGHORVSURFHVRV

1LQJ~QSURFHVRIXHUDGHVXUHJLyQFUtWLFDSXHGH
+ EORTXHDUDRWURVSURFHVRV

1LQJ~QSURFHVRGHEHUiHVSHUDUHWHUQDPHQWHDHQWUDU
+ HQVXUHJLyQFUtWLFD

1RVHGHEHSRVSRQHULQGHILQLGDPHQWHODGHFLVLyQGH
+ FXiOHVHOVLJXLHQWHSURFHVRTXHGHEHHQWUDU

9$5,26
6,67(0$6
Ï,QKLELFLyQGH,QWHUUXSFLRQHV
Ï9DULDEOH&HUURMR (VSHUD$FWLYD
Ï6HPiIRURV
Ï0RQLWRUHV
Ï

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

La ventaja de este sistema es su simplicidad. Sin embargo, también da lugar a


varios inconvenientes:
• La región crítica debe ser corta, pues de lo contrario se podrían perder
interrupciones generadas por los dispositivos de E/S si no se tratan a tiempo.
• Inhibiendo las interrupciones, no solamente se impide que otro proceso
cooperante que desee entrar en la región crítica pueda continuar su ejecución,
sino también a procesos cooperantes que no intentan en la región crítica. Y lo
que es peor, también se impide que prosigan el resto de los procesos del
sistema, que por no ser cooperantes, en ningún momento intentarán acceder a
los datos compartidos que se están protegiendo.
• Este método sólo es apropiado para sistemas monoprocesador, pues en un
entorno con múltiples CPU’s la prohibición de las interrupciones en un
procesador no afecta a los demás. Una solución a este problema podría ser el
centralizar en un único procesador la gestión de interrupciones, pero no
siempre esto es posible.
• Resulta peligroso darle al usuario un medio para inhibir las interrupciones,
pues podría ocurrir que por un error de programación o por cualquier otro
motivo no las volviera a permitir, provocando la consiguiente pérdida de las
subsiguientes interrupciones y el acaparamiento en exclusiva de la CPU.

Sin embargo, con frecuencia resulta conveniente para el núcleo del sistema
operativo la inhibición de interrupciones durante unas pocas instrucciones mientras
actualiza estructuras de datos del sistema, por ejemplo, para actualizar la lista de
procesos Preparados, pues si se dejase en un estado inconsistente, el sistema se
iría al traste.

Como conclusión, se puede decir que la inhibición de interrupciones resulta, a


veces, una técnica útil o práctica para el núcleo, pero no es apropiada como
mecanismo general de exclusión mutua para los procesos de usuario.

Acabamos de ver una solución hardware al problema de la exclusión mutua.


Pasemos a tratar algunas soluciones basadas en algoritmos software que tratan de
evitar la pérdida de interrupciones y la injusticia de que la protección de una región
crítica perjudique a todos los procesos del sistema, incluso a aquellos que no tienen
ninguna relación con la estructura de datos compartida que se quiere proteger.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

([FOXVLyQ0XWXD ,QKLELFLyQGH,QWHUUXSFLRQHV
,QWHUUXSFLRQHV 3RVHVLyQLQGHILQLGDGHOD&38
,QKLELGDV

352%/(0$6
36HSXHGHQSHUGHULQWHUUXSFLRQHV
36HSULYDD72'26ORVSURFHVRVGHOD&38
33HOLJURVRHQPDQRVGHOXVXDULR

62/8&,Ð1(VSHFLILFDUOD5HJLyQ&UtWLFD

...
Entrar_RC (RC_Eventos);
...
...
Salir_RC (RC_Eventos);
...

8WLOSDUDORVQ~FOHRVGHORVVLVWHPDVRSHUDWLYRV

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

(VSHUD$FWLYD&HUURMRV
Otra posibilidad de impedir el acceso simultáneo de dos procesos a la región crítica
es haciendo que una variable actúe como cerrojo o indicador de si la región crítica
está ocupada o no. Esto se puede hacer con una variable booleana que tome los
valores 0 (libre) y 1 (ocupada). Todos los procesos que deseen acceder a una
región crítica deben cumplir un protocolo de acceso y salida de la región. Cuando un
proceso llegue a la entrada de la región debe consultar el valor de la variable-
cerrojo; si el valor es 0, la pone a 1 y entra en la región crítica; si la variable ya
estaba a 1, el proceso debe esperar hasta que valga 0.

La idea de este método es similar al acceso al lavabo de un avión de pasajeros. Si


el cartel indica /LEUH, entramos, cerramos la puerta, y el cartel pasa a indicar
2FXSDGR. En cambio, si al intentar acceder al lavabo, el cartel indica 2FXSDGR,
simplemente esperamos a que quede libre.

Por desgracia, esta idea contiene el mismo problema que nuestro ejemplo de los
procesos Observador y Reportero. Supongamos que un Proceso A lee el cerrojo y
ve que está a 0. Antes de que pueda ponerlo a 1, pierde el control de la CPU y lo
toma otro Proceso B que también quiere entrar en la región crítica. Este último
consulta también el valor del cerrojo (que sigue a 0), lo pone a 1 y entra en la región.
Antes de salir de la región crítica, finaliza su porción de tiempo y se le devuelve el
control al Proceso A, que continúa lo que estaba haciendo, con lo que pone el
cerrojo a 1 (otra vez) y entra en la región crítica, creyendo que él es el único que
está dentro, porque ha entrado siguiendo el protocolo de acceso.

Como vemos, el problema se debe a que la consulta del cerrojo y su posterior


puesta a 1 (ocupado) no se realiza como una acción atómica.

Para solucionar esto han surgido diversos algoritmos, entre ellos, el de la panadería
(de Lamport); el algoritmo de Decker (para 2 procesos); el de Dijkstra (para n
procesos); el de Knuth, Eisemberg y McGuire; y el de Peterson. La descripción de
estos algoritmos puede encontrarse en cualquiera de los textos indicados en la
bibliografía.

Se consigue una buena solución con un poco de ayuda hardware. Los procesadores
de propósito general suelen ofrecer la siguiente instrucción máquina (por lo tanto,
ininterrumpible en su ejecución):

TAS (Cerrojo, Valor)

Esta instrucción (7HVW DQG 6HW) actúa de la siguiente manera. Se lee la variable
&HUURMR, y en función de su valor, se establecen los IODJV correspondientes en el
Registro de Estado del procesador. Por último, a dicha variable se le asigna el
contenido del segundo parámetro, 9DORU. De esta forma se consigue realizar las dos
acciones de comprobación y asignación del valor “ocupado” sin ninguna
interrupción.

En la implementación de Entrar_RC y Salir_RC con TAS de la Figura 21 se


puede ver que cuando un proceso llega a la entrada de la región crítica, si la

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

([FOXVLyQ0XWXD 9DULDEOH&HUURMR
(QWUDUB5& TST RC_Eventos
BNZ Entrar_RC

£ (VWDP
MOV 1, RC_Eventos
RV
DOSUL FRPR
RET

6DOLUB5& MOV
RET
0, RC_Eventos QFLSLR

5&B(YHQWRV DB 0

+D\9DULRV$OJRULWPRVTXHOR6ROXFLRQDQ
à 'HNNHU SURFHVRV 'LMNVWUD QSURFHVRV
à .QXWK(LVHQEHUJ 0F*XLUH
à $OJRULWPRGHOD3DQDGHUtDGH/DPSRUW
à 3HWHUVRQ

0HMRUFRQ$\XGDGHO+: Entrar_RC TAS RC_Eventos,1


BNZ Entrar_RC
RET
Test and Set
Salir_RC MOV 0,RC_Eventos
RET

+´3UREOHPDGHOD,QYHUVLyQGH3ULRULGDGHVµ
+£\VLKD\P~OWLSOHVSURFHVDGRUHV"

(O*UDQ3UREOHPDGHHVWDV6ROXFLRQHV
(63(5$
£&RQVXPH5RGDMDVGH7LHPSR
$&7,9$

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

variable-cerrojo RC_EVENTOS no tiene el valor 0, todo lo que tiene que hacer es


ejecutarse en bucle hasta que la consulta del Registro de Estado indique que la
variable tenía el valor 0. Para anunciar la salida de la región crítica, lo único que hay
que hacer es simplemente poner el valor 0 en el cerrojo.

Un problema que se puede producir aquí es que un proceso cumpla con el protocolo
de entrada a la región crítica y acceda a ella, pero que a la salida se le olvide
liberarla. El acceso a la región queda bloqueado indefinidamente.

Hasta ahora este método funciona porque las acciones de FRPSUREDU \ SRQHU se
ejecutan como una sola instrucción ininterrumpible, pero veamos con mayor detalle
cómo se ejecuta la instrucción TAS. En primer lugar la CPU toma posesión del bus
(direcciones, datos y control) para leer el contenido del cerrojo de su posición de
memoria. Una vez traído el dato, el procesador suelta la posesión del bus para
analizar el contenido de la variable leída y establecer los IODJV del Registro de
Estado de acuerdo a su valor. Por último, vuelve a adueñarse del bus y realiza la
escritura del cerrojo con el valor que corresponda.

Pues bien, ¿funcionaría esto en un sistema multiprocesador? La respuesta es no.


Entre la lectura y la escritura del cerrojo, el bus de acceso a memoria queda libre,
con lo que nada impide que otro procesador lea el contenido del cerrojo en ese
intervalo y se crea con permiso para entrar en la región crítica. Para solucionar esto,
suele disponerse de una instrucción TASL 7HVWDQG6HWZLWK/RFN), que mantiene el
bus retenido para la CPU durante toda la operación de lectura y escritura del cerrojo.

Hemos dicho que “FXDQGR XQ SURFHVR OOHJD D OD HQWUDGD GH OD UHJLyQ FUtWLFD VL OD
YDULDEOHFHUURMR QR WLHQH HO YDORU  WRGR OR TXH WLHQH TXH KDFHU HV HMHFXWDUVH HQ
EXFOH KDVWD TXH OD FRQVXOWD GHO UHJLVWUR GH HVWDGR LQGLTXH TXH OD YDULDEOH WHQtD HO
YDORU”. Esto es un problema, o al menos, un gasto inútil de CPU. Si el cerrojo tiene
el valor 1 y el proceso que desea acceder a la región crítica está comprobando
LQLQWHUUXPSLGDPHQWH su valor, está claro que dicho valor no va a cambiar, al menos
mientras tal proceso continúe ejecutando la instrucción TAS. Seguramente habrá
que esperar a que se acabe su porción de tiempo, tome control el proceso que se
encuentra dentro de la región y al finalizar ponga a 0 el cerrojo. Así tenemos que el
proceso que quiere entrar gasta inútilmente sus porciones de tiempo esperando a
que aparezca el valor 0, cosa que nunca va a suceder mientras él tenga la posesión
de la CPU.

Por esto, a este método para conseguir la exclusión mutua se le conoce como
HVSHUDDFWLYD, pues el proceso espera su turno consumiendo, inútilmente, tiempo
de procesador.

Cuando la política de planificación de CPU es por prioridades, utilizando


mecanismos de sincronización de espera activa, se puede presentar el siguiente
escenario, conocido como el problema de la LQYHUVLyQ GH SULRULGDGHV.
Supongamos dos procesos cooperantes, P1 con prioridad 1 (alta) que está en
espera, y otro proceso P2 con prioridad 2 (baja) que está en ejecución. El proceso
P2 entra en la región crítica, y antes de abandonarla, el proceso P1 sale de espera y
pasa a Preparado, y debido a su mayor prioridad pasa inmediatamente a Ejecución,

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

por lo que al proceso P2 se le expulsa a Preparados. El proceso P1 se ejecuta hasta


llegar a la entrada de la región crítica, pero tras consultar el estado del cerrojo, se
dedica a realizar la espera activa hasta que el cerrojo quede libre. Como su prioridad
es mayor que la del proceso que se encuentra dentro de la región crítica (P2), nunca
va a ceder el procesador, con lo que el proceso P2 no va a continuar ejecutándose y
nunca abandonará la región crítica. Los dos procesos quedan bloqueados.

6HPiIRURV
En apartados anteriores hemos hablado sobre el ciclo de vida de un proceso,
durante el cual se pasa por periodos de instrucciones de CPU y por periodos de
espera de E/S. Debido a que las operaciones con recursos externos tardan mucho
tiempo en realizarse (incluso puede haber una cola de procesos requiriendo sus
servicios), los procesos que solicitan una operación de E/S pasan a estado de
Espera y ceden la CPU a otro proceso, no desperdiciando así el tiempo de
procesador.

E.W. Dijkstra propuso en 1965 el Semáforo como mecanismo de sincronización.


Una de sus principales peculiaridades es que evita la espera activa. La idea era
tratar las regiones críticas como recursos de acceso exclusivo por los que compiten
los procesos, de tal manera que el que consigue el permiso entra en la región
crítica, y el resto de los procesos quedan en estado de Espera hasta que les llegue
el turno de acceso.

Un semáforo es un tipo abstracto de datos con dos operaciones básicas más una de
inicialización (Figura 22). A las dos operaciones básicas las llamaremos %DMDU y
6XELU, aunque originalmente se llamaran 3 y 9, y posteriormente hayan recibido
otros nombres muy utilizados como (VSHUDU y 6HxDODU :DLW y 6LJQDO . Para cada
región crítica se declara una variable de tipo Semáforo que identifica a la región
como un recurso compartido.

Estas dos operaciones básicas se corresponden con los protocolos de acceso y


salida de la región crítica, tal que para entrar en ella hay que ejecutar una operación
Bajar sobre el semáforo asociado a esa región, y a la salida debe llamarse a la
instrucción Subir sobre el mismo semáforo.

Desde un punto de vista abstracto, el comportamiento de estas dos primitivas es el


siguiente:
%DMDU 6  ZKLOH S ≤ 0 GR Esperar;
S := S-1;

6XELU 6  S := S+1;

Con esta descripción ya puede empezarse a entender cómo funcionan los


semáforos, pues su comportamiento es similar a las acciones realizadas con el
mecanismo de espera activa. Pero ya hemos dicho que en este caso, no hay espera
activa, así que profundicemos, con ayuda de la Figura 22, en los significados de las
operaciones Bajar y Subir. Centrémonos en la acción (VSHUDU que se realiza dentro
de la operación Bajar de un semáforo. Esta acción significa:

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

1. Meter al proceso llamante en la cola de espera de los procesos que quieren


entrar en la región crítica.
2. Quitarle la CPU, pues no puede continuar la ejecución.
3. Llamar al Planificador para que seleccione un proceso de la cola Preparados
y seguidamente se le ceda el control del procesador.

([FOXVLyQ0 XWXD 6HP iIRURV


Package Semaforos is
type SEMAFOROS is private;

procedure ,QLFLDOL]DU(S : SEMAFOROS;


/ODP DGDVDO6LVWHP D
Valor : INTEGER);
DWyPLFDV
procedure %DMDU (S: SEMAFOROS);

procedure 6XELU (S: SEMAFOROS);

end Semaforos;

%$ -$ 5⇒ LI9DORUB6HP iIRUR WKHQ


&HGHUB&38
3DVDUBDB(VSHUD
HOVH
9DORUB6HP iIRUR 9DORUB6HPiIRUR
HQGLI

68 %,5⇒ LI9DORUB6HP iIRUR!WKHQ


9DORUB6HP iIRUR 9DORUB6HPiIRUR
HOVH
LI+ D\B$ OJ~QB3URFHVRB(VSHUDQGRWKHQ
3RQHUORB3UHSDUGR
3ODQLILFDGRU
HOVH
9DORUB6HP iIRUR 
HQGLI
HQGLI

6 LV WH P D V 2 S H UD WLY R V , * H V WLy Q G H 3 U R F H V R V  

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

Nos queda hablar de la inicialización del semáforo. Como ya veremos en las notas
sobre su implementación, un semáforo tiene un contador interno asociado, tal que
cada vez que un proceso realiza una operación Bajar sobre él, se decrementa en 1,
hasta llegar a cero. Por esto, el valor inicial de dicho contador indica el número
máximo de procesos que pueden utilizar simultáneamente el recurso asociado. En el
caso de que el recurso sea una región crítica, el valor inicial del semáforo deberá ser
1, pues es el número máximo de procesos que pueden acceder simultáneamente a
la región. Los valores que puede tomar el contador del semáforo son, entonces, 0 y
1; por esto a un semáforo de este tipo se le denomina VHPiIRURELQDULR.

Ahora podemos ver, en la Figura 23, la versión con semáforos de nuestro ejemplo
del Observador y el Reportero. Obsérvese que el programa Cuenta_Eventos
comienza inicializando a 1 el semáforo S, y arrancando a continuación los dos
procesos.

6 HP iIRURV H Q  Q X H V W U R  H M H P S O R

Program Cuenta_Eventos;
Contador : INTEGER;
S : SEMAFOROS;

process Observador;
repeat
Esperar_Evento;
%DMDU 6 
Contador := Contador + 1;
6XELU 6 
forever;
end Observador;

process Reportero;
repeat
%DMDU 6 
Imprimir (Contador);
Contador := 0;
6XELU 6 
forever;
end Reportero;

begin
,QLFLDOL]DU 6 
Observador;
Reportero;
end Cuenta_Eventos;

6 LV W H P D V 2 S H U D W LY R V , * H V W Ly Q G H 3 U R F H V R V  

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

En la Figura 24 se muestra una aproximación a la implementación del semáforo.


Ciertos detalles concretos pueden variar de un sistema a otro, sobre todo
dependiendo de la política de planificación utilizada. Como se puede ver, un
Semáforo es un registro con dos campos: el FRQWDGRU indicador del número de
procesos que pueden acceder al recurso, y una FROD para los procesos que tengan
que esperar su turno. Como comentario adicional a lo mostrado en dicha Figura 24,
cabe resaltar el hecho de que las operaciones sobre el semáforo son Llamadas al
Sistema y, por lo tanto, desde el punto de vista del usuario, se realizan de forma
DWyPLFD, es decir, que las instrucciones sobre la estructura de datos que compone
el semáforo se ejecutan siempre en exclusión mutua. La forma de conseguir la
exclusión mutua (no incluida en la Figura 24) depende de la implementación
concreta de cada sistema operativo.

Ya tenemos un mecanismo de sincronización que nos ha resuelto el gran problema


de la espera activa, pues ya no se desperdicia el tiempo de la CPU. No obstante, se
sigue manteniendo uno de los problemas de los cerrojos, esto es, sigue siendo
responsabilidad del programador el uso adecuado de las primitivas de
sincronización, o sea, %DMDU para entrar en la región crítica, y 6XELU para salir de ella.

Si inadvertidamente no se sigue estrictamente el protocolo de entrada y salida de la


región, o no inicializa adecuadamente el semáforo, el sistema falla.

0RQLWRUHV
Para evitar el problema de los descuidos del programador a la hora de seguir el
protocolo estipulado para acceder y salir de una región crítica, Hoare propuso en
1974 una primitiva de sincronización de más alto nivel denominada PRQLWRU. Brinch
Hansen propuso otra versión del monitor en 1975, y posteriormente ha habido otras
más, así como también hay diversas políticas de planificación para los procesos que
intervienen en un monitor.

Básicamente, un monitor es una colección de procedimientos y datos, agrupados en


una especie de módulo muy especial conocido como módulo monitor. Los procesos
pueden llamar a los procedimientos del monitor siempre que lo deseen, pero no
pueden acceder directamente a las estructuras de datos internas del monitor desde
procedimientos declarados fuera del monitor. Las estructuras de datos escondidas
en el monitor solamente están accesibles por los procedimientos del monitor.

En la Figura 25 se muestra un monitor llamado Control_Eventos que esconde


una variable interna: Num_Eventos, y que exporta dos procedimientos de acceso a
la variable: Incrementa e Imprime. En la parte inferior tenemos una versión de
nuestro ejemplo en la que se utiliza el monitor.

Los monitores tienen una propiedad especial para conseguir la exclusión mutua:
“6RODPHQWHXQSURFHVRSXHGHHVWDUDFWLYRDODYH]GHQWURGHXQPRQLWRU”. Dicho de
otra forma, dado un proceso A que ha llamado a un procedimiento de un monitor, y
mientras se está ejecutando dicho procedimiento, toma el control de la CPU otro
proceso B, si este proceso B llama a cualquier procedimiento del monitor, el proceso
B quedará detenido en la entrada (en estado de Espera) hasta que el proceso A
abandone el monitor.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

6HPiIRURV ,PSOHPHQWDFLyQ
type SEMAFOROS is private;
procedure ,QLFLDOL]DU(S : SEMAFOROS;
Valor : INTEGER);
procedure %DMDU (S : SEMAFOROS);
procedure 6XELU (S : SEMAFOROS);
private -- Inaccesible al usuario
type SEMAFOROS is
record
Contador : INTEGER;
Cola : COLA_PROCESOS;
end;
--
procedure ,QLFLDOL]DU (S : SEMAFOROS;
Valor : INTEGER) is
begin
S.Contador := Valor;
end Inicializar;

procedure %DMDU (S : SEMAFOROS) is


begin
if S.Contador < 1 then
Encolar (Este_Proceso, S.Cola);
Suspender; -- Implica llamada al Planificador
else
S.Contador := S.Contador - 1;
endif;
end Bajar;

procedure 6XELU (S : SEMAFOROS) is


Proceso : ID_PROCESO;
begin
if s.Cola.Primero /= 0 then -- Si algun proc. Esperando
Desencolar (Proceso, S.Cola);
Preparar (Proceso); -- Llamada al Planificador
else
S.Contador := S.Contador + 1;
endif;
end Subir;

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

([FOXVLyQ0XWXD 0RQLWRU
/RVVHPiIRURVVRQXQEXHQPHFDQLVPR
SDUDVLQFURQL]DFLyQGHSURFHVRVSHUR

6LQRVHXVDQDGHFXDGDPHQWH 3$FFHVR&RQFXUUHQWH
RUGHQQ~PHURYDORULQLFLDO 3,QWHUEORTXHRV

8Q0RQLWRUHVXQFRQMXQWRGDWRVFRQVXVSURFHGLPLHQWRVGH
DFFHVRHQFHUUDGRVHQXQPyGXORHVSHFLDO 0RQLWRU 

6RODPHQWHXQSURFHVRSXHGHHVWDUDFWLYR (;&/86,Ð1
HQXQPRQLWRUHQXQPRPHQWRGDGR 0878$

Module Monitor Control_Eventos;


Num_Eventos : INTEGER;
procedure ,QFUHPHQWD is
Num_Eventos := Num_Eventos + 1;
end Incrementa;

procedure ,PSULPH is
Imprimir (Num_Eventos);
Num_Eventos := 0;
end Imprime;

process Observador;
repeat
Esperar_Evento;
&RQWUROB(YHQWRV,QFUHPHQWD
forever;
end Observador;

process Reportero;
repeat
&RQWUROB(YHQWRV,PSULPH
Hacer_Otras_Cosas;
forever;
end Reportero;

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

Los monitores son una construcción de los lenguajes de programación, de tal forma
que los compiladores saben que las llamadas a los procedimientos de un monitor se
manejan de forma distinta a las llamadas a los procedimientos convencionales.
Normalmente, en la llamada de un proceso a un procedimiento de un monitor, las
primeras instrucciones del procedimiento son para comprobar si algún otro proceso
se encuentra dentro del monitor; si es así, el proceso llamante queda bloqueado
hasta que el otro proceso salga del monitor. Si no hay ningún proceso dentro del
monitor, el proceso llamante puede entrar y continuar la ejecución.

La implementación de la exclusión mutua en las entradas del monitor es labor del


compilador. Normalmente suele realizarse con ayuda de un semáforo binario, de tal
forma que en el prólogo de un procedimiento de monitor, se incluye una llamada a
Bajar(S), y a la terminación del procedimiento se llama a Subir(S), siendo S un
semáforo asociado a cada monitor.

Ya que es el compilador, no el programador, el que se ocupa de cumplir el protocolo


de acceso a la región crítica, es mucho más difícil que algo se haga mal o que se
produzca algún olvido. En cualquier caso, la persona que escribe un monitor no
tiene por qué saber cómo implementa la exclusión mutua el compilador. Le basta
con saber que metiendo las regiones críticas en monitores, nunca habrá más de un
proceso dentro de la misma región crítica al mismo tiempo.

Puesto que cuando un proceso se encuentra dentro de un monitor, no puede entrar


otro proceso, debe evitarse que el proceso que está dentro pueda quedarse
bloqueado en estado de Espera, o sea, que no debe ejecutar llamadas al sistema
que lo pasen a tal estado. Sin embargo hay situaciones en las que se requiere pasar
a Espera. Para ello, existen otras construcciones, para que cuando un proceso
dentro de un monitor deba pasar a espera, se permita la entrada a algún otro
proceso que esté esperando para entrar. Aunque estas construcciones (las YDULDEOHV
FRQGLFLyQ), no vamos a tratarlas en estos apuntes, puede obtenerse una descripción
detallada consultando la bibliografía de referencia.

 3DVRGH0HQVDMHV
Aunque los semáforos y los monitores son buenos mecanismos para la
sincronización de procesos todavía tienen algunas pegas: los semáforos son de
demasiado bajo nivel, y los monitores solamente están disponibles en unos pocos
lenguajes de programación.

Otro problema con los monitores y los semáforos es que están diseñados para
resolver el problema de la exclusión mutua en sistemas con una o más CPU’s que
comparten una memoria común. Pero cuando se trata de un sistema distribuido,
formado por múltiples procesadores, cada uno con su propia memoria particular y
conectados por una red de área local, estas primitivas se vuelven inservibles, pues
ya no hay variables compartidas, y ninguno de estos mecanismos proporciona
intercambio de información entre máquinas. Se necesita algo más.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

&RPXQLFDFLyQGH3URFHVRV 3DVRGH0HQVDMHV
&RPXQLFDFLyQ
HQWUH3URFHVRV

0HPRULD&RPSDUWLGD 3DVRGH0HQVDMHV
(OSURJUDPDGRUUHVXHOYHORV 1RKD\YDULDEOHV
SUREOHPDVGHFRQFXUHQFLD FRPSDUWLGDV

(QYLDU 0HQVDMH 5HFLELU 0HQVDMH

3URF 3URF
 

5HFLELU 0HQVDMH (QYLDU 0HQVDMH

$/*81$6&8(67,21(6
8QLGLUHFFLRQDO Ï0HPRULD&RPSDUWLGD
(O(QODFH
+ 3XHGH6HU
Ï%XV+DUGZDUH
%LGLUHFFLRQDO Ï5HGGH&RPXQLFDFLRQHV

Ï&RPXQLFDFLyQ',5(&7$R,1',5(&7$
+ 0RGHORV
/yJLFRV Ï&RPXQLFDFLyQ6,0e75,&$2$6,0e75,&$

+ &DSDFLGDGGHO%X]yQ
Ï)LMR
+ 7DPDxRGHO0HQVDMH
Ï9DULDEOH

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

Ese algo más es el SDVRGHPHQVDMHV. La función del paso de mensajes es permitir


que dos procesos se comuniquen sin necesidad de utilizar variables compartidas.
Este método de comunicación entre procesos ofrece, al menos, dos primitivas:
(QYLDU y 5HFLELU, que, a diferencia de los monitores, no son construcciones del
lenguaje de programación, sino Llamadas al Sistema, por lo que para utilizarlas
basta con llamar a los correspondientes procedimientos de biblioteca.

Mediante (QYLDU se expide un mensaje a un proceso destino, mientras que con


5HFLELU se indica el deseo de recibir un mensaje de algún proceso fuente. Si no hay
ningún mensaje disponible, el proceso receptor puede quedarse bloqueado hasta
que llegue uno.

El paso de mensajes no es de uso exclusivo de sistemas con múltiples


procesadores (con o sin memoria común), sino que es válido para cualquier sistema
multiproceso, ya sea con una o más CPU’s, o con memoria compartida o local a
cada procesador, pues su semántica solamente indica el paso de información de un
proceso a otro. Esta independencia del hardware subyacente hace que los
programas que utilizan paso de mensajes como mecanismo de sincronización y
comunicación sean más portables entre distintas máquinas, pues sólo requieren que
dispongan del mismo sistema operativo. (Obsérvese que esto no quiere decir que
para que dos procesos que se ejecutan en máquinas distintas puedan comunicarse,
tengan que ejecutarse sobre el mismo sistema operativo; simplemente tienen que
utilizar el mismo protocolo de comunicación).

Si dos procesos $ y % quieren comunicarse, deben enviarse y recibir mensajes, por


lo que debe establecerse un HQODFH GH FRPXQLFDFLyQ entre ellos. Este enlace
puede implementase de distintas maneras. Aquí no vamos a tratar la
implementación física del enlace (memoria compartida, un bus o una red), que es
más propia de los cursos de comunicación de datos, sino que vamos a
preocuparnos de los aspectos lógicos de la implementación. Así, estudiaremos
cuestiones relacionadas con los siguientes puntos:

• ¿Cuál es el tamaño de los mensajes? ¿El tamaño es fijo o puede ser variable?
• ¿Un enlace puede ser unidireccional o bidireccional? Es decir ¿los mensajes
solamente pueden viajar en un sentido, o en ambos?
• Modelos de comunicación: comunicación directa o indirecta, simétrica o
asimétrica.
• La información se puede enviar por copia o por referencia.

En cuanto al tamaño de los mensajes que se envían los procesos, pueden ser de
longitud fija o variable. Si sólo se permiten mensajes de longitud fija, la
implementación del sistema es más fácil, pero tal restricción dificulta la tarea del
programador.

Veamos el resto de las cuestiones enumeradas en los siguientes apartados.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

 0RGHORVGH&RPXQLFDFLyQ
Primero debemos decir que los procesos reciben mensajes en EX]RQHV. Es decir,
que cuando un proceso envía mensajes, estos van a parar a buzones, y los
procesos que quieren recibirlos tendrán que sacarlos de esos buzones.

Los procesos que desean comunicarse deben disponer de una manera explícita de
indicar el destino del mensaje que se envía, o el remitente del mensaje que se
quiere recibir, y esto depende de que la comunicación sea 'LUHFWD o ,QGLUHFWD.

&RPXQLFDFLyQ'LUHFWD
En la comunicación directa de envío de mensajes, tanto el emisor como el receptor
deben indicar explícitamente el proceso destino o remitente del mensaje. Así, las
primitivas de envío y recepción tendrán el siguiente aspecto:

(QYLDU 3URFHVRB'HVWLQR0HQVDMH 
5HFLELU 3URFHVRB2ULJHQ0HQVDMH 

Como se puede apreciar en el esquema superior de la Figura 27, cada proceso tiene
su propio buzón de recepción de mensajes.

Una comunicación de este tipo tiene las siguientes características:


• Automáticamente se establece un enlace entre cada par de procesos que se
quieren comunicar.
• Cada uno de ellos necesita saber la identidad del otro proceso con el que se
quiere comunicar.
• Un enlace asocia únicamente a dos procesos.
• Entre cada par de procesos solamente existe un enlace.
• El enlace o comunicación puede ser unidireccional o bidireccional.

En la Figura 27 también tenemos a nuestros dos procesos, el Observador y el


Reportero, comunicándose por mensajes. Tal comunicación es directa,
unidireccional (el Observador sólo envía, y el Reportero solamente recibe) y
VLPpWULFD, pues tanto el emisor como el receptor necesita conocer el nombre del
otro proceso.

Una variante dentro de esta comunicación directa, es que el modelo sea DVLPpWULFR
(esquema inferior de la Figura 27), es decir, que puede haber múltiples procesos
enviando mensajes a un único receptor, en cuyo caso el proceso receptor no tiene
que indicar de qué proceso quiere recibir mensajes. En la comunicación asimétrica,
la llamada al sistema para enviar mensajes permanece invariable, mientras que la
de recepción, queda así:

5HFLELU ,GB3URFHVR0HQVDMH 

Donde ambos parámetros son de salida. ,GB3URFHVR contendrá el identificador del


proceso que envió el 0HQVDMH recibido.

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

3DVRGH0HQVDMHV &RPXQLFDFLyQ'LUHFWD
3&DGDSURFHVRWLHQHVXSURSLREX]yQ
3&DGDSURFHVRLPSOLFDGRGHEHLQGLFDU
H[SOtFLWDPHQWHHOQRPEUHGHOUHFHSWRURHPLVRU

(QYLDU 30HQVDMH 5HFLELU 30HQVDMH

3URF 3URF
 

5HFLELU 30HQVDMH (QYLDU 30HQVDMH

(VTXHPD6,0e75,&2

2EVHUYDGRU 5HSRUWHUR
repeat repeat
. . . . . .
Esperar_Evento 5HFLELU 2EVHUYDGRU0HQVDMH
. . . . . .
(QYLDU 5HSRUWHUR0HQVDMH Imprimir_Evento
forever forever

(QYLDU 5HSRUWHUR0HQVDMH 
2EV


2EV (QYLDU 5HSRUWHUR0HQVDMH 


5HS

5HFLELU 5HPLWHQWH0HQVDMH 
2EV
 (QYLDU 5HSRUWHUR0HQVDMH 

(VTXHPD$6,0e75,&2
3HJDGHOD
6LFDPELDHOQRPEUHGHXQSURFHVR
&RPXQLFDFLyQ
KD\TXHUHYLVDUWRGDVODVUHIHUHQFLDVDpO
'LUHFWD
6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

La desventaja que presenta este modelo de comunicación directa (tanto el simétrico


como el asimétrico), es el problema que se plantea con el mantenimiento de los
nombres de proceso. Es decir, en un programa, que puede estar formado por
muchos módulos, si se cambia el identificador de un proceso hay que revisar todos
los módulos para comprobar y modificar todas las referencias a tal proceso, para
que indiquen el nuevo identificador.

&RPXQLFDFLyQ,QGLUHFWD
Con la comunicación indirecta, los mensajes se envían a buzones, no a procesos, y,
análogamente, el receptor debe indicar el buzón del que quiere recibir un mensaje,
no de qué proceso. Véase el croquis superior de la Figura 28.

Un buzón se puede ver como un objeto en el que se pueden poner y retirar


mensajes. Cada buzón tiene un identificador único. Según este esquema, un
proceso puede comunicarse con otros procesos mediante distintos buzones. Las
primitivas de envío y recepción de mensajes quedan así:

(QYLDU %X]yQ0HQVDMH 
5HFLELU %X]yQ0HQVDMH 

siendo %X]yQ el identificador del buzón al que se envía o del que se desea recibir un
mensaje.

Observando el citado croquis de la Figura 28, puede verse que, ahora, un enlace de
comunicaciones tiene las siguientes propiedades:

• Dos procesos solamente pueden comunicarse entre ellos si comparten un


buzón.
• Un enlace de comunicaciones puede estar asociado a más de dos procesos.
• Entre cada pareja de procesos comunicantes puede haber varios enlaces,
donde cada enlace corresponde a un buzón.
• Un enlace puede ser unidireccional o bidireccional.

Este modelo de comunicación indirecta ya no adolece del problema del


mantenimiento de los nombres o identificadores de procesos que se producía en la
comunicación directa, pues ahora nunca se nombra el proceso destino u origen, sino
un buzón común. Este esquema de comunicaciones es similar al servicio de
$SDUWDGRV GH &RUUHRV que ofrecen las compañías postales. La comunicación
mediante un apartado de correos, es independiente de la dirección o domicilio real
de la persona o entidad a quien se dirige la correspondencia, ya que ésta se le envía
indirectamente, vía un apartado de correos, y aunque el destinatario cambie de
domicilio, basta con que siga yendo a recoger la correspondencia al cajetín del
apartado de correos para que la comunicación siga siendo efectiva. Obviamente, la
comunicación se mantiene en tanto en cuanto se mantenga invariable el nombre del
buzón o apartado de correos común. Pero mientras que los cambios de domicilio

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

3DVRGH0HQVDMHV &RPXQLFDFLyQ,QGLUHFWD

2EV /RV%X]RQHV6RQ&RPSDUWLGRV
 %X]yQB
Enviar (Buzón_1, Mensaje) Recibir (Buzón_1, Mensaje);

2EV 5HS
 %X]yQB

Enviar (Buzón_1, Mensaje) Recibir (Buzón_2, Mensaje);
Enviar (Buzón_2, Mensaje)

2EV 5HS
 
Enviar (Buzón_2, Mensaje) Recibir (Buzón_2, Mensaje);

/RVPHQVDMHVVHHQYtDQ\UHFLEHQDGHEX]RQHVQRDSURFHVRV
5HSB\5HSBHMHFXWDQRecibir (Buzón_2, Mensaje)
2EVBHQYtDXQPHQVDMHDO%X]yQB
+¢4XpSURFHVRUHFLEHHOPHQVDMHGH2EVB"

(O6LVWHPD2SHUDWLYRRIUHFHVHUYLFLRVSDUD
Procedure &UHDUB%X]RQ (Nombre : in Nombres;
Buzon : out Buzones);

procedure &RQHFWDUB%X]RQ (Nombre : in Nombres;


Buzon : out Buzones);

procedure (QYLDU (Buzon : in Buzones;


Mensaje : in Mensajes);

procedure 5HFLELU (Buzon : in Buzones;


Mensaje : out Mensajes);

procedure 'HVWUXLUB%X]RQ (Buzon : in out Buzones);

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

son previsibles, no hay razón para no seguir manteniendo el mismo apartado de


correos, y así nadie se ve afectado por el cambio de dirección.

Cuando la comunicación es indirecta, se pueden tener varios buzones de


comunicación entre dos procesos, con lo que se puede enviar mensajes a uno u otro
buzón, dependiendo del motivo o importancia de cada mensaje.

Obsérvese que, debido a la flexibilidad que ofrece este esquema, se puede tener
cualquier combinación numérica de procesos productores y consumidores de
mensajes:
• Un productor- un consumidor
• Varios productores - un consumidor
• Un productor - varios consumidores
• Varios productores - varios consumidores

Las primitivas de gestión de buzones y mensajes varían según el sistema operativo.


No obstante, en el cuadro inferior de la Figura 28 podemos ver las primitivas que se
suelen ofrecer para estos servicios:

• Crear un buzón.
• Conectarse a un buzón.
• Enviar y recibir mensajes de un buzón.
• Destruir un buzón.

Para crear un buzón, se llama a la primitiva correspondiente, indicando como


parámetro de entrada el nombre del buzón, esto es, una tira preestablecida de
caracteres (al igual que los nombres de ficheros), y el sistema crea el buzón
devolviendo como parámetro de salida el LGHQWLILFDGRU GHO EX]yQ creado. Este
identificador es el que se debe utilizar en el resto de las operaciones que se realicen
con ese buzón.

Cuando varios procesos vayan a comunicarse mediante un buzón, uno de ellos (el
propietario) lo debe crear y, posteriormente, el resto debe conectarse a él, pues
conocen de antemano el nombre del buzón a compartir. Seguidamente ya pueden
realizar las operaciones de envío y recepción de mensajes utilizando el identificador
de buzón.

El proceso propietario del buzón debe ocuparse de destruir el buzón cuando no se


requieran más sus servicios, para liberar la memoria y recursos del sistema que se
utilizan en la gestión del buzón.

 &DSDFLGDGGHO%X]yQ
Un buzón tiene una capacidad que determina el número de mensajes que puede
tener almacenados temporalmente. Este almacén se puede ver como una cola de
mensajes asociada al buzón. Básicamente hay tres posibilidades sobre el tamaño
del buzón:

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

• &DSDFLGDG/LPLWDGD
La cola tiene una longitud máxima finita (Q mensajes), lo que quiere decir que,
como mucho, podrá haber Q mensajes en el buzón esperando a ser recibidos por
uno o varios procesos. Así, si la cola no está llena, cuando un proceso envía un
mensaje, éste se encola en el buzón, y el emisor puede continuar la ejecución sin
ninguna espera. El proceso receptor podría estar esperando a recibir un mensaje,
con lo que ahora ya podría recibirlo y continuar también la ejecución; pero
también es posible que no hubiera todavía ningún proceso esperando por el
mensaje, en cuyo caso, el mensaje simplemente queda encolado en el buzón
hasta que un proceso quiera recibirlo. No obstante, ya que el tamaño del buzón
es limitado, si al enviar un mensaje a un buzón, éste está lleno, entonces hay dos
alternativas:

- El proceso emisor queda bloqueado en espera de que haya espacio libre en


el buzón.
- Como parámetro de salida en la llamada a (QYLDU, se le indica un VWDWXV de
error indicativo del llenado del buzón. En este caso, el proceso emisor no se
bloquea, y se deja en sus manos la decisión de reenvío o no del mensaje.
La técnica habitual de implementar estos buzones es mediante un buffer
circular.

• 6LQ/tPLWHGH&DSDFLGDG
La cola de mensajes tiene una longitud potencialmente infinita, por lo que puede
albergar cualquier número de mensajes, y por lo tanto, el emisor nunca queda
bloqueado.

Los buzones de capacidad ilimitada suelen implementarse como una lista


encadenada de mensajes, por lo que la limitación sólo está en la cantidad de
memoria que el proceso tiene disponible.

• &DSDFLGDG1XOD
La longitud máxima de la cola de mensajes es cero, por lo que no puede haber
mensajes en el buzón esperando a ser recibidos. En este caso, el emisor o
remitente que quiera enviar un mensaje debe esperar hasta que el destinatario
reciba el mensaje. Así, los dos procesos deben ponerse de acuerdo en el
momento de realizar el envío/recepción del mensaje para que la transferencia
tenga lugar. En este caso se dice que la comunicación es VtQFURQD. A esta
comunicación síncrona también se le conoce con el nombre de UHQGH]YRXV (cita).

Se debe hacer notar que cuando la capacidad del buzón no es cero, el proceso
emisor no sabe cuándo un mensaje enviado ha llegado a su destino, pues no tiene
que esperar a que el proceso destino lo reciba (comunicación DVtQFURQD), y esto
puede ser crucial para el normal desarrollo del programa. En este caso, el receptor
debería comunicarse explícitamente con el emisor para hacerle saber que ha
recibido el mensaje.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

3DVRGH0HQVDMHV &DSDFLGDGGHO%X]yQ
+&DSDFLGDG/LPLWDGD QPHQVDMHV

6LKD\HVSDFLR⇒(OHPLVRUFRQWLQ~DODHMHFXFLyQ
GHVSXpVGHOHQYtR

6LHVWiOOHQR⇒3(OHPLVRUTXHGDEORTXHDGRKDVWDTXH
KD\DHVSDFLRHQHOEX]yQSDUDGHMDU
XQPHQVDMH
36HGHYXHOYHXQVWDWXV OOHQR

+&DSDFLGDG,OLPLWDGD
(OHPLVRUQXQFDVHEORTXHDHQHOHQYtRGHPHQVDMHV

+&DSDFLGDG1XOD
(OHPLVRUTXHGDEORTXHDGRKDVWDTXHHOUHFHSWRUHVWi
OLVWRSDUDUHFLELUHOPHQVDMH

&RPXQLFDFLyQ 5(1'(=9286
6tQFURQD

(QORVEX]RQHVFRQFDSDFLGDG!
¢&yPRVDEHHOHPLVRUTXHHOPHQVDMHKDOOHJDGRDVXGHVWLQR"
PROCESO P1:
Enviar (P2, Mensaje);
Recibir (P2, Mensaje);
+D\TXHIRU]DUHO
VLQFURQLVPR
PROCESO P2:
Recibir (P1, Mensaje);
Enviar (P1, Mensaje);

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

7KUHDGV
Vamos a comentar ahora un nuevo tipo de procesos que en los últimos años a
surgido con fuerza entre los sistemas operativos y que se adapta especialmente
bien a la estructura de los sistemas con múltiples procesadores: los WKUHDGV o
procesos ligeros.

Para que un programa pueda ejecutarse (convertirse en proceso) se requieren dos


elementos:
• Un procesador.
• Un entorno de ejecución.

El entorno de ejecución se refiere al resto de los recursos (además del procesador)


que necesita un programa para ejecutarse. Así, por ejemplo, sabemos que se
necesita un área de memoria para acoger el código del programa, un área de
memoria para ubicar las variables globales, otro área de memoria, denominado
KHDS, de donde se sirven las peticiones dinámicas de memoria, y por último también
se necesita un área de memoria para albergar la pila de trabajo. Una vez que el
proceso está cargado en memoria puede comenzar su ejecución. Con memoria
virtual, el comienzo de la ejecución va a implicar que todas las primeras referencias
a memoria generen una falta de página, tanto en la caché como en memoria
principal, por lo que se deberá dedicar un tiempo a la carga de páginas iniciales
hasta que se consiga el conjunto de trabajo estable. También es normal que un
proceso haga operaciones de E/S con ficheros, por lo que será necesario abrir los
ficheros con los que se vaya a trabajar, y durante la ejecución se tendrán EXIIHUV de
E/S asociados con cada dispositivo o fichero. Como nos podemos imaginar se
requiere cierto tiempo para conseguir un entorno de ejecución estable.

Los procesos tradicionales (como los de Unix) se crean de tal forma que comparten
el procesador (en un entorno monoprocesador) y a cada uno se le asigna un entorno
de ejecución diferente, es decir, cada uno tiene sus áreas de memoria, sus tablas de
páginas, sus descriptores de los ficheros que maneja, sus EXIIHUV de E/S,
semáforos, etc. Y esto, que parece razonable, se hace para todos los procesos del
sistema, es decir, se trata a todos los procesos del sistema como si fueran
totalmente disjuntos y que lo único que comparten es el procesador.

Ahora bien, hay situaciones en las que un trabajo laborioso, puede descomponerse
en varias subtareas o subprocesos, de tal forma que todos ellos cooperan en la
consecución de un mismo fin. En estos trabajos es normal que haya mucha
comunicación y compartimiento de datos entre los subprocesos componentes. El
mecanismo normal de comunicación en estos casos son los mensajes, que no deja
de ser un mecanismo costoso en tiempo. La comunicación entre procesos por
memoria compartida, aunque rápida, es peligrosa, pues un proceso malintencionado
podría machacar áreas de memoria de otro proceso con el que comparte memoria,
sin embargo esto no es normal en procesos cooperantes.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

7KUHDGV
3DUDHMHFXWDUXQSURJUDPDVHUHTXLHUH

0& (QWRUQRGH(MHFXFLyQ
(VSDFLRGHGLUHFFLRQHV
9DULDEOHVJOREDOHV
&RQWDGRUGHSURJUDPD )LFKHURVDELHUWRV
3XQWHURGHSLOD 7HPSRUL]DGRUHV
5HJLVWURGHHVWDGR 3URFHVRVKLMRV
5HJLVWURVJHQHUDOHV 6HxDOHV
 6HPiIRURV
&RQWDELOLGDG

&DPELRGH 3&DPELRHQHOSURFHVDGRU
FRQWH[WR 3&DPELRGHHQWRUQRGHHMHFXFLyQ
)DOWDVGHSiJLQD
1HFHVDULRFRQ3URFHVRV )DOWDVGHFDFKp
'LVMXQWRV $EULUILFKHURV
/OHQDUEXIIHUVGH(6
3(52

SLOD SLOD SLOD SLOD


3URFHVRV&RRSHUDQWHV GDWRV GDWRV
38('(1&203$57,5
FRG FRG FyGLJR
5(&85626
3 3 3 3

3&UHDFLyQ5iSLGDGH3URFHVRV
3&DPELR5iSLGRGH&RQWH[WR

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

Hemos visto que la creación de un proceso puede ser un tanto costosa, pues crea
un entorno de trabajo totalmente diferente para cada uno. Sin embargo, para
procesos cooperantes parece que no se requieren entornos totalmente distintos,
pues es muy posible que prefieran áreas de memoria comunes para compartir datos
(a su propio riesgo) y comunicarse rápidamente. Es más, seguramente prefieren
compartir las memorias caché, y los EXIIHUV de E/S, pues parte de los datos que
manejan van dirigidos o provienen de las mismas fuentes. Dos o más procesos
gemelos cooperantes podrían compartir incluso el área de código. Según esto, la
creación de procesos cooperantes requeriría un entorno de trabajo muy reducido: la
pila de trabajo y, en caso de procesos no gemelos, un área de memoria para el
código. Esto traería ventajas por dos vertientes:

• Creación rápida de procesos


• Cambio rápido de contexto

Debemos darnos cuenta de que, en este escenario, la conmutación del entorno de


trabajo entre dos procesos cooperantes puede ser muy rápida, pues solamente hay
que cambiar los registros del procesador (lo cual implica un cambio de pila de
trabajo y de contador de programa). El proceso que asume la posesión del
procesador va a seguir utilizando las mismas áreas de memoria para datos
estáticos, código (si los procesos son gemelos), ficheros, EXIIHUV de E/S. El
compartimiento de memoria va a implicar, además, que al cambiar de contexto no
se van a vaciar las cachés y no se van a producir faltas de página en las primeras
referencias a memoria, con lo que se evitan más pérdidas de tiempo.

Por lo visto hasta ahora, parece que sería interesante que los sistemas operativos
ofrecieran dos tipos de procesos (cooperantes y no cooperantes), con sus
correspondientes primitivas de creación, gestión, comunicación y sincronización.

Prácticamente todos los núcleos de sistemas operativos para sistemas distribuidos


ofrecen los dos tipos de procesos, y estos son los nombres que les han dado:

.HUQHOGH62SURFHVRVFRRSHUDQWHV3URFHVRVQRFRRSHUDQWHV
$PRHED 7KUHDG Proceso
&KRUXV 7KUHDG Actor
0DFK 7KUHDG Tarea
96\VWHP Proceso Equipo (7HDP)

Aunque los distintos sistemas operativos no se han puesto totalmente de acuerdo,


los nombres más extendidos son los proceso, tarea o proceso pesado para los
procesos tradicionales (los que tienen un entorno de trabajo totalmente diferente de
los demás procesos), y WKUHDG o SURFHVROLJHUR para los procesos cooperantes que
quieren compartir el entorno de trabajo.

Windows/NT, OS/2 y Solaris (Unix de Sun) son algunos ejemplos de sistemas


operativos comerciales que disponen de WKUHDGV.

 $SXQWHVGH62,
*HVWLyQGH3URFHVRV

7KUHDGV
.HUQHOGH62 3URF&RRSHUDQWHV 3URF1R&RRSHUDQWHV
$PRHED 7KUHDG 3URFHVR
&KRUXV 7KUHDG $FWRU
0DFK 7KUHDG 7DUHD
96\VWHP 3URFHVR (TXLSR 7HDP

7KUHDG 3URFHVR
R R
3URFHVR/LJHUR 3URFHVR3HVDGR

0D\RU
1~PHURGH 0HMRUDSURYHFKDPLHQWR
3URFHVRV GHORV7KUHDGV
&RRSHUDQWHV
WKUHDG 5$0FRPSDUWLGD
UHSDUWLGRU

/RV6'VHSUHVWDQD WKUHDGV
WHQHUDSOLFDFLRQHV GHVHUYLFLR
IRUPDGDVSRUPXFKRV
SURFHVRVFRRSHUDQWHV

FDFKp

EX]yQ
SHWLFLyQ

62 &UHDUSURFHVRWKUHDG &DPELRFRQWH[WR


8QL[ PV PV
7RSD] PV PV
6REUHXQSURFHVDGRU&9$;GH',*,7$/

6LVWHPDV2SHUDWLYRV, *HVWLyQGH3URFHVRV

$SXQWHVGH62, 
*HVWLyQGH3URFHVRV

A los programas con WKUHDGV se les saca más provecho en aplicaciones compuestas
de múltiples procesos cooperantes, y esto es algo que se suele dar mucho en los
sistemas distribuidos, en los que por su filosofía cliente-servidor se prestan mucho a
este tipo de aplicaciones. Debido a la premura de tiempo no vamos a profundizar en
esta justificación, no obstante, se recomienda echar un vistazo a los capítulos
dedicados a WKUHDGV de los textos indicados en la bibliografía, en los cuales pueden
encontrarse numerosos ejemplos de situaciones de programas en las que se
muestran beneficios de los WKUHDGV frente a los procesos convencionales.

Algunos datos sobre los tiempos creación de procesos nos pueden ayudar a
convencernos de las ventajas de los WKUHDGV. La creación de un proceso Unix sobre
un procesador CVAX de DIGITAL requiere 11 milisegundos, mientras que la
creación de un WKUHDG en un kernel llamado Topaz, sobre el mismo procesador
solamente necesita 1 milisegundo. Los tiempos de cambio de contexto entre
procesos Unix oscilan alrededor de 1,8 milisegundos, siendo solamente 0,4 ( y en
algunos casos 0,004) entre WKUHDGV de Topaz. A estos datos habría que añadir los
beneficios que se ganan a largo plazo, es decir, el tiempo que no se pierde en
cargar de nuevo las páginas de memoria virtual, de la caché, creación de
semáforos, apertura y cierre de ficheros, llenado de EXIIHUV de E/S, etc.

 $SXQWHVGH62,

Potrebbero piacerti anche