Procesos: Justificación Y Resumen
Procesos: Justificación Y Resumen
Procesos: Justificación Y Resumen
2.2 PROCESOS
Esta sección examina la naturaleza de los procesos y la forma en que son administrados por los sistemas operativos.
En particular, la atención se centra en la forma en que la entrada y salida (IO) de los dispositivos se asigna a los
flujos de IO de un proceso y la forma en que se logra la comunicación entre procesos (IPC) con las tuberías.
Un programa es una lista de instrucciones junto con la estructura y la información de secuencia para controlar el
orden en que se ejecutan las instrucciones.
Esto significa que cuando ejecutamos (o ejecutamos) un programa, se crea un proceso (ver más abajo).
La distinción más importante entre un programa y un proceso es que un programa no hacer
cualquier cosa; más bien, es una descripción de cómo hacer alguna cosa. Cuando se crea un proceso, llevará a cabo las
instrucciones del programa relacionado.
Un conjunto de instrucciones que se suministran con los muebles de montaje en el hogar son una analogía útil
con un programa de computadora. Proporcionan un conjunto secuenciado (numerado) de pasos individuales que
describen cómo ensamblar los muebles. Los muebles no se ensamblarán solo por la existencia de las instrucciones.
Solo cuando alguien realmente lleva a cabo las instrucciones, en el orden correcto, se construyen los muebles. El
acto de seguir las instrucciones paso a paso es análogo al proceso de la computadora.
Otra relación importante entre un programa y un proceso es que el mismo programa se puede ejecutar (ejecutar)
muchas veces, dando lugar cada vez a un proceso único. Podemos extender la analogía de los muebles de montaje en el
hogar para ilustrar; cada conjunto de muebles se entrega con una copia de las instrucciones (es decir, el mismo
"programa"), pero estos se instancian (es decir, las instrucciones se llevan a cabo) en muchos momentos diferentes y en
diferentes lugares, posiblemente superpuestos en el tiempo de modo que dos personas pueden estar construyendo sus
muebles al mismo tiempo, sin que el otro lo sepa.
Los errores semánticos son errores en la presentación de su lógica. En otras palabras, el significado expresado
por la lógica del código del programa no es el significado pretendido por el programador. Estos errores son
potencialmente mucho más graves porque, en general, el compilador no podrá detectarlos. Por ejemplo, considere
que tiene tres variables enteras A, B y C y tiene la intención de realizar el cálculo C = A - B pero accidentalmente
escribes C = B - A ¡en lugar de! El compilador no encontrará ningún error en su código, ya que es sintácticamente
correcto, y el compilador ciertamente no tiene conocimiento de la forma en que se supone que funciona su
programa y no le importa qué valor o significado real se atribuye a las variables A, B y C. Los errores semánticos
deben evitarse mediante un diseño cuidadoso y disciplina durante la implementación y se detectan mediante la
vigilancia por parte del desarrollador, y esto debe estar respaldado por un régimen de pruebas riguroso.
Supongamos que ahora tenemos un programa lógicamente correcto que ha sido compilado y almacenado en un
archivo en forma de código de máquina que el hardware del microprocesador puede entender. Este tipo de archivo es
24 CAPITULO 2 LA VISTA DEL PROCESO
comúnmente conocido como un archivo ejecutable, porque puede "ejecutarlo" (ejecutar) proporcionando su nombre al
sistema operativo. Algunos sistemas identifican dichos archivos con una extensión de nombre de archivo en particular, por
ejemplo, el sistema Microsoft Windows usa la extensión ".exe".
Es importante considerar el mecanismo para ejecutar un programa. Un programa es una lista de instrucciones
almacenadas en un archivo. Cuando ejecutamos un programa, el sistema operativo lee la lista de instrucciones y crea un
proceso que comprende una versión en memoria de las instrucciones, las variables utilizadas en el programa y algunos
otros metadatos relacionados, por ejemplo, con qué instrucción se va a utilizar. ejecutado a continuación. En conjunto, esto
se denomina imagen del proceso y los metadatos y las variables (es decir, las partes cambiables de la imagen) se denominan
estado del proceso, ya que los valores de estos describen y dependen del estado del proceso (es decir, el estado del
proceso). progreso del proceso a través del conjunto de instrucciones, caracterizado por la secuencia específica de valores
de entrada en esta instancia de ejecución del programa). El estado es lo que diferencia varios procesos de un mismo
programa. El sistema operativo identificará el proceso con un único
Identificador de proceso ( PID). Esto es necesario porque puede haber muchos procesos ejecutándose al mismo tiempo en
un sistema informático moderno y el sistema operativo tiene que realizar un seguimiento del proceso en términos de, entre
otras cosas, quién lo posee, cuánto recurso de procesamiento está utilizando, y qué dispositivos IO está utilizando.
La semántica (lógica) representada por la secuencia de acciones que surgen del funcionamiento del proceso es
la misma que se expresa en el programa. Por ejemplo, si el programa suma dos números primero y luego duplica el
resultado, el proceso realizará estas acciones en el mismo orden y proporcionará una respuesta predecible por
alguien que conozca tanto la lógica del programa como los valores de entrada utilizados.
Considere el escenario de ejecución de programa más simple como se ilustra en Figura 2.1 .
Figura 2.1 muestra el pseudocódigo para un proceso muy simple. El pseudocódigo es una forma de representar
las principales acciones lógicas del programa, en un lenguaje casi natural, pero sin ambigüedades, que es
independiente de cualquier lenguaje de programación en particular. El pseudocódigo no contiene el nivel completo
de detalle que se proporcionaría si el programa real se proporcionara en un lenguaje en particular, como C ++, C # o
Java, sino que se utiliza como ayuda para explicar la función y el comportamiento general de los programas.
El programa ilustrado en Figura 2.1 es un caso muy restringido. El procesamiento en sí puede ser lo que desee,
pero sin entrada ni salida, el programa no puede ser útil, porque el usuario no puede influir en su comportamiento
y el programa no puede señalar sus resultados al usuario.
Un escenario más útil se ilustra en Figura 2.2 .
Comienzo
Hacer un poco de procesamiento
Final
FIGURA 2.1
Comienzo
Obtener información
Hacer un poco de procesamiento
Producir salida
Final
FIGURA 2.2
Esto sigue siendo conceptualmente muy simple, pero ahora, hay una manera de dirigir el procesamiento para hacer
algo útil (la entrada) y una manera de averiguar el resultado (la salida).
Quizás la forma más fácil de construir una imagen mental de lo que está sucediendo es asumir que el dispositivo de entrada es
un teclado y el dispositivo de salida es la pantalla de visualización. Podríamos escribir un programa sumador con la lógica que se
muestra en Figura 2.3 .
Si escribo el número 3 y luego el número 6 (los dos valores de entrada), la salida en la pantalla será
9.
Más exactamente, un proceso obtiene su entrada de un flujo de entrada y envía su salida a un flujo de salida. El proceso
cuando se ejecuta no controla ni accede directamente a dispositivos como el teclado y la pantalla de visualización; es el
trabajo del sistema operativo pasar valores hacia y desde dichos dispositivos. Por lo tanto, podríamos expresar la lógica del
programa Adder como se muestra en Figura 2.4 .
Cuando se ejecuta el programa, el sistema operativo puede (de forma predeterminada) asignar el dispositivo de teclado al flujo
de entrada y la pantalla de visualización al flujo de salida.
Definamos un segundo programa Doubler (ver Figura 2.5 ).
Podemos ejecutar el programa Doubler, creando un proceso con el flujo de entrada conectado al teclado
y el flujo de salida conectado a la pantalla de visualización. Si escribo el número 5 (la entrada), la salida en la
pantalla será 10.
Comienzo
FIGURA 2.3
Dos aspectos del proceso, el pseudocódigo y una representación en bloque, que muestra el proceso con sus
entradas y salidas.
Comienzo
FIGURA 2.4
Comienzo
FIGURA 2.5
El sistema operativo
conecta las dos corrientes
Aporte
{X, Y} {Z} {V} {W} Salida
Sumador Doblador
Arroyo Producción Aporte Arroyo
corriente de corriente
FIGURA 2.6
Método
Esta actividad se realiza en tres pasos:
1. Ejecute el programa Adder.exe en una ventana de comandos. Navegue a la subcarpeta ProcessView del directorio
SystemsProgramming y luego ejecute el programa Adder escribiendo Adder.exe seguido de la tecla Enter. El programa ahora se
ejecutará como un proceso. Espera hasta que se hayan escrito dos números (cada uno seguido de la tecla Intro) y luego agrega los
números y muestra el resultado, antes de salir.
2. Ejecute el programa Doubler.exe de la misma manera que para el programa Adder.exe en el paso 1. El proceso espera a
que se escriba un solo número (seguido de la tecla Intro) y luego duplica el número y muestra el resultado, antes de salir. .
3. Ejecute los programas Adder y Doubler en una canalización. El objetivo es tomar dos números ingresados por el usuario y mostrar el doble de
la suma de los dos números. Podemos escribir esto de forma formulada como:
Z = (2 * XY+ )
dónde X y Y son los números ingresados por el usuario y Z es el resultado que se muestra. Por ejemplo, si X = 16 y Y = 18, el
valor de Z será 68.
La sintaxis de la línea de comandos para lograr esto es Adder | Doubler.
El primer proceso esperará a que el usuario ingrese dos números. La salida del proceso Adder se asignará
automáticamente a la entrada del proceso Doubler (porque se utilizó |), y el proceso Doubler duplicará el valor que
recibe, que luego se muestra en la pantalla.
La siguiente imagen muestra los resultados cuando se siguen los tres pasos anteriores:
28 CAPITULO 2 LA VISTA DEL PROCESO
Gastos esperados
Para cada uno de los dos primeros pasos, verá que el programa se ejecutó como un proceso que sigue la lógica del programa (consulte el código
fuente en el archivo Adder.cpp y también Doubler.cpp). Tenga en cuenta que, de forma predeterminada, el sistema operativo asignó
automáticamente el teclado como dispositivo de entrada y la pantalla de visualización como dispositivo de salida, aunque esto no se especificó en
el programa. También verá cómo el sistema operativo reasigna los flujos de entrada y salida de los procesos cuando se utiliza la canalización. Los
programas individuales se utilizan como bloques de construcción para construir una lógica más compleja.
Reflexión
1. Estudie el código fuente de los dos programas y lea los comentarios incorporados.
2. Piense en los diferentes roles del programador que escribió los dos programas, el sistema operativo y el usuario que escribe la línea de comando
y proporciona la entrada y cómo cada uno influye en el resultado final. Es importante darse cuenta de que el comportamiento general
observado en esta actividad es el resultado de una combinación de la lógica del programa, el control de ejecución del sistema operativo y los
datos de entrada del usuario.
1. Los procesos pueden comunicarse; por ejemplo, la salida de un proceso puede convertirse en la entrada para
otro proceso.
2. Para que los procesos se comuniquen, debe haber un mecanismo que facilite esto.
En el escenario simple aquí, el sistema operativo facilita el IPC utilizando el mecanismo de
canalización como un conector entre el flujo de salida de un proceso y el flujo de entrada del
otro.
3. La funcionalidad de nivel superior se puede construir a partir de bloques de construcción lógicos más simples. En el ejemplo,
la lógica visible externamente es sumar dos números y duplicar el resultado; el usuario ve el resultado final
en la pantalla de visualización y no necesita saber cómo se logró. Este es el concepto de modularidad y es
necesario para lograr un comportamiento sofisticado en los sistemas mientras se mantiene la complejidad
de los elementos lógicos individuales a niveles manejables.
los procesos en el sistema y seleccionar cuál debe ejecutarse en un momento dado. Esta es la forma más
obvia y directa de interacción entre los procesos y el sistema operativo, aunque la interacción indirecta
también surge a través de la forma en que el sistema operativo controla los otros recursos que un proceso
en particular puede estar usando.
Las primeras computadoras eran muy caras y, por lo tanto, eran compartidas por grandes comunidades de usuarios.
Por ejemplo, es posible que una gran computadora central se haya compartido en toda una universidad. Una forma común
de satisfacer las necesidades de muchos usuarios era operar en "modo por lotes". Esto requería que los usuarios enviaran
sus programas para ejecutarlos en una “cola de lotes” y los operadores del sistema supervisarían la ejecución de estos
programas uno por uno. En términos de gestión de recursos, es muy sencillo; a cada proceso se le asignan todos los
recursos de la computadora, hasta que se completa la tarea o se puede imponer un límite de tiempo de ejecución. La
desventaja de este enfoque es que un programa puede mantenerse en la cola durante varias horas. Muchos programas se
ejecutaron en lotes durante la noche, lo que significaba que los resultados de la ejecución no estaban disponibles hasta el
día siguiente.
Las misiones espaciales Apolo a la Luna en las décadas de 1960 y 1970 proporcionan un ejemplo interesante del uso temprano de
computadoras en aplicaciones críticas de control. La potencia de procesamiento de la computadora de guía Apollo a bordo 1 ( AGC) era una mera
fracción de la de los sistemas modernos, a menudo comparada de manera simplista con la de una calculadora de escritorio moderna, aunque a
través de un diseño sofisticado, la potencia de procesamiento limitada de esta computadora se utilizó de manera muy efectiva. El AGC de un solo
núcleo tenía un programador en tiempo real, que podía admitir ocho procesos simultáneamente; cada uno tenía que ceder si esperaba otra
tarea de mayor prioridad. El sistema también admitía tareas periódicas controladas por temporizador dentro de los procesos. Este era un
sistema relativamente simple para los estándares actuales, aunque los aspectos en tiempo real seguirían siendo un desafío desde el punto de
vista del diseño y las pruebas. En particular, se trataba de un sistema cerrado en el que todos los tipos de tareas se conocían de antemano y las
simulaciones de las diversas combinaciones de cargas de trabajo podían ejecutarse antes de la implementación para garantizar la corrección en
términos de tiempo y uso de recursos. Algunos sistemas modernos se cierran de esta manera, en particular los sistemas integrados, como el
controlador de su lavadora o el sistema de gestión del motor de su automóvil (ver más abajo). Sin embargo, las computadoras de uso general
que incluyen computadoras de escritorio, servidores en bastidores, teléfonos inteligentes y tabletas están abiertas y pueden ejecutar programas
que el usuario elija, en cualquier combinación; por tanto, el planificador debe poder garantizar un uso eficiente de los recursos y también
proteger los requisitos de calidad de servicio (QoS) de las tareas con respecto, por ejemplo, a la capacidad de respuesta. y las tabletas están
abiertas y pueden ejecutar programas que el usuario elija, en cualquier combinación; por tanto, el planificador debe poder garantizar un uso
eficiente de los recursos y también proteger los requisitos de calidad de servicio (QoS) de las tareas con respecto, por ejemplo, a la capacidad de
respuesta. y las tabletas están abiertas y pueden ejecutar programas que el usuario elija, en cualquier combinación; por tanto, el planificador debe poder garantizar un
Algunos sistemas integrados actuales proporcionan ejemplos útiles de una computadora de propósito fijo. Dicho
sistema solo realiza una función especificada en el tiempo de diseño y, por lo tanto, no necesita cambiar entre múltiples
aplicaciones. Considere, por ejemplo, el sistema informático que está integrado en una lavadora típica. Toda la
funcionalidad ha sido predecida en el momento del diseño. El usuario puede proporcionar entradas a través de los controles
en la interfaz de usuario, como configurar la temperatura de lavado o la velocidad de centrifugado, y así configurar el
comportamiento, pero no puede cambiar la funcionalidad. Hay un solo programa precargado
1 La especificación técnica real de la computadora de guía Apollo, en uso desde 1966 hasta 1975, era un procesador de 16 bits basado
en lógica discreta resistor-transistor (que es anterior a la lógica superior transistor-transistor) y operaba a una frecuencia de 2MHz.
Tenía 2kB de RAM (que se usa para almacenar valores variables, así como información de control del sistema, como la pila) y 36kB de
memoria de solo lectura, ROM (que se usa para almacenar el código del programa y valores de datos constantes). En su día, esto
representó tecnología de punta. Para comprender los aumentos de rendimiento y la reducción de costos que se ha producido desde
la época de las misiones Apollo, un microcontrolador Atmel ATmega32 es una computadora de un solo chip de 8 bits que opera a
16MHz y tiene 2kB de RAM y 33kB de ROM (en realidad, memoria Flash de 32k). , que se puede utilizar de la misma forma que la ROM, £
5 en el momento de escribir este artículo.
30 CAPITULO 2 LA VISTA DEL PROCESO
Prerrequisitos
Requiere un sistema operativo Microsoft Windows.
Método
Esta actividad se realiza en dos partes, utilizando dos herramientas diferentes para inspeccionar el conjunto de procesos que se ejecutan en la computadora.
Parte 1
Inicie el Administrador de tareas presionando las teclas Control, Alt y Suprimir simultáneamente y seleccione "Iniciar Administrador de tareas" de las opciones
presentadas. Luego, seleccione la pestaña Aplicaciones y vea los resultados, seguida de la pestaña Procesos, y también vea los resultados. Asegúrese de que la casilla
de verificación "Mostrar procesos para todos los usuarios" esté marcada, para que pueda ver todo lo que está presente. Tenga en cuenta que este procedimiento
puede variar según las diferentes versiones de los sistemas operativos de Microsoft.
Parte 2
Explore los procesos presentes en el sistema utilizando el comando TASKLIST que se ejecuta desde una ventana de comandos. Este comando ofrece
una presentación de información diferente a la proporcionada por el Administrador de tareas. Escriba TASKLIST /? para ver la lista de parámetros que
se pueden utilizar. Explore los resultados obtenidos utilizando algunos de estos parámetros de configuración para ver qué puede averiguar sobre los
procesos en el sistema y su estado actual.
Reflexión
Probablemente tendrá una buena idea de qué aplicaciones ya se están ejecutando (después de todo, usted es el usuario), por lo que la lista de
aplicaciones no le sorprenderá. En los ejemplos de mi computadora, puede ver que estaba editando un capítulo de este libro mientras escuchaba
un CD a través de la aplicación del reproductor multimedia.
Sin embargo, probablemente verá una larga lista de procesos al seleccionar la pestaña Procesos. Mire de cerca estos
procesos; ¿Es esto lo que esperabas? ¿Tiene alguna idea de lo que están haciendo todos estos o por qué son necesarios?
32 CAPITULO 2 LA VISTA DEL PROCESO
Tenga en cuenta que muchos de los procesos que se ejecutan son procesos del "sistema" que forman parte del sistema operativo o están
relacionados con la gestión de recursos; los controladores de dispositivo son un buen ejemplo de esto.
Tenga en cuenta especialmente el "Proceso inactivo del sistema", que en el ejemplo anterior "se ejecuta" durante el 98% del tiempo. Este pseudoproceso se ejecuta
cuando no hay procesos reales listos para ejecutarse, y la parte del tiempo de CPU que obtiene se usa a menudo como una indicación inversa de la carga general en la
computadora.
en estos sistemas, y este programa se ejecuta como el único proceso. Muchas plataformas de sistemas integrados tienen
recursos muy limitados (especialmente en términos de poca memoria y baja velocidad de procesamiento) y, por lo tanto, no
pueden permitirse los gastos generales adicionales de tener un sistema operativo.
Ahora, teniendo en cuenta cuántos procesos están activos en su computadora, considere cuántos procesos se pueden ejecutar
en un momento dado. Para los sistemas más antiguos, es probable que sea solo 1. Para los sistemas más nuevos, que son "de doble
núcleo", por ejemplo, la respuesta es 2, o 4 si es "de cuatro núcleos". Incluso si tuviéramos quizás 32 núcleos, normalmente todavía
tendríamos más procesos que núcleos.
Básicamente, la necesidad de programar es que tiende a haber más procesos activos en una computadora que
unidades de procesador que pueden manejarlos. Si este es el caso, entonces algo tiene que arbitrar; esta es una de
las cosas más importantes que hace el sistema operativo.
Se han ideado varias técnicas de programación (algoritmos) diferentes. Estos tienen características diferentes,
siendo el diferenciador más importante la base sobre la que se seleccionan las tareas para su ejecución.
Dado que generalmente hay más procesos que procesadores, se puede decir que los procesos compiten por el
procesador. Para decirlo de otra manera, cada uno de los procesos tiene trabajo que hacer y es importante que
eventualmente completen su trabajo, pero en un momento dado, algunos de estos pueden ser más importantes o urgentes
que otros. El planificador debe asegurarse de que, siempre que sea posible, el procesador siempre esté siendo utilizado por
un proceso, de modo que no se desperdicien recursos. Por lo tanto, el rendimiento del programador se analiza más
comúnmente en términos de la eficiencia resultante del sistema, y esto se mide en términos del porcentaje de tiempo que
la unidad de procesamiento se mantiene ocupada. “Mantener el procesador ocupado” se expresa comúnmente como el
objetivo principal del planificador. Figuras 2.7 y 2.8 ilustrar este concepto de eficiencia.
Como se muestra en Figura 2.7 , el programa A utiliza 8 de los 10 intervalos de tiempo del procesador y, por lo tanto, tiene una eficiencia del 80%.
Los dos intervalos de tiempo que no se utilizaron se pierden, ese recurso no se puede recuperar. Otra forma de pensar en
esta situación es que este sistema está realizando el 80% del trabajo que posiblemente podría hacer, por lo que está
trabajando al 80% de su tasa máxima posible.
Figura 2.8 muestra el escenario ideal en el que se utiliza cada intervalo de tiempo y, por lo tanto, es 100% eficiente. Este
sistema está realizando todo el trabajo que podría hacer, por lo que está funcionando a su máxima velocidad posible.
P1 P2 P3 P1 No usado P3 P1 No usado P3 P2
1 2 3 4 5 6 7 8 9 10
Porción de tiempo
FIGURA 2.7
El Programa A usa 8 de cada 10 intervalos de tiempo (un sistema con tres procesos).
2.3 PROGRAMACIÓN DEL PROCESO 33
P1 P2 P3 P1 P3 P1 P3 P1 P3 P2
1 2 3 4 5 6 7 8 9 10
Porción de tiempo
FIGURA 2.8
El Programa B usa 10 de cada 10 intervalos de tiempo (un sistema con tres procesos).
P1 P2 P3 P1 P3 P1 P3 P1
1 2 3 4 5 6 7 8
(a) Porción de tiempo
P1 P2 P3 P1
1 2 3 4
(B) Porción de tiempo
FIGURA 2.9
ing imágenes. En los sistemas de procesamiento, los cuantos más cortos dan lugar a actividades de programación más frecuentes, lo que
absorbe parte del recurso de procesamiento. La proporción de esta sobrecarga como una fracción del tiempo total de procesamiento disponible
aumenta a medida que los cuantos se hacen más pequeños y, por lo tanto, el sistema puede realizar un trabajo menos útil en un período de
tiempo determinado, por lo que la tasa de cambio de contexto óptima no es simplemente la tasa más rápida que puede ser logrado.
En la parte (b) de Figura 2.9 , los cuantos se han incrementado hasta aproximadamente el doble del tamaño de los
se muestra en la parte a. Esto aumenta la tosquedad del proceso de intercalado y podría afectar la capacidad de respuesta de las
tareas, especialmente aquellas que tienen fechas límite o limitaciones de tiempo real asociadas con su trabajo. Para obtener una
comprensión inicial de la forma en que esto puede materializarse, imagine una interfaz de usuario que tenga una respuesta
entrecortada, en la que parezca congelarse momentáneamente entre ocuparse de su entrada. Esta respuesta desigual podría ser un
síntoma de que el proceso que controla la interfaz de usuario no está obteniendo una parte suficiente del tiempo de procesamiento o
que el tiempo de procesamiento no se comparte de manera suficientemente detallada. A pesar de los impactos en la capacidad de
respuesta de las tareas que pueden surgir al tener cuantos más grandes, son más eficientes porque las actividades de programación
del sistema operativo ocurren con menos frecuencia. absorbiendo así una proporción menor del recurso de procesamiento total; para
el escenario mostrado, la sobrecarga de programación para el sistema (b) es la mitad que la del sistema (a).
La elección del tamaño del cuanto de tiempo es un problema de optimización entre, por un lado, la eficiencia general
del procesamiento y, por otro, la capacidad de respuesta de las tareas. Hasta cierto punto, el valor ideal depende de la
naturaleza de las aplicaciones que se ejecutan en el sistema. Los sistemas en tiempo real (es decir, los que tienen
aplicaciones con una función o dependencia basada en el tiempo, como la transmisión de video en la que cada cuadro debe
procesarse con requisitos de tiempo estrictos) generalmente necesitan usar cuantos más cortos para lograr un entrelazado
más fino y así garantizar el no se pierden los plazos de las solicitudes. Los problemas de programación específicos que
surgen de los plazos o las limitaciones de los procesos en tiempo real se discutirán con más detalle más adelante.
Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido los recursos complementarios necesarios como se explica en Actividad P1 .
Método
Esta actividad se lleva a cabo en tres partes, utilizando un programa sencillo que escribe caracteres en la pantalla de forma periódica, para ilustrar
algunos aspectos de cómo el planificador gestiona los procesos.
Parte 1
1. Ejecute el programa PeriodicOutput.exe en una ventana de comandos. Navegue a la subcarpeta "ProcessView" y luego ejecute el
programa PeriodicOutput escribiendo PeriodicOutput.exe seguido de la tecla Intro. El programa ahora se ejecutará como un
proceso. Imprimirá un mensaje de error porque necesita que se escriba información adicional después del nombre del programa. La
información adicional proporcionada de esta manera se denomina "argumentos de la línea de comandos" y es una forma muy útil e
importante de controlar la ejecución de un programa.
2. Mire el mensaje de error producido. Nos dice que debemos proporcionar dos piezas adicionales de información, que son la
longitud del intervalo de tiempo (en ms) entre la impresión de caracteres en la pantalla y el carácter que se imprimirá al
final de cada intervalo de tiempo.
3. Ejecute el programa PeriodicOutput.exe nuevamente, esta vez proporcionando los parámetros de línea de comandos requeridos.
2.3 PROGRAMACIÓN DEL PROCESO 35
Por ejemplo, si escribe PeriodicOutput 1000 A, el programa imprimirá una serie de A, una por segundo (1000ms).
Como otro ejemplo, si escribe PeriodicOutput 100 B, el programa imprimirá una serie de B, una cada 1/10 de segundo. Experimente
con otros valores. Tenga en cuenta que el programa siempre se ejecutará durante 10 s (10,000 ms) en total, por lo que el número de
caracteres impresos siempre será 10,000 dividido por el primer valor del parámetro. Examine el código fuente de este programa (que se
proporciona como parte de los recursos complementarios) y relacione el comportamiento del programa con las instrucciones.
Parte 2
1. Ahora ejecutaremos varias copias del programa PeriodicOutput.exe al mismo tiempo. Para hacer esto, usaremos un archivo por lotes.
Un archivo por lotes es un script que contiene una serie de comandos que debe ejecutar el sistema; Para el propósito de esta
actividad, puede pensar en él como un metaprograma que nos permite especificar cómo se ejecutarán los programas. El archivo por
lotes que usaremos se llama “PeriodicOutput_Starter.bat”. Puede examinar el contenido de este archivo escribiendo "cat
PeriodicOutput_Starter.bat" en el símbolo del sistema. Verá que este archivo contiene tres líneas de texto, cada una de las cuales es
un comando que hace que el sistema operativo inicie una copia del programa PeriodicOutput.exe, con parámetros ligeramente
diferentes.
2. Ejecute el archivo por lotes escribiendo su nombre, seguido de la tecla Intro. Observa lo que pasa.
El archivo por lotes establece los intervalos entre la impresión de caracteres en 20 ms para cada uno de los tres procesos. Esto está en una
escala similar al tamaño de un cuanto de programación típico y produce un entrelazado bastante regular (que surge porque los tres procesos pasan
un tiempo relativamente largo "durmiendo" entre su breve actividad de salida de un solo carácter a la vez, dando al programador suficiente tiempo
para administrar otras actividades en el sistema).
Parte 3
Experimente con diferentes configuraciones editando el archivo por lotes (use un editor de texto simple como el Bloc de notas y no use un procesador
de texto, ya que esto puede agregar caracteres especiales, que confunden la parte del intérprete de comandos del sistema operativo). Intente cambiar
el intervalo de tiempo o agregar algunos procesos adicionales. En particular, pruebe con un intervalo de impresión de caracteres más corto para los
tres procesos, como 5 ms. Cuanto más rápida sea la tasa de actividad en los tres procesos, menos probable será que el programador logre el
entrelazado regular que vimos en la parte 2 anterior. Debido a que la computadora tiene otros procesos en ejecución (como vimos en Actividad P2 ), es
posible que no veamos un patrón intercalado perfecto, y si ejecutamos el archivo por lotes varias veces, deberíamos esperar resultados ligeramente
diferentes cada vez; es importante que se dé cuenta de por qué es así.
Reflexión
Ha visto que el entrelazado simple logrado al ejecutar tres procesos desde un archivo por lotes puede conducir a un patrón regular
cuando el sistema no está estresado (es decir, cuando los procesos pasan la mayor parte de su tiempo bloqueados porque realizan IO,
entre episodios cortos de ejecución y volver a bloquearse). Esencialmente, hay suficiente capacidad de CPU de repuesto en esta
configuración para que cada proceso obtenga el tiempo de procesamiento que necesita, sin pasar mucho tiempo en el estado listo.
Sin embargo, dependiendo de la experimentación adicional que haya realizado en la parte 3, probablemente habrá descubierto que la
regularidad del entrelazado depende de hecho del número y comportamiento de otros procesos en el sistema. En este experimento, no
hemos establecido ningún requisito específico de sincronía entre los tres procesos (es decir, el sistema operativo no tiene conocimiento de
que se requiere un patrón de entrelazado regular), por lo que la irregularidad en el patrón sería aceptable.
Además de comprender el comportamiento de la programación, también debería haber adquirido un nivel de competencia con
respecto a la configuración y ejecución de procesos, el uso de argumentos de línea de comandos y archivos por lotes, y una comprensión
de los efectos del programador.
Una observación interesante es que, si bien durante las últimas dos décadas las velocidades de funcionamiento del procesador
central (el número de ciclos de CPU por segundo) han aumentado drásticamente, el rango típico de tamaños cuánticos de
programación se ha mantenido generalmente sin cambios. Una razón principal de esto es que la complejidad de los sistemas y las
aplicaciones que se ejecutan en ellos también han aumentado a altas velocidades, lo que requiere más procesamiento para lograr su
rica funcionalidad. La compensación subyacente entre la capacidad de respuesta (tamaño cuántico más pequeño) y los gastos
generales bajos (tamaño cuántico más grande) permanece, independientemente de la rapidez con la que opere la CPU. Los tamaños
cuánticos en los sistemas modernos suelen estar en el rango de aproximadamente 10 ms a aproximadamente 200 ms, aunque en
plataformas muy rápidas se utilizan cuantos más pequeños (hasta aproximadamente 1 ms). Se puede utilizar una variedad de
tamaños cuánticos como un medio para implementar la prioridad del proceso; esto funciona sobre la base simple de que los procesos
de mayor prioridad deberían recibir una mayor proporción de los recursos de procesamiento disponibles. Por supuesto, si los
procesos son intensivos en E / S, se bloquean cuando realizan E / S independientemente de la prioridad.
Otro factor principal por el que los tamaños cuánticos no se han reducido en línea con los avances tecnológicos es que la
percepción humana de la capacidad de respuesta no ha cambiado. Es importante desde el punto de vista de la usabilidad que los
procesos interactivos respondan a las acciones del usuario (como escribir una tecla o mover el mouse) de manera oportuna,
idealmente para lograr la ilusión de que la aplicación del usuario se ejecuta de forma continua, ininterrumpida, en la computadora.
Considere una actividad de actualización de pantalla; el requisito de procesamiento entre bastidores ha aumentado debido, por
ejemplo, a pantallas de mayor resolución y bibliotecas de interfaz de usuario con ventanas más complejas. Esto significa que una
actualización de pantalla requiere más ciclos de CPU en los sistemas modernos que en los sistemas anteriores más simples.
2.3 PROGRAMACIÓN DEL PROCESO 37
Para los procesos interactivos, un cuanto debe ser lo suficientemente grande como para que se pueda completar el procesamiento
asociado con la solicitud del usuario. De lo contrario, una vez que finaliza el cuanto, el proceso del usuario tendrá que esperar, mientras que
otros procesos se turnan, introduciendo un retraso en el proceso del usuario. Además, como se explicó anteriormente, un tamaño cuántico más
pequeño incurre en un nivel más alto de sobrecarga del sistema debido a la actividad de programación. Entonces, aunque la velocidad del ciclo
sistema de programación de manera genérica, aunque también ocurren algunos otros estados y se presentarán más adelante.
2 Esto se considera extremadamente lento en comparación con otras tecnologías disponibles en la actualidad, pero los sistemas integrados, como los sistemas
de sensores, tienden a tener pocos requisitos de procesamiento de información. La tecnología más lenta da lugar a dos ventajas: tiene un consumo de
energía muy bajo y, por lo tanto, los nodos de sensores pueden funcionar con baterías durante períodos prolongados; y los dispositivos son muy baratos, por
lo que se pueden implementar en aplicaciones a gran escala.
38 CAPITULO 2 LA VISTA DEL PROCESO
Los tres estados ( corriendo, listo, y obstruido) se puede describir en un diagrama de transición de estado, llamado
así porque el diagrama muestra los estados permitidos del sistema y las transiciones que pueden ocurrir para pasar
de un estado a otro. Un proceso recién creado se colocará inicialmente en el Listo Expresar.
Figura 2.10 muestra el diagrama de transición de tres estados. Debe leer este diagrama desde la perspectiva de un
proceso en particular. Es decir, el diagrama se aplica individualmente a cada proceso del sistema; así, por ejemplo, si hay
tres procesos {A, B, C}, podemos describir cada proceso como si estuviera en un estado particular en un momento dado. Las
mismas reglas de transición se aplican a todos los procesos, pero los estados pueden ser diferentes en cualquier momento.
Las transiciones que se muestran son las únicas permitidas. La transición de Listo a corriendo ocurre cuando el sistema
operativo selecciona el proceso para ejecutarse; esto se conoce como envío. Los diversos algoritmos de programación
seleccionan el siguiente proceso que se ejecutará en función de diferentes criterios, pero la esencia general es que el
proceso seleccionado ha alcanzado la parte superior de la cola de listas porque ha estado esperando más tiempo o se ha
elevado a la parte superior porque tiene una mayor prioridad que otros en la cola.
La transición de corriendo a Listo ocurre cuando el proceso ha agotado su tiempo cuántico. Esto se llama preferencia y
se hace para asegurar justicia y para prevenir inanición; este término se utiliza para describir la situación en la que un
proceso acapara la CPU, mientras que otro proceso se mantiene en espera, posiblemente de forma indefinida.
La transición de corriendo a obstruido ocurre cuando el proceso ha solicitado una operación de E / S y debe
esperar a que responda el subsistema de E / S más lento (lento con respecto a la CPU). El subsistema IO podría ser
un dispositivo de almacenamiento como una unidad de disco duro magnético o una unidad óptica (CD o DVD), o
podría ser un dispositivo de salida como una impresora o una pantalla de video. La operación de IO también podría
ser una operación de red, quizás esperando a que se envíe o reciba un mensaje. Cuando el dispositivo IO es un
dispositivo de entrada del usuario, como un mouse o un teclado, el tiempo de respuesta se mide en segundos o
quizás décimas de segundo, mientras que la CPU en una computadora moderna es capaz de ejecutar quizás varios
miles de millones de instrucciones en 1s ( esta es una cantidad increíble). Por lo tanto, en el tiempo que transcurre
entre que un usuario escribe las teclas, la CPU puede ejecutar muchos millones de instrucciones,
Transición TERMINAR
Correr
FIGURA 2.10
La transición de obstruido a Listo ocurre cuando la operación IO se ha completado. Por ejemplo, si el motivo del
bloqueo era esperar la pulsación de una tecla del usuario, una vez que se haya recibido la pulsación, se decodificará
y se proporcionarán los datos del código de tecla relevante en el flujo de entrada del proceso. 3 En este punto, el
proceso puede continuar procesando, por lo que se mueve al estado listo. Del mismo modo, si el motivo del
bloqueo fue esperar a que llegara un mensaje a través de una conexión de red, el proceso se desbloqueará una vez
que se haya recibido un mensaje. 4 y se coloca en un búfer de memoria accesible al proceso (es decir, el sistema
operativo mueve el mensaje al espacio de memoria del proceso).
Tenga en cuenta que cuando se desbloquea un proceso, no vuelve a entrar corriendo Estado directamente. Debe pasar por el Listo
Expresar.
Como se explicó anteriormente, el bloqueo y el desbloqueo están vinculados a eventos reales en el comportamiento del proceso y en la
respuesta de los subsistemas IO, respectivamente. Para ilustrar esto, considere lo que ocurre cuando un proceso escribe en un archivo en un
disco duro. Un disco duro es un "dispositivo de bloque"; esto significa que los datos se leen y se escriben en el disco en bloques de tamaño fijo.
Este acceso por bloques se debe a que una gran fracción del tiempo de acceso lo ocupa el cabezal de lectura / escritura que se coloca sobre la
pista correcta donde se escribirán los datos y espera a que el disco gire al sector correcto (parte de una pista) donde se escribirán los datos. Por
lo tanto, sería extremadamente ineficiente y requeriría sistemas de control muy complejos si se escribiera un solo byte a la vez. En cambio, un
bloque de datos, quizás de muchos kilobytes, se lee o escribe secuencialmente una vez que la cabeza está alineada (la actividad de alineación se
denomina "búsqueda" o "búsqueda de cabeza"). Las velocidades de rotación del disco y las velocidades de movimiento del cabezal son muy
lentas en relación con las velocidades de procesamiento de las computadoras modernas. Entonces, si imaginamos un proceso que está
actualizando un archivo que está almacenado en el disco, podemos ver que habría muchos retrasos asociados con el acceso al disco. Además de
la primera búsqueda, si el archivo está fragmentado en el disco, como es habitual, cada nuevo bloque que se va a escribir requiere una nueva
búsqueda. Incluso después de que se completa una búsqueda, hay un retraso adicional (en el contexto de las operaciones de velocidad de la
CPU) ya que la velocidad real a la que se escriben o leen los datos en el disco también es relativamente muy lenta. Cada vez que el proceso
estuviera listo para actualizar un siguiente bloque de datos, el programador lo bloquearía. Una vez que se haya completado la operación del
disco, el programador volvería a mover el proceso a la cola lista. Cuando ingresa al estado de ejecución, procesará la siguiente sección del
archivo y luego, tan pronto como esté listo para escribir en el disco nuevamente, se bloqueará una vez más. Describiríamos esto como una tarea
intensiva en IO y esperaríamos que se bloqueara regularmente antes de agotar sus porciones de tiempo completo. Este tipo de tarea pasaría
una gran fracción de su tiempo en el estado bloqueado, como se ilustra en Describiríamos esto como una tarea intensiva en IO y esperaríamos
que se bloqueara regularmente antes de agotar sus porciones de tiempo completo. Este tipo de tarea pasaría una gran fracción de su tiempo en
el estado bloqueado, como se ilustra en Describiríamos esto como una tarea intensiva en IO y esperaríamos que se bloqueara regularmente
antes de agotar sus porciones de tiempo completo. Este tipo de tarea pasaría una gran fracción de su tiempo en el estado bloqueado, como se
3 Tenga en cuenta que un teclado es un ejemplo de " dispositivo de caracteres " —Esto significa que la entrada desde el teclado se procesa con una sola tecla. Las dos razones
principales de esto son la capacidad de respuesta de la aplicación y también para mantener la lógica de control simple. Para entender esto, considere la posible alternativa, es
decir, convertirlo en un “ dispositivo de bloqueo, ”Que devolvería un bloque de datos a la vez como, por ejemplo, lo hace un disco duro. El problema aquí es que el proceso
tendría que esperar un “bloque” completo de pulsaciones de teclas antes de responder (porque permanecería bloqueado hasta que el sistema operativo hubiera contado y
enviado al proceso el número de bytes requerido). Hay algunos escenarios en los que esto podría funcionar con cierto éxito, como cuando se escribe una gran cantidad de
pulsaciones de teclas como entrada a un procesador de texto, pero en la gran mayoría de los escenarios, sería inviable. Los procesadores de texto modernos realmente
aprovechan la base carácter por carácter del flujo de claves, ya que ejecutan subcomponentes como el corrector gramatical y el corrector ortográfico y autocompletar, incluso
4 Una interfaz de red es efectivamente una "Bloquear dispositivo" porque envía y recibe mensajes que son grupos de bytes de datos recopilados
en un búfer de memoria. El búfer en sí es un área de memoria reservada para almacenar el mensaje (esto se analiza en detalle en el capítulo
Vista de recursos) y tiene un tamaño fijo. Sin embargo, por razones de eficiencia, solo se transmitirá el mensaje real a través de la red, que puede
ser considerablemente más pequeña de lo que puede contener el búfer. Por lo tanto, los mensajes pueden variar en tamaño y, por lo tanto, la
cantidad de datos que se pasan hacia / desde el proceso puede variar en cada evento de envío o recepción.
40 CAPITULO 2 LA VISTA DEL PROCESO
Porción de tiempo
R B D R B ...
Hora
D ESTADO D READY
B Estado bloqueado
Proporción indicativa
del tiempo empleado en cada
uno de los tres estados
B
principales del proceso
IO proceso intenso. Realiza E / S con frecuencia y rara vez usa su segmento de tiempo
completo antes de ser bloqueado. Una vez que se desbloquea, se une a la cola lista
antes de volver a pasar al estado de ejecución.
FIGURA 2.11
Figura 2.11 ilustra la secuencia de estado del proceso para un proceso típico con uso intensivo de E / S. Las proporciones
reales que se muestran son indicativas, porque dependen de las características específicas del proceso y del sistema
anfitrión (incluida la combinación de otras tareas presentes, lo que afecta el tiempo de espera).
El ejemplo anterior sobre el acceso a un sistema de almacenamiento secundario, como una unidad de
disco, plantea un problema de rendimiento clave, que debe tenerse en cuenta durante el diseño del
comportamiento de la aplicación de bajo nivel, que afecta no solo a su rendimiento sino también a su
solidez. Los datos almacenados en la memoria mientras se ejecuta el programa son volátiles; esto significa
que si el proceso falla o se pierde la energía, los datos también se pierden. El almacenamiento secundario,
como un disco duro magnético, es no volátil o "persistente" y, por lo tanto, conservará los datos después de
que finalice el proceso e incluso después de que se apague la computadora. Claramente, existe un conflicto
entre el rendimiento máximo y la robustez máxima, porque los gastos generales de tiempo y costo del
enfoque más sólido de escribir datos en el disco cada vez que hay un cambio generalmente no son
tolerables desde el punto de vista del rendimiento.
Los procesos de computación intensiva tienden a usar su porción de tiempo completo y, por lo tanto, el sistema operativo se
apropia de ellos para permitir que otro proceso realice algún trabajo. Por lo tanto, la capacidad de respuesta de los procesos de
procesamiento intensivo se relaciona principalmente con la parte total de los recursos del sistema que cada uno obtiene. Por ejemplo,
si un proceso de cálculo intensivo fuera el único proceso presente, utilizaría todo el recurso de la CPU. Sin embargo, si tres tareas
intensivas en CPU estuvieran compitiendo en un sistema, el comportamiento del estado del proceso de cada una sería similar al que
se muestra en Figura 2.12 .
Se proporciona un ejemplo más para garantizar que el comportamiento sea claro. Si un proceso de computación intensiva
compite con cuatro procesos similares, cada uno representará una quinta parte del total de los recursos de computación disponibles,
como se ilustra en Figura 2.13 .
Porción de tiempo
R D R D R ...
Hora
ESTADO D READY
R
B Estado bloqueado
Proporción indicativa
de tiempo invertido en cada
D
B de los tres principales
estados de proceso
Proceso intensivo en computación, compitiendo con otros. Rara vez realiza IO y tiende a usar su
segmento de tiempo completo cada vez que se selecciona para ejecutar, por lo tanto, en este
escenario, el proceso usa un tercio de la potencia de procesamiento total del
sistema
FIGURA 2.12
Ilustración de comportamiento en tiempo de ejecución para un proceso de cálculo intensivo que compite con dos procesos similares.
Porción de tiempo
R D R D ...
Hora
Calcule un proceso intenso, compitiendo con otros cuatro. Aquí el proceso utiliza una quinta
parte de la potencia de procesamiento total del sistema.
FIGURA 2.13
Ilustración de comportamiento en tiempo de ejecución para un proceso de cálculo intensivo que compite con cuatro procesos similares.
Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido los recursos complementarios necesarios como se explica en Actividad P1 .
Método
Esta actividad se lleva a cabo en cuatro partes y utiliza un programa intensivo en CPU que, cuando se ejecuta como un proceso, realiza un cálculo
continuo sin realizar ninguna E / S y, por lo tanto, nunca se bloquea. Como tal, el proceso siempre utilizará por completo su intervalo de tiempo.
Parte 1 (Calibración)
1. Inicie el Administrador de tareas como en Actividad P2 y seleccione la pestaña "Procesos". Ordene la pantalla en orden de intensidad de uso de la CPU, la más alta
en la parte superior. Si no hay tareas de cálculo intensivo presentes, la cifra de uso de CPU más alta típica podría ser aproximadamente el 1%, mientras que
muchos procesos que tienen poca actividad mostrarán que usan el 0% del recurso de la CPU. Deje abierta la ventana del Administrador de tareas, al costado de la
pantalla.
2. Ejecute el programa CPU_Hog.exe en una ventana de comandos. Navegue a la subcarpeta "ProcessView" de la carpeta
SystemsProgramming y luego ejecute el programa CPU_Hog escribiendo CPU_Hog.exe seguido de la tecla Enter. El programa ahora
se ejecutará como un proceso. Examine las estadísticas del proceso en la ventana del Administrador de tareas mientras se ejecuta el
proceso CPU_Hog.
La captura de pantalla a continuación muestra que CPU_Hog consume tanto recurso de CPU como el programador le da, que en este caso es el
50%.
3. Ejecute el proceso CPU_Hog de nuevo y, esta vez, registre cuánto tarda en ejecutarse con un cronómetro. El programa realiza un
bucle un gran número fijo de veces, cada vez que realiza algún cálculo. El tiempo necesario será aproximadamente el mismo cada vez
que se ejecute el proceso, en una computadora en particular, siempre que la carga de trabajo en segundo plano no cambie
significativamente. Es necesario que lo programe en su propia computadora para obtener una línea de base para los experimentos
posteriores en esta actividad y que lo haga sin que se ejecuten otros procesos intensivos en la CPU. La precisión de cronometraje al
segundo más cercano es adecuada. Como referencia, tomó aproximadamente 39 s en mi computadora (no particularmente rápida).
Parte 2 (predicción)
1. Teniendo en cuenta el recurso compartido de CPU utilizado por una sola instancia de CPU_Hog, ¿qué crees que sucederá si
ejecutamos dos instancias de este programa al mismo tiempo? ¿Qué porcentaje del recurso de la CPU cree que obtendrá
cada copia del programa?
2. Según la cantidad de tiempo que tardó en ejecutarse una sola instancia de CPU_Hog, ¿cuánto tardará en ejecutarse cada
copia si se ejecutan dos copias al mismo tiempo?
3. Ahora, use el archivo por lotes llamado "CPU_Hog_2Starter.bat" para iniciar dos copias de CPU_Hog al mismo tiempo. Use la ventana
del Administrador de tareas para observar la proporción de recursos de CPU que obtiene cada proceso, y no olvide medir también el
tiempo de ejecución de los dos procesos (cuando se hayan completado, los verá desaparecer de la lista de procesos).
La captura de pantalla a continuación muestra que a cada proceso CPU_Hog se le dio aproximadamente la mitad del recurso total de la CPU. ¿Es esto
lo que esperabas?
2.3 PROGRAMACIÓN DEL PROCESO 43
2. Utilice el archivo por lotes denominado "CPU_Hog_3Starter.bat" para iniciar tres copias de CPU_Hog al mismo tiempo. Como antes, use la ventana del
Administrador de tareas para observar la proporción de recursos de CPU que obtiene cada proceso, y también recuerde medir el tiempo de ejecución de los tres
procesos (esto puede ser más complicado, ya que es posible que no todos terminen al mismo tiempo, pero un valor de tiempo promedio será suficiente para
Volviendo a nuestro experimento, este resultado significa que el primer proceso termina antes que los otros dos. Una vez que finaliza el primer
proceso, los dos restantes reciben el 50% del recurso de la CPU cada uno, por lo que su velocidad de procesamiento se ha acelerado. Grabé los
siguientes tiempos: primer proceso, 40; segundo y tercer proceso, aproximadamente 60s. Esto tiene sentido; en el punto donde termina el primer
proceso, los otros dos han tenido la mitad de recursos de procesamiento cada uno y, por lo tanto, están aproximadamente a la mitad de su tarea.
Una vez que tienen el 50% de los recursos de la CPU cada uno, les toma 20 segundos más completar la mitad restante de su trabajo, lo cual es
consistente.
En este caso, cada proceso usó aproximadamente el 25% del recurso de la CPU y cada uno tardó aproximadamente 80 segundos en ejecutarse, lo cual es
Reflexión
Este experimento ilustra algunos aspectos muy importantes de la programación y de la forma en que interactúan el comportamiento del programador
y el comportamiento de los procesos. Esto incluye la complejidad de predecir los tiempos de ejecución y la medida en que el comportamiento de los
programadores puede ser sensible a la combinación de procesos en el sistema.
En primer lugar, vemos que el recurso de la CPU es finito y debe compartirse entre los procesos del sistema. También vemos que a medida que se completan
algunos procesos, la parte de recursos se puede reasignar a los procesos restantes. También es importante darse cuenta de que un proceso necesitará una cantidad
predecible de tiempo de ejecución de la CPU para ejecutarse. Sin embargo, debido a que el proceso generalmente obtiene menos del 100% del recurso de la CPU, su
tiempo de ejecución real (su tiempo en el sistema, incluso cuando está en la cola lista) es mayor que su tiempo de ejecución de la CPU, y el tiempo de ejecución total es
depende de la carga del sistema, que en general fluctúa continuamente. Estos experimentos han arañado la superficie de la complejidad de la programación, pero
brindan información valiosa sobre los problemas en cuestión y nos brindan las habilidades para explorar más a fondo. Puede diseñar más experimentos empíricos
basados en las herramientas que he proporcionado (por ejemplo, puede editar los archivos por lotes para activar diferentes combinaciones de programas), o puede
utilizar las simulaciones de programación de Operating Systems Workbench que admiten la experimentación flexible en una amplia gama de condiciones y
combinaciones de procesos y también le permiten decidir qué programador utilizar. La simulación de programación introductoria proporciona configuraciones con
tres programadores diferentes y combinaciones de cargas de trabajo de hasta cinco procesos de tres tipos diferentes. La simulación de programación avanzada admite
un algoritmo de programación adicional y facilita la configuración del tamaño del cuanto de programación de la CPU y también la latencia del dispositivo para procesos
intensivos de IO. El banco de trabajo se presentará más adelante en este capítulo. puede editar los archivos por lotes para activar diferentes combinaciones de
programas), o puede usar las simulaciones de programación de Operating Systems Workbench que admiten la experimentación flexible en una amplia gama de
condiciones y combinaciones de procesos y también le permiten decidir qué programador usar. La simulación de programación introductoria proporciona configuraciones con tres programadores difere
pero también puede realizar su IO a otros dispositivos, incluidas las unidades de disco y la red; por ejemplo,
puede ser un proceso de servidor que recibe solicitudes de una conexión de red y envía respuestas a través
de la red.
Se muestran ejemplos de programas intensivos en IO en el pseudocódigo en Figura 2.14 (E / S interactiva con el
usuario), Figura 2.15 (E / S basada en disco) y Figura 2.16 (E / S basada en red).
2.3 PROGRAMACIÓN DEL PROCESO 45
Comienzo
Establecer suma = 0
Repite 100 veces
{
Ingrese un número
Sumar número para sumar
}
Mostrar suma
Final
FIGURA 2.14
Figura 2.14 muestra el pseudocódigo de un proceso intensivo de E / S interactivo que pasa una gran parte de su tiempo
esperando una entrada. El tiempo para realizar la operación de suma sería mucho menor que una millonésima de segundo,
por lo que si el usuario ingresa números a una velocidad de 1 por segundo, el programa podría pasar más del 99,9999% de
su tiempo esperando la entrada.
Figura 2.15 muestra un pseudocódigo para un programa con uso intensivo de E / S de disco que pasa la mayor parte del tiempo
esperando una respuesta del disco duro; cada iteración de bucle realiza dos operaciones de E / S de disco.
Comienzo
Repita hasta llegar al final del archivo A {
FIGURA 2.15
Figura 2.16 muestra un pseudocódigo para un programa intensivo de E / S de red que pasa la mayor parte del
tiempo esperando que llegue un mensaje de la red.
Comienzo
Repetir para siempre
{
Espere un mensaje entrante
Identificar el remitente del mensaje desde el encabezado del mensaje
Enviar una copia del mensaje al remitente
}
Final
FIGURA 2.16
Figuras 2.14–2.16 proporcionan tres ejemplos de procesos que están impulsados por E / S y el proceso pasa la
mayor parte de su tiempo esperando (en estado bloqueado). Dichos procesos se bloquearán cada vez que realicen
una solicitud de E / S, lo que significa que no utilizarían completamente sus cuantos asignados. El sistema operativo
es responsable de seleccionar otro proceso de la cola lista para que se ejecute lo antes posible para
46 CAPITULO 2 LA VISTA DEL PROCESO
mantener la CPU ocupada y así mantener el sistema eficiente. Si no hubiera ningún otro proceso disponible para ejecutarse, el tiempo
de la CPU no se utilizaría y la eficiencia del sistema se reduciría.
Comienzo
Cargar un conjunto de datos masivo desde archivos (IO)
FIGURA 2.17
Pronóstico del tiempo programa de cálculo intensivo (realice un cálculo CFD extenso en un conjunto de datos masivo).
Procesos equilibrados
El término "equilibrado" podría usarse para describir un proceso que no solo usa la CPU de manera moderadamente intensiva, sino
que también realiza IO a un nivel moderado. Esta terminología se utiliza en el entorno de trabajo de sistemas operativos (consulte los
recursos adjuntos). Un ejemplo de esta categoría de programa podría ser un procesador de texto que pasa gran parte de su tiempo
esperando la entrada del usuario, pero que también incorpora actividades computacionalmente intensas como la revisión ortográfica
y gramatical. Dicha funcionalidad puede ser activada automáticamente o bajo demanda por el usuario y requiere ráfagas cortas de
cantidades significativas de recursos de procesamiento.
Figura 2.18 muestra un pseudocódigo para un programa "equilibrado" cuyo comportamiento a veces es
intensivo en IO y otras en computación. Tal proceso tendría períodos en los que pasa gran parte de su tiempo en el
estado bloqueado, pero también habrá períodos en los que se mueva predominantemente entre los estados de
ejecución y listo.
Comienzo
Cargar archivo (disk-IO)
Repita hasta completar la edición {
FIGURA 2.18
Procesador de textos Programa "equilibrado" (alterna entre comportamiento intensivo en IO y ráfagas de cálculo de alta
intensidad).
que el entorno de tiempo de ejecución del proceso (como lo ve el proceso en sí) es exactamente el mismo que estaba
cuando el proceso se ejecutó por última vez, inmediatamente antes de que fuera reemplazado o bloqueado. Este cambio del
contexto de ejecución de un proceso al contexto de ejecución de otro se denomina cambio de contexto. Este es el
componente más grande de la sobrecarga de programación y debe realizarse de manera eficiente porque la CPU no está
realizando un trabajo útil hasta que el proceso se está ejecutando nuevamente. A medida que el contador del programa se
restaura a su valor anterior en la secuencia de instrucciones del proceso, el programa continuará ejecutándose desde el
lugar exacto en el que se interrumpió anteriormente. Tenga en cuenta que esto es conceptualmente similar al mecanismo
de retorno de una llamada de función (subrutina). El despachador es parte del sistema operativo, por lo que se ejecuta en lo
que se denomina "modo privilegiado"; esto significa que tiene acceso completo a las estructuras mantenidas por el sistema
operativo y los recursos del sistema. Al final de su trabajo, el despachador debe volver a cambiar el sistema al "modo de
usuario", lo que significa que el proceso que se está despachando solo tendrá acceso al subconjunto de recursos que
necesita para su funcionamiento y al resto. de los recursos del sistema (incluidos los que pertenecen a otros procesos a nivel
de usuario) están protegidos y efectivamente ocultos del proceso.
La memoria física es finita y, a menudo, es un recurso de cuello de botella porque es necesaria para cada actividad que
realiza la computadora. Cada proceso usa memoria (la imagen del proceso real debe mantenerse en la memoria física
mientras el proceso se está ejecutando; esta imagen contiene las instrucciones reales del programa, los datos que está
usando mantenidos en variables, los búferes de memoria usados en la comunicación de red y estructuras especiales tales
como como el contador de pila y programa que se utilizan para controlar el progreso del proceso). El sistema operativo
también utiliza cantidades considerables de memoria para realizar sus actividades de gestión (esto incluye estructuras
especiales que realizan un seguimiento de cada proceso). Inevitablemente, hay ocasiones en las que la cantidad de memoria
necesaria para ejecutar todos los procesos presentes excede la cantidad de memoria física disponible.
Los dispositivos de almacenamiento secundarios, como las unidades de disco duro, tienen una capacidad mucho mayor
que la memoria física en la mayoría de los sistemas. Esto se debe en parte al costo de la memoria física y en parte a los
límites en el número de ubicaciones de memoria física que los microprocesadores pueden abordar. Debido a la
disponibilidad relativamente alta de almacenamiento secundario, los sistemas operativos están equipados con mecanismos
para mover (intercambiar) imágenes de proceso de la memoria física al almacenamiento secundario para dejar espacio para
procesos adicionales y mecanismos para mover (intercambiar) imágenes de proceso desde la memoria física. el
almacenamiento secundario a la memoria física. Esto aumenta el tamaño efectivo de la memoria más allá de la cantidad de
memoria física disponible. Esta técnica se denomina memoria virtual y se analiza con más detalle en el Capítulo 4.
Para incorporar el concepto de memoria virtual en el comportamiento de programación, son necesarios dos estados de
proceso adicionales. El estado suspendido-bloqueado se usa para sacar un proceso de estado bloqueado del conjunto activo
actual y así liberar la memoria física que estaba usando el proceso. El mecanismo de esto es almacenar la imagen de
memoria de todo el proceso en el disco duro en forma de un archivo especial y establecer el estado del proceso en
"suspendido-bloqueado", lo que significa que no está disponible de inmediato para ingresar al estados listos o en ejecución.
El beneficio de este enfoque es que se libera el recurso de memoria física, pero hay costos involucrados. El acto de mover la
imagen de la memoria del proceso al disco requiere tiempo (específicamente, se requiere que la CPU ejecute algún código
dentro del sistema operativo para lograr esto, y esta es una actividad de IO en sí misma (que ralentiza las cosas) que se
suma al aspecto de tiempo de los gastos generales de programación. El proceso debe tener su imagen de memoria
restaurada (de vuelta a la memoria física) antes de que pueda ejecutarse nuevamente, agregando así latencia al tiempo de
respuesta del proceso. Además de la latencia de programación regular, un proceso suspendido debe soportar la latencia del
intercambio al disco y, posteriormente, volver a la memoria física.
Un proceso intercambiado se puede intercambiar en cualquier momento que elija el sistema operativo. Si el proceso se
encuentra inicialmente en el estado suspendido-bloqueado, se moverá al estado bloqueado, ya que aún debe esperar a que
se complete la operación de E / S que causó que ingresara originalmente en el estado bloqueado.
Sin embargo, mientras se cambia, la operación de E / S puede completarse y, por lo tanto, el sistema operativo
moverá el proceso al estado suspendido listo; esta transición está etiquetada evento ocurrido. El estado listo
suspendido significa que el proceso aún se intercambia pero que se puede mover al estado listo cuando finalmente
se intercambia.
El estado suspendido listo también se puede utilizar para liberar memoria física cuando no hay procesos
bloqueados para intercambiar. En tal caso, el sistema operativo puede elegir un proceso de estado listo para
cambiar al estado listo suspendido.
Figura 2.19 ilustra el modelo de proceso de cinco estados, incluidos los estados suspendido-listo y suspendido
bloqueado y las transiciones de estado adicionales de suspender, activar y evento-ocurrido.
Clave: Expresar
TERMINAR
Transición
Correr
El proceso espera en
El proceso espera en
este estado hasta que
este estado hasta
Suspendido- Suspendido- se reactive a la
reactivado
(Movido hacia atrás
Listo Ocurrió un evento de IO, así que procese
Obstruido Estado bloqueado, o
FIGURA 2.19
El modelo de transición de estado de proceso extendido que incluye estados de proceso suspendidos.
• Para
programación
minimizar los gastos generales de
Realizar un cambio de contexto (el acto de cambiar qué proceso se está ejecutando en cualquier momento) requiere que el
componente del programador del sistema operativo se ejecute temporalmente para seleccionar qué proceso se ejecutará a
continuación y cambiar realmente el estado de cada uno de los procesos. Esta actividad requiere algo de tiempo de procesamiento y,
por lo tanto, no es deseable realizar cambios de contexto con demasiada frecuencia. Sin embargo, un sistema puede no responder si
los procesos se dejan en ejecución durante largos períodos sin prioridad; esto puede contradecir otros objetivos de la programación,
como la equidad.
50 CAPITULO 2 LA VISTA DEL PROCESO
El tiempo de espera es problemático en términos de capacidad de respuesta y, por lo tanto, un objetivo general de la programación
es mantenerlo al mínimo para todos los procesos. El tiempo de espera impacta más notablemente en los procesos y procesos
interactivos con restricciones de tiempo real, pero también es importante considerar el tiempo de espera como una proporción del
tiempo de ejecución total. Un proceso con un requisito de tiempo de ejecución muy corto (en términos de uso real de la CPU) verá
afectado su rendimiento relativamente mucho más que un proceso con un requisito de tiempo de ejecución prolongado, por
cualquier cantidad de retraso. Los algoritmos de programación del trabajo más corto primero y el siguiente trabajo restante más
corto favorecen específicamente los procesos de tiempo de ejecución más cortos. Esto tiene dos efectos importantes: en primer lugar,
los procesos más cortos son los que menos esperan y, por lo tanto, la proporciones medias de tiempo de espera tiempo de espera /
tiempo de ejecución) de los procesos se reducen; y en segundo lugar, el significa tiempos de espera absolutos de los procesos se
reducen porque un proceso largo espera solo un corto tiempo para que se complete el proceso corto, en lugar de que el proceso
corto espere mucho tiempo para que se complete el proceso largo.
• Para maximizar el rendimiento
El rendimiento es una medida del trabajo total realizado por el sistema. La capacidad total de procesamiento del
sistema está limitada por la capacidad de la CPU, de ahí la importancia que se le da a mantener la CPU ocupada. Si
la CPU se usa de manera eficiente, es decir, el programador la mantiene ocupada continuamente, entonces se
maximizará el rendimiento a largo plazo. El rendimiento a corto plazo puede verse influenciado por los procesos
que se eligen ejecutar, de modo que varios procesos cortos podrían completarse en el mismo tiempo que se ejecuta
uno más largo. Sin embargo, como medida de rendimiento, el rendimiento a largo plazo es más significativo.
FIGURA 2.20
Una posible secuencia de estado del proceso para la programación por turnos con tres procesos.
Requisito previo
Descargue Operating Systems Workbench y la documentación de respaldo del sitio web de materiales complementarios del
libro. Lea el documento "Programación de actividades (introductorias) y experimentos".
Actividad P5_CPU
Explorar el orden de llegada, el trabajo más corto primero y la programación por turnos con procesos intensivos en CPU
Método
Establezca el proceso 1 en "CPU intensivo" y establezca el tiempo de ejecución en 60 ms.
programación FCFS.
52 CAPITULO 2 LA VISTA DEL PROCESO
Gastos esperados
La simulación se ejecutará a través de la secuencia de cambios de estado para cada proceso, hasta que todos los procesos se hayan completado. Verá las
estadísticas en tiempo de ejecución y las estadísticas del sistema actualizándose en tiempo real a medida que se ejecuta la simulación. Estas estadísticas permiten
analizar el comportamiento de bajo nivel de los algoritmos y sus efectos en los procesos presentes.
Actividad P5_balanced
Explorar el orden de llegada, el trabajo más corto primero y la programación por turnos con procesos "equilibrados" (estos son
procesos que realizan cantidades moderadas de entrada y salida, pero también consumen moderadamente la CPU entre las
actividades de E / S)
Método
Lo mismo que para la Actividad P5_CPU anterior, excepto que configure ambos procesos para que estén "equilibrados".
Gastos esperados
Verá que los procesos se bloquean cuando realizan E / S y, por lo tanto, acumulan tiempo en el estado bloqueado. Debería notar una
diferencia significativa entre los comportamientos de los tres algoritmos cuando un proceso se bloquea.
Actividad P5_IO
Explorar el orden de llegada, el trabajo más corto primero y la programación por turnos con procesos intensivos de IO (estos son
procesos que realizan IO regularmente)
Método
Lo mismo que para la Actividad P5_CPU anterior, excepto que configura ambos procesos para que sean "intensivos en E / S".
Gastos esperados
Verá que los procesos realizan IO con frecuencia y se bloquean regularmente y, por lo tanto, acumulan una proporción significativa de su
tiempo en el sistema en el estado bloqueado. Debe notar que la eficiencia del sistema en general se ve afectada si todos los procesos
realizan E / S con frecuencia, ya que puede surgir una situación temporal cuando no hay procesos en el estado listo y, por lo tanto, ninguno
puede ejecutarse.
Reflexión
Ha examinado el comportamiento de tres algoritmos de programación con una variedad de procesos que se comportan de manera diferente. Según
las estadísticas de tiempo de ejecución que ha recopilado, ¿cómo se comparan estos algoritmos de programación entre sí? Aquí hay algunas
preguntas específicas para comenzar su investigación:
1. ¿Qué se puede decir sobre la equidad de los algoritmos (pista: considere los tiempos de espera de los procesos)?
2. ¿Qué se puede decir sobre la eficiencia del uso de la CPU (pista: considere el tiempo total en el sistema para cada proceso y el tiempo
total transcurrido del sistema)?
3. ¿En qué medida influye el tipo de comportamiento del proceso en la eficacia de los algoritmos de programación? ¿Siempre
hay un ganador claro o los algoritmos tienen diferentes fortalezas según los tipos de procesos del sistema?
Exploración adicional
Puede realizar más experimentos con la simulación “Programación de algoritmos: Introducción” para ayudar a obtener una
comprensión más profunda. Puede utilizar el botón de paso único para ejecutar las simulaciones paso a paso, lo que le permite trabajar
a su propio ritmo.
Los tres algoritmos de programación discutidos hasta este punto son bastante limitados en el contexto de los sistemas
modernos de propósito general con una variedad muy amplia de tipos de aplicaciones con comportamientos muy diferentes. Estos
algoritmos de programación carecen de la sofisticación necesaria para seleccionar procesos basados en factores contextuales
relacionados con la dinámica del propio sistema y los procesos internos. Algunos algoritmos más avanzados tienen en cuenta las
prioridades del proceso, los plazos, el tiempo de ejecución esperado o el tiempo de ejecución acumulado.
2.3 PROGRAMACIÓN DEL PROCESO 53
o varios otros factores para mejorar el rendimiento de los procesos individuales y del sistema en general. Sin
embargo, la programación por turnos es ideal para sistemas en los que varios procesos similares tienen la misma
importancia y, como resultado de su enfoque simple por turnos, tiene la ventaja de estar "libre de inanición", lo que
significa que un proceso no puede acaparar la CPU a expensas de otros, lo que puede ocurrir durante períodos
prolongados, posiblemente indefinidamente en algunos otros esquemas.
corto en la cola se ejecuta primero, reduciendo así el tiempo de espera promedio) con el comportamiento preventivo de RR (que mejora la
capacidad de respuesta al dar turnos a los procesos en utilizando la CPU y también mejora la eficiencia al mantener la CPU ocupada siempre que
hay un proceso que está listo para ejecutarse). El algoritmo SRJN da turnos a los procesos en ejecución, similar a la forma en que lo hace RR, pero
con la diferencia significativa de que tiene en cuenta la cantidad de tiempo de ejecución restante (la cantidad real de tiempo de procesamiento
de la CPU requerido para completar el proceso). El proceso en la cola lista con el tiempo de ejecución restante más corto se selecciona para
ejecutarse a continuación. De esta forma, si ningún proceso realiza E / S (y por lo tanto queda bloqueado), el algoritmo se comporta de manera
similar a SJF porque, aunque hay preferencia, el trabajo anteriormente más corto seguirá siendo el más corto si obtiene la CPU a continuación.
Sin embargo, una vez que un proceso se bloquea, otro proceso tiene la oportunidad de ejecutarse, manteniendo ocupada la CPU. También es
posible que el orden del proceso (en términos de tiempo de ejecución restante) se altere, ya que mientras un proceso está bloqueado, otro
puede superarlo en términos de convertirse en el nuevo proceso con el tiempo de ejecución restante más bajo. Se explora el comportamiento de
SRJN, y se compara con los de RR y SJF, en También es posible que el orden del proceso (en términos de tiempo de ejecución restante) se altere,
ya que mientras un proceso está bloqueado, otro puede superarlo en términos de convertirse en el nuevo proceso con el tiempo de ejecución
restante más bajo. Se explora el comportamiento de SRJN, y se compara con los de RR y SJF, en También es posible que el orden del proceso (en términos de tiempo de
Colas multinivel
En lugar de tener una sola cola lista, hay varias, una para cada categoría diferente de tareas. Entonces, por ejemplo, podría haber una cola para
los procesos del sistema (la prioridad más alta), otra para los procesos interactivos y otra para los procesos de computación intensiva, como las
simulaciones de computación científica (la prioridad más baja). Cada cola puede tener una política de programación diferente aplicada, por lo
que, por ejemplo, los procesos de cálculo intensivo podrían programarse utilizando FIFO, mientras que los procesos del sistema y los procesos
interactivos necesitarán un esquema preventivo como RR. También debe haber una división del recurso de la CPU en las diferentes colas, ya que
solo puede haber un proceso realmente programado en cualquier momento (asumiendo un solo núcleo). En un sistema que tiene muchos
procesos interactivos, podría ser apropiado asignar quizás el 80% o más del recurso de procesamiento a los procesos interactivos. Cuando
abundan los procesos informáticos intensivos, puede ser necesario asignar más recursos a esta categoría para garantizar que los usuarios
obtengan un tiempo de respuesta razonable mientras se equilibra esto con los tiempos de respuesta mucho más cortos que necesitan los
procesos interactivos. Tenga en cuenta que en el ejemplo citado anteriormente, el uso de la programación FIFO solo tendría un efecto localizado
en la cola de procesos de cálculo intensivo y no afectaría al entrelazado más detallado de la programación RR de procesos interactivos. El
algoritmo de colas multinivel se ilustra en Puede ser necesario asignar más recursos a esta categoría para garantizar que los usuarios obtengan
un tiempo de respuesta razonable mientras se equilibra esto con los tiempos de respuesta mucho más cortos que necesitan los procesos
interactivos. Tenga en cuenta que en el ejemplo citado anteriormente, el uso de la programación FIFO solo tendría un efecto localizado en la cola
de procesos de cálculo intensivo y no afectaría al entrelazado más detallado de la programación RR de procesos interactivos. El algoritmo de colas multinivel se ilustra
FIGURA 2.21
nivel. Si el proceso usa todo su cuanto, pero no se completa durante ese cuanto, se degrada al siguiente nivel de
cola. Sin embargo, si el proceso renuncia al control durante el primer cuanto, permanece en el mismo nivel. Los
procesos que realizan E / S y, por lo tanto, se bloquean, se promueven a la siguiente cola más alta. Estas reglas se
aplican de forma iterativa y en todos los niveles de prioridad, de modo que el nivel de prioridad de los procesos se
ajusta continuamente para reflejar su comportamiento. Por ejemplo, un proceso de cálculo intensivo irá bajando
gradualmente hasta la cola de menor prioridad. Sin embargo, si después de un largo período de cálculo el mismo
proceso comenzara una serie de operaciones de E / S para entregar sus resultados, o leer más datos, volvería a
subir algunos niveles de prioridad (porque las operaciones de E / S hacen que el proceso se bloquee antes de su
finalización). Quantum expira).
Esta estrategia da prioridad a las tareas cortas porque comenzarán con la prioridad más alta y pueden completarse en
ese nivel o algunos niveles más abajo. Las tareas intensivas en IO también se priorizan porque es probable que se bloqueen
antes de usar su cantidad de tiempo en general, lo que garantiza que es probable que permanezcan en los niveles más altos
de prioridad de programación. Las tareas con uso intensivo de cómputo no obtienen tan buenos resultados en este
esquema, ya que irán bajando por los niveles de la cola y permanecerán en el nivel más bajo, donde se programan en forma
RR hasta que se completen. En este nivel, las tareas solo reciben recursos de procesamiento cuando no hay tareas listas en
niveles de mayor prioridad.
La programación entre colas se realiza estrictamente sobre una base de prioridad de cola, de modo que
si hay algún proceso en la cola más alta, se programará de manera local FIFO (es decir, con respecto a su
posición en la cola particular). Cuando la cola lista se agota temporalmente porque los procesos se
completan, bloquean o degradan, se atiende al siguiente nivel de la cola. Si las tareas llegan a colas listas de
nivel superior, el programador vuelve a subir a la cola más poblada y programa las tareas desde allí.
Nivel de cola
3 (máxima prioridad)
2
Enviado Corre por uno
cuántico
1
Enviado Corre por uno
cuántico
Clave
FIGURA 2.22
1 2 3 4 5 6 7
2 P2 P4 X ( P4 completa)
P4
1 P2
0 P2 P2 P2 P2
(prioridad más baja)
FIGURA 2.23
Ejemplo de MLFQ con dos procesos interactivos y dos procesos de cálculo intensivo.
Requisito previo
Las instrucciones a continuación asumen que ha obtenido Operating Systems Workbench y la documentación de
respaldo como se explica en Actividad P5 .
Lea el documento "Programación de actividades y experimentos (avanzados)".
56 CAPITULO 2 LA VISTA DEL PROCESO
ACTIVIDAD P6 USO DEL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR EL COMPORTAMIENTO DE
PROGRAMACIÓN (AVANZADO): COMPARACIÓN DE LOS ALGORITMOS DE PROGRAMACIÓN DEL TRABAJO MÁS CORTO
PRIMERO, ROUND-ROBIN Y EL TRABAJO RESTANTE MÁS CORTO SIGUIENTE — Cont.
turnos
3. Para descubrir las características de la simulación de programación avanzada del entorno de trabajo de sistemas operativos, que se puede
utilizar para respaldar la exploración adicional.
4. Para reforzar aún más la comprensión de los conceptos generales de programación, que se analizan en este capítulo.
Esta actividad se divide en tres secciones para proporcionar una estructura.
Actividad P6_CPU
Comparar el trabajo restante más corto a continuación con el trabajo más corto primero y la programación por turnos con procesos intensivos en CPU.
Método
Esta actividad utiliza la simulación “Programación de algoritmos — Avanzada”, que se encuentra en la pestaña “Programación” del entorno de trabajo de
sistemas operativos. La simulación de programación avanzada facilita una investigación más profunda del comportamiento de programación. Admite
un algoritmo de programación adicional y hasta cinco procesos y permite la configuración de la latencia del dispositivo IO y el tamaño cuántico de
programación:
1. Usamos la misma configuración inicial de cinco procesos intensivos en CPU y comparamos el comportamiento usando cada uno de los tres
algoritmos de programación diferentes. Al final de cada simulación, anote los valores en la sección de estadísticas en la parte inferior de la
ventana de simulación.
2. Configuración:
Establezca el proceso 1 en "CPU intensivo" y establezca el tiempo de ejecución en 60 ms.
Establezca inicialmente la latencia del dispositivo IO en 10 ms (esto no tendrá ningún efecto cuando todos los procesos consuman mucha CPU) y establezca el
tamaño cuántico en 10 ms. Configure el algoritmo de programación inicialmente en el trabajo más corto primero.
3. Cuando esté listo, presione el botón "Free Run" para iniciar la simulación. Repita la simulación con la misma configuración, pero utilizando los
algoritmos de programación por turnos y luego el siguiente trabajo restante más corto. Anote los resultados en las ventanas de estadísticas de
tiempo de ejecución y estadísticas del sistema. Estos se utilizarán para comparar el rendimiento de los diferentes algoritmos de programación.
Las capturas de pantalla a continuación muestran la configuración inicial y final, respectivamente, cuando se ejecutan los procesos con el algoritmo de
ACTIVIDAD P6 USO DEL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR EL COMPORTAMIENTO DE
PROGRAMACIÓN (AVANZADO): COMPARACIÓN DE LOS ALGORITMOS DE PROGRAMACIÓN DEL TRABAJO MÁS CORTO
PRIMERO, ROUND-ROBIN Y EL TRABAJO RESTANTE MÁS CORTO SIGUIENTE — Cont.
Gastos esperados
La simulación se ejecutará a través de la secuencia de cambios de estado para cada proceso, hasta que todos los procesos se hayan completado. Verá
las estadísticas en tiempo de ejecución y las estadísticas del sistema actualizándose en tiempo real a medida que se ejecuta la simulación, al igual que
con la simulación de programación introductoria. Estas estadísticas permiten analizar el comportamiento de bajo nivel de los algoritmos y sus efectos
en los procesos presentes. Es muy importante prestar atención a estas estadísticas al evaluar el rendimiento relativo de los algoritmos. En el anterior Actividad
P5 , solo había dos procesos, por lo que era razonablemente fácil predecir el resultado de la programación. Con cinco procesos, el sistema de tiempo de
ejecución se vuelve bastante complejo y, por lo tanto, confiamos en estas estadísticas generadas automáticamente para comparar el comportamiento
de los algoritmos de programación.
Actividad P6_balanced
Comparación del trabajo restante más corto a continuación con el trabajo más corto primero y programación por turnos con procesos "equilibrados"
Método
Lo mismo que para la Actividad P6_CPU anterior, excepto que establezca todos los procesos para que estén "equilibrados". Esta vez, además de
cambiar el algoritmo de programación, también investigue los efectos de cambiar la latencia del dispositivo IO y el tamaño cuántico.
Gastos esperados
Verá que los procesos se bloquean cuando realizan E / S y, por lo tanto, acumulan tiempo en el estado bloqueado. Debería notar una
diferencia significativa entre los comportamientos de los tres algoritmos cuando un proceso se bloquea. En particular, preste atención al
tiempo de espera y al tiempo de E / S, y observe cómo se ven afectados por los distintos cambios de configuración.
Actividad P6_IO
Comparación del trabajo restante más corto a continuación con el trabajo más corto primero y programación por turnos con procesos "intensivos en E / S"
Método
Lo mismo que para la Actividad P6_ balanceada anterior, excepto que configura todos los procesos para que sean "intensivos en E / S". Una vez más,
asegúrese de investigar los efectos de varias configuraciones de latencia del dispositivo IO y el tamaño cuántico.
Gastos esperados
Verá que los procesos realizan IO con frecuencia y se bloquean regularmente y, por lo tanto, acumulan una proporción significativa de su tiempo en el
sistema en el estado bloqueado. Debe notar que la eficiencia del sistema en general se ve afectada si todos los procesos realizan E / S con frecuencia, ya
que puede surgir una situación temporal cuando no hay procesos en el estado listo y, por lo tanto, ninguno puede ejecutarse. Observe las estadísticas
generadas automáticamente e intente determinar qué programador tiene el mejor desempeño en todos los aspectos (el buen desempeño se indica por
tiempos de espera bajos y tiempo total en el sistema para procesos individuales y por tiempo de inactividad de CPU bajo para el sistema).
Reflexión
Ha examinado el comportamiento de tres algoritmos de programación con una variedad de procesos y configuraciones de sistema que se comportan
de manera diferente. Al tener cinco procesos, el sistema puede exhibir un comportamiento significativamente más complejo que las simulaciones
introductorias de dos procesos anteriores. Este escenario más rico es mejor en términos de encontrar las fortalezas y debilidades de los diversos
algoritmos de programación. Según las estadísticas de tiempo de ejecución que ha recopilado, ¿cómo se comparan estos algoritmos de programación
entre sí? Aquí hay algunas preguntas específicas para comenzar su investigación:
1. Qué se puede decir sobre la equidad de los algoritmos (pista: considere los tiempos de espera de los procesos). ¿Alguna vez los procesos se
“mueren de hambre”, es decir, cuando tienen que esperar mucho tiempo sin obtener ninguna parte de la CPU?
2. Qué se puede decir sobre la eficiencia del uso de la CPU (pista: considere el tiempo de inactividad de la CPU). ¿Se producen situaciones en las que la CPU está
inactiva innecesariamente?
3. ¿En qué medida influye el tipo de comportamiento del proceso en la eficacia de los algoritmos de programación? ¿Siempre
hay un ganador claro o los algoritmos tienen diferentes fortalezas según los tipos de procesos del sistema? ¿Hasta qué
4. punto el algoritmo SRJN es "lo mejor de ambos mundos" al tomar atributos de SJF y RR? ¿Hay algún aspecto negativo que
puedas detectar de los experimentos que has realizado?
Exploración adicional
Se recomienda encarecidamente que experimente más con la simulación de “Algoritmos de programación — Avanzado” para ayudar a obtener una
comprensión más profunda. Por ejemplo, intente utilizar una mezcla de diferentes tipos de procesos en la misma simulación. Puede utilizar el botón
de paso único para ejecutar las simulaciones paso a paso, lo que le permite trabajar a su propio ritmo.
58 CAPITULO 2 LA VISTA DEL PROCESO
en realidad hay varias restricciones de comportamiento que surgen de la velocidad de fotogramas especificada. Además del hecho de que las
tramas deben producirse a una velocidad constante con un espaciado igual, también deben entregarse al consumidor a través de la red,
manteniendo este espaciado uniforme. Este es un desafío importante para los sistemas distribuidos en tiempo real. La tecnología de la red en sí
puede causar un retardo variable en el flujo de datos de video (y por lo tanto introducir jitter, reduciendo la calidad final percibida por el usuario).
También existe el requisito de que cada fotograma de vídeo se procese antes de que llegue el siguiente. Entonces, si los fotogramas se producen
cada 1/24 de segundo, el tiempo máximo de procesamiento disponible para tratar cada fotograma (por ejemplo, acciones de compresión de
vídeo) es, por tanto, 1/24 de segundo; ya no se permite porque eso causaría que la trama se retrasara, y con cada trama subsiguiente, la demora
aumentaría aún más. Para poner esto en contexto, describiré brevemente un sistema con restricciones de tiempo muy estrictas. En la década de
1980, desarrollé un sistema de reconocimiento de voz que muestreaba y digitalizaba la entrada de sonido a una velocidad de 8 kHz. El
procesador era un Motorola MC68010 que funcionaba a 10MHz, que era una velocidad de funcionamiento típica en ese momento, pero del
orden de mil veces más lento que la tecnología actual. Cada muestra de sonido tuvo que ser procesada dentro de los 125 Describiré brevemente
un sistema con limitaciones de tiempo muy estrictas. En la década de 1980, desarrollé un sistema de reconocimiento de voz que muestreaba y
digitalizaba la entrada de sonido a una velocidad de 8 kHz. El procesador era un Motorola MC68010 que funcionaba a 10MHz, que era una
velocidad de funcionamiento típica en ese momento, pero del orden de mil veces más lento que la tecnología actual. Cada muestra de sonido
tuvo que ser procesada dentro de los 125 Describiré brevemente un sistema con limitaciones de tiempo muy estrictas. En la década de 1980,
desarrollé un sistema de reconocimiento de voz que muestreaba y digitalizaba la entrada de sonido a una velocidad de 8 kHz. El procesador era
un Motorola MC68010 que funcionaba a 10MHz, que era una velocidad de funcionamiento típica en ese momento, pero del orden de mil veces
más lento que la tecnología actual. Cada muestra de sonido tuvo que ser procesada dentro de los 125 μ s intervalo de tiempo entre muestras. El
procesamiento incluyó buscar el comienzo de la palabra, el final de la palabra y detectar varias características de la firma de energía en una palabra, así como medir la dura
Podemos dividir los sistemas en tiempo real en categorías “duras”, “firmes” y “blandas”. Un sistema duro en tiempo real
es aquel en el que una fecha límite incumplida tiene graves consecuencias (en el contexto de la aplicación y sus usuarios).
Los ejemplos más comunes de este tipo de sistemas se encuentran en sistemas de control como los sistemas fly-bywire y los
sistemas de control robótico. Incumplir la fecha límite en estos sistemas es inaceptable, y se pone mucho énfasis en las
etapas de diseño y prueba para garantizar que esto no suceda. Los sistemas en la categoría "firme" requieren que los plazos
se cumplan la mayor parte del tiempo, pero los incumplimientos ocasionales pueden ser
2.4 PROGRAMACIÓN DE SISTEMAS EN TIEMPO REAL 59
tolerado. Un ejemplo de esto podría ser un sistema automatizado de negociación de acciones, que intenta ejecutar acuerdos a alta
velocidad para alcanzar el precio anunciado actual de una acción cuando se ha alcanzado un umbral específico en el precio. Para
estos sistemas, la velocidad es esencial y el diseño incorporará ciertos supuestos del tiempo de respuesta de la transacción. Un
sistema flexible en tiempo real es aquel en el que los incumplimientos de los plazos tienen un impacto que afecta a la calidad del
servicio, pero el sistema aún puede funcionar y, por lo tanto, el impacto es menos grave. Por ejemplo, se puede considerar que la
capacidad de respuesta de una interfaz de usuario basada en eventos es en tiempo real si la aplicación implica alguna actividad en la
que el usuario necesita una respuesta con un límite de tiempo. Si el usuario tiene la expectativa de un tiempo de respuesta particular,
entonces la violación de esto podría tener un impacto que va desde la insatisfacción (p. Ej., si los resultados de las consultas de la base
de datos se retrasan) hasta una pérdida financiera considerable (por ejemplo, en una aplicación de comercio electrónico si se realiza
un pedido basado en información desactualizada o si el pedido se realiza pero la falta de respuesta provoca un retraso en el pedido).
actuado). Una gran proporción de sistemas distribuidos comerciales pueden clasificarse como sistemas blandos en tiempo real o al
menos tendrán algunos requisitos de procesamiento blando en tiempo real.
Los programadores de propósito general (es decir, programadores en tiempo no real) no priorizan las
tareas en función de su periodicidad o fechas límite; por lo general, ni siquiera son conscientes de la fecha
límite como un atributo de los procesos o tareas generadas por los procesos. Sin embargo, estos
programadores se encuentran comúnmente en uso en sistemas no especializados y, por lo tanto, muchas
tareas con características suaves en tiempo real (especialmente aplicaciones comerciales) se ejecutan en
sistemas controlados por programadores de propósito general. También existe un desafío con respecto a la
priorización de tareas dentro de las aplicaciones comerciales y de comercio electrónico en el sentido de que
todos los usuarios consideran que sus propias tareas (como sus consultas a la base de datos o sus descargas
de archivos) son las más importantes de todas y no tienen un visión de toda la empresa de lo que es en
realidad la tarea más importante que debe realizar el sistema en un momento determinado. Por eso,
Considere la situación que exploramos en Actividad P3 en el que los procesos PeriodicOutput realizan tareas en períodos
fijos. Imagine que las tareas deben ocurrir a una velocidad específica para garantizar una QoS particular, como, por ejemplo,
en el procesamiento de fotogramas de video en una aplicación de transmisión de video. Con un programador de propósito
general, puede ser posible lograr un tiempo específico cuando solo hay un proceso activo en el sistema, pero no hay
garantía de que las restricciones de tiempo (fechas límite) se mantengan cuando hay otros procesos que compiten por los
recursos. del sistema. Esto se explora en Actividad P7 , en el que se utilizan tres instancias del programa PeriodicOutput para
representar una aplicación en pseudo-tiempo real en la que se requiere un entrelazado regular de tareas. La actividad
muestra cómo un programador no en tiempo real puede lograr esto en ausencia de tareas en segundo plano intensivas en
computación, pero cuando una tarea intensiva en computación se inserta abruptamente, la secuencia de programación se
vuelve impredecible, ya que la tarea intensiva en computación se ve favorecido en términos de asignación de CPU (no se
bloquea como lo hacen las IO intensivas).
Para los sistemas duros en tiempo real, determinar realmente la fecha límite para las tareas suele ser sencillo porque se
relaciona directamente con las características de la tarea en sí: los eventos del mundo real que están involucrados, su
periodicidad y el procesamiento que debe llevarse a cabo como un resultado.
60 CAPITULO 2 LA VISTA DEL PROCESO
Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido los recursos complementarios necesarios como se explica en Actividad P1 .
Método
Esta actividad utiliza el programa PeriodicOutput que usamos en Actividad P3 como un programa pseudo en tiempo real, y también usamos el
programa CPU_Hog intensivo en CPU que usamos en Actividad P4 para crear una gran carga de trabajo en segundo plano:
1. Inicie el Administrador de tareas como en Actividad P4 y seleccione la pestaña "Procesos". Ordene la pantalla en orden de intensidad de uso de la CPU,
más alto en la parte superior. Esto proporcionará un medio para inspeccionar la carga de trabajo de la computadora, así que deje abierta la ventana del
Administrador de tareas, al costado de la pantalla. Inicialmente, la computadora debe estar efectivamente "inactiva"; este es un término que se usa para
describir una computadora con poca o ninguna carga de trabajo (por lo general, la utilización de la CPU no será más que un pequeño porcentaje).
2. Abra dos ventanas de comando. En cada uno, navegue hasta la subcarpeta "ProcessView" de la carpeta SystemsProgramming. En una
3. de las ventanas de comandos, ejecute el archivo por lotes PeriodicOutput_Starter.bat. Observe la salida; Las B y C de A producidas por
la primera, segunda y tercera copias del programa deben estar intercaladas de manera razonablemente uniforme como vimos en Actividad
P3 . Siendo esta observación importante, usaremos este entrelazado como un requisito de pseudo-tiempo real, es decir, que los
procesos produzcan resultados alternativamente. Realmente no importa lo que estén haciendo los procesos, porque la investigación
aquí se ocupa de cómo la carga de trabajo en segundo plano del sistema puede afectar el comportamiento de la programación en sí.
4. Una vez que se hayan completado los procesos que se ejecutan en el paso 3, reinícielos utilizando el mismo archivo por lotes. Esta vez,
aproximadamente a la mitad de su progreso, inicie cuatro copias del programa CPU_Hog.exe utilizando el archivo por lotes CPU_Hog_4Starter.bat
en la otra ventana de comandos. Esto crea una carga de trabajo en segundo plano significativa en la computadora, que el programador debe
administrar.
5. Observe el efecto de los procesos CPU_Hog en la ventana del Administrador de tareas. También debe examinar el efecto del fuerte
aumento de la carga de trabajo en el entrelazado de la salida de los procesos PeriodicOutput.
La siguiente captura de pantalla de mi computadora muestra el efecto que tuvieron los procesos CPU_Hog en el entrelazado de la
salida de los procesos PeriodicOutput.
2.4 PROGRAMACIÓN DE SISTEMAS EN TIEMPO REAL 61
Gastos esperados
Debería ver algo similar a mi captura de pantalla, aunque el comportamiento real es sensible a exactamente qué variante del programador
está presente. En la captura de pantalla, podemos ver claramente el comportamiento antes y después de que se iniciara la carga de trabajo en
segundo plano CPU_Hog. Inicialmente, las tres copias del proceso PeriodicOutput se estaban ejecutando y su salida estaba intercalada como
era de esperar. Justo después de la mitad de este experimento, los procesos CPU_hog se iniciaron en la segunda ventana de comandos. Puede
ver que el impacto en los procesos PeriodicOutput (que realizan E / S con regularidad y, por lo tanto, bloquean) fue que recibieron ranuras de
tiempo de CPU menos regulares. El efecto de esto se muestra como una interrupción del entrelazado, ya que los tres procesos tienen que
esperar en el estado bloqueado y luego volver a unirse a la cola lista. que siempre tiene al menos tres de los cuatro procesos CPU_Hog
también, porque estos procesos nunca se bloquean. Esta es una demostración simple pero muy importante de las formas en que el
rendimiento de un proceso puede verse afectado por el comportamiento de los otros procesos que constituyen la carga de trabajo de la
computadora host. También demuestra cómo la intensidad de la carga de trabajo puede cambiar repentinamente y en cantidades
significativas y, por lo tanto, ilustra lo difícil que puede ser predecir con precisión el rendimiento del proceso o el comportamiento de todo el
sistema.
Reflexión
Este experimento ilustra las limitaciones de los programadores de propósito general con respecto a la ordenación de procesos y el intercambio
de recursos. Como el planificador no tiene conocimiento de ninguna fecha límite o requisitos de comportamiento periódico de sus procesos,
simplemente comparte el recurso de la CPU según el modelo de estado del proceso. Como la mayoría de las tareas en tiempo real también son
considerablemente intensivas en IO (considere la transmisión de video, las aplicaciones de voz y música, y la lectura de sensores o la realización
de salidas de control en la maquinaria), es probable que se bloqueen con regularidad, teniendo que esperar eventos y hardware IO.
mecanismos, antes de unirse nuevamente a la cola lista. Por tanto, este tipo de tareas son las más afectadas por cambios abruptos en las cargas
de trabajo del sistema, como hemos visto en esta actividad.
Considere un subsistema en un fly-by-wire 5 sistema en el que un usuario proporciona entradas de control en una estación de
mando remota (por ejemplo, la cabina de un avión). Los comandos deben subastarse en un plazo breve de tiempo para que surtan
efecto en la aeronave tan pronto como sea posible después de que se haya emitido el comando. El usuario (en este caso el piloto)
recibe retroalimentación a través del correspondiente cambio de comportamiento del avión. En algunos escenarios, puede ser
necesario aplicar una entrada de control de forma progresiva, utilizando la retroalimentación como una indicación de cuándo
relajarse. Para tal situación, podría ser apropiado un tiempo de respuesta de un extremo a otro de unas pocas decenas de
milisegundos, es decir, el tiempo entre el usuario que prueba una entrada de control, el sistema que actúa sobre ella y el usuario que
recibe retroalimentación. Por lo tanto, para una entrada determinada (que se convierte en una tarea en el sistema), se puede
determinar una fecha límite. Un incumplimiento de la fecha límite en este escenario se traduce en una respuesta retrasada y el avión
se vuelve difícil o imposible de volar. Puede haber un punto marginal en el que
5 Fly-by-wire se utiliza para describir la actuación y el control remotos donde no hay conexión mecánica. Entonces, por ejemplo, en aviones, los
cables de control que se usaron para mover físicamente los alerones en las alas y la cola son reemplazados por sistemas informáticos donde las
entradas de control del piloto se detectan y digitalizan y luego se transmiten a motores o servos remotos, que realmente se mueven. la
superficie de control en consecuencia. Estos sistemas tienen requisitos muy estrictos que incluyen seguridad contra fallas y estabilidad, y quizás
la comprensión más obvia de las limitaciones para tales sistemas es en términos de su capacidad de respuesta: una demora demasiado larga
entre el piloto que realiza una entrada de control y la reacción del sistema haría que el sistema peligroso. Un breve retraso fijo puede ser
tolerable en algunas circunstancias en las que el usuario puede adaptarse, pero lo que no sería aceptable sería un gran retraso o fluctuaciones
en el retraso, lo que significa que el sistema no es predecible. Por lo tanto, estos son sistemas duros en tiempo real y la fecha límite para el
procesamiento de tareas debe determinarse con mucho cuidado.
El concepto de vuelo por cable no se limita a los sistemas de aviación; Se utilizan varios términos X-by-wire para describir el control en cualquier
sistema donde el enlace mecánico tradicional ha sido reemplazado por un sistema de comunicación digital. Por ejemplo, el término drive-by-wire
se usa para describir el reemplazo de conexiones mecánicas como la columna de dirección, y de manera similar, freno por cable se refiere al
reemplazo de líneas de freno en vehículos de carretera.
62 CAPITULO 2 LA VISTA DEL PROCESO
T Respuesta
T Re sp en se Hora
El tiempo requerido para el físico
sistema para responder al
comando y para que el piloto
recibir comentarios
El sistema se comporta correctamente (el comando se ejecuta antes de la fecha límite de la tarea).
El avión responde y el piloto recibe comentarios a su debido tiempo.
FIGURA 2.24
Ilustración simplificada de la importancia de cumplir con la fecha límite en un sistema de tiempo real estricto, como un vehículo
fly-bywire.
Piloto recibe
Mando Comando accionado realimentación
Un escenario que podría surgir porque el comando no cumple con la fecha límite de la tarea. El piloto
inyecta una señal de control adicional, provocando un sobreimpulso y provocando inestabilidad.
FIGURA 2.25
Ilustración simplificada de la importancia de cumplir con la fecha límite en un sistema de tiempo real estricto, como un vehículo
fly-bywire.
la demora hace que el avión responda con movimientos espasmódicos a medida que el piloto obtiene retroalimentación confusa de
sus acciones, lo que lleva a la alternancia entre el control excesivo y las entradas de compensación. Un ligero aumento de la demora
más allá de este punto y el avión es totalmente inestable y no se puede volar. Figuras 2.24 y 2,25 proporcionar una ilustración
simplificada de este escenario; T
Respuesta
es el tiempo de respuesta del sistema de control una vez que
la solicitud del piloto ha sido subastada; para un funcionamiento seguro, esto debe completarse antes de la fecha límite de respuesta.
Figuras 2.24 y 2,25 proporcionar un ejemplo de cómo la fecha límite de una tarea de computadora en un sistema en
tiempo real (en este caso, un sistema de control de aeronaves fly-by-wire) se asigna al límite entre el comportamiento
seguro ( Figura 2.24 ) y comportamiento inseguro ( Figura 2.25 ). En tales sistemas, es necesario que el sistema informático
logre una capacidad de respuesta lo más cercana posible a la de los sistemas con un enlace mecánico directo. Esto se
explica en términos de los sistemas de control de dos aviones muy diferentes. El Tiger Moth es un icono
2.4 PROGRAMACIÓN DE SISTEMAS EN TIEMPO REAL 63
biplano desarrollado en la década de 1930. Se descubrió que su manejo era adecuado para su uso como entrenador de
pilotos y, de hecho, se usó para entrenar a los pilotos de Spitfire durante la Segunda Guerra Mundial. El Moth tiene enlaces
de cable directos desde su joystick central hasta los alerones de las alas. Empujas la palanca hacia adelante y el avión se
zambulle, lo tira hacia atrás y sube, y lo empujas hacia la izquierda o hacia la derecha y realiza un viraje inclinado hacia la
izquierda o hacia la derecha, respectivamente. El piloto recibe retroalimentación instantánea de sus acciones a través del
movimiento del avión. Si, por ejemplo, el piloto siente que el avión está subiendo demasiado abruptamente, soltará la
palanca, moviéndola hacia la posición central, y la subida se nivelará. TheMoth responde muy bien y es fácil de volar, y el
sistema de control es muy simple: no hay latencia agregada por el sistema de control en sí. El piloto' Las acciones de
movimiento de la palanca se traducen directa e instantáneamente en el movimiento equivalente de los alerones. Ahora,
compare el Moth con los aviones de pasajeros modernos, que son fly-by-wire con algunos sistemas de control muy
sofisticados y en los que algunas de las superficies de control son extremadamente pesadas y grandes. El piloto exige un
sistema que responda tanto como el del Moth (habiendo tenido el privilegio de haber volado un Tiger Moth, puedo
confirmar que las entradas de control tienen un efecto inmediato y, como resultado, el avión responde muy bien). El avión
moderno tiene una amplia gama de sistemas de seguridad, sensores y actuadores; Para administrar estos sistemas, una
gran cantidad de procesos computacionales pueden estar activos en sus computadoras a bordo en cualquier momento. Este
es un muy buen ejemplo de un sistema en tiempo real riguroso en el que varias tareas tienen diferentes fechas límite y
prioridades. Es necesario tratar las señales de los sensores de rutina de forma periódica, así como los eventos impredecibles
(asíncronos), como las señales de alarma que indican fallas detectadas, al tiempo que se garantiza que las señales de control
para mover las superficies de control no pierdan sus plazos. Las diversas actividades de cálculo y control deben llevarse a
cabo dentro de plazos estrictos y las prioridades de las diferentes tareas pueden cambiar dependiendo del contexto de
vuelo, como si el avión está despegando, en vuelo suave, en condiciones turbulentas o aterrizando.
Como Figura 2.25 muestra, un retraso en una acción de control podría dar lugar a entradas adicionales del piloto, lo que conduce
a una compensación excesiva y, en última instancia, a la desestabilización de la aeronave.
Un proceso que se ocupa de las entradas de un joystick podría ser un ejemplo de un proceso periódico, generando
tareas periódicas. En lugar de detectar los movimientos de la palanca de mando y reaccionar a cada uno, un enfoque más
probable es leer la posición de la palanca de mando a una velocidad suficientemente alta y comunicar el valor a las
superficies de control independientemente de si la palanca se ha movido o no. De esta manera, las superficies de control
siempre siguen la posición de la palanca con un cierto retraso, que es el tiempo total para leer el sensor, procesar los datos
para calcular la posición de la superficie de control, transmitir el comando al actuador (como en el wing) y, de hecho, mover
la superficie de control. La latencia total debe ser lo suficientemente pequeña para que el piloto obtenga una respuesta
natural del avión; idealmente, debería existir la ilusión de una conexión directa del cable al palo, tirando de los alerones.
Además de la baja latencia, la latencia real debe ser constante para que el piloto obtenga una respuesta predecible y
consistente desde el avión; el planificador debe garantizar esto incluso cuando se trata de otras acciones urgentes.
A modo de ilustración, considere que la frecuencia de muestreo podría ser quizás de 100 Hz (es decir, muestree la
posición de la palanca de control 100 veces por segundo). La latencia total permitida una vez que se ha realizado el
muestreo podría ser quizás de 50 ms. Esto significa que el tiempo de demora total, entre el movimiento de la palanca y la
superficie de control de la aeronave que lo sigue, sería de 50 ms más hasta 10 ms debido al intervalo de muestreo (es decir,
si la palanca se moviera inmediatamente después de la toma de muestras, entonces otros 10 ms serían transcurrir antes de
que se detecte el nuevo movimiento).
Muchos sistemas de control y / o supervisión en tiempo real incluyen tareas periódicas en las que se realiza una
actividad de detección y una activación de control subsiguiente con un período de tiempo regular (como en el ejemplo de
palanca de mando de avión discutido anteriormente). A menudo, habrá múltiples flujos de tareas periódicas, generadas por
diferentes procesos. Por ejemplo, reproducir una película puede implicar dos flujos de tareas diferentes, uno para el
64 CAPITULO 2 LA VISTA DEL PROCESO
U=C/P U= Σn C I
yo = 1 PAG I
FIGURA 2.26
fotogramas de vídeo y uno para el sonido. En este caso, las relaciones de tiempo no solo dentro de las corrientes sino
también entre las corrientes deben mantenerse (para que el sonido y la visión permanezcan sincronizados). La
programación de dichos sistemas tiene algunos requisitos específicos, incluida la necesidad de mantener el intervalo entre
la ejecución de tareas en un flujo determinado y la necesidad de programar todas las tareas de todos los flujos dentro de
sus limitaciones de tiempo. La capacidad de procesamiento de la CPU es un factor limitante en la velocidad a la que se
pueden realizar las tareas. La utilización de la CPU (para un flujo determinado de tareas) depende de una combinación del
período entre las tareas y el tiempo de cálculo necesario para cada instancia de tarea.
Las fórmulas para calcular la utilización de la CPU para tareas periódicas se muestran en Figura 2.26 . La mano izquierda
La fórmula es para una única secuencia de tareas periódicas (secuencia continua de tareas), mientras que la fórmula de la derecha es
para un conjunto de n secuencias de tareas periódicas. C es el tiempo de cálculo por instancia de tarea (dentro de una secuencia), P es
el período entre tareas y U es la utilización de CPU resultante como una fracción entre 0 y 1. Para la fórmula de la derecha, norte es el
número de secuencias de tareas periódicas en el conjunto concurrente, y I significa una secuencia de tareas periódica específica en el
conjunto {1… norte}.
Cuanto menor sea la utilización U, es más probable que todas las tareas se puedan programar correctamente. Si U excede
1.0, entonces el sistema está sobrecargado y algunas tareas definitivamente se retrasarán. Incluso con valores de U
que están por debajo de 1.0 donde hay múltiples flujos de tareas, es posible que algunas instancias de tareas
específicas se retrasen debido a los requisitos de entrelazado de los múltiples flujos. Cuadro 2.1 ilustra la forma en
que funcionan las fórmulas de utilización con algunas configuraciones de ejemplo para una única secuencia1 de 11
tareas
12
periódica
norte
1
P {PT, PT… PT} y para dos secuencias de
1
tareas
11
periódicas
12
Pnorte
{PT, PT… PT}
2
y 2P1 {PT, 2PT…
1 2
PT}. 2 norte
10 (soltero 10 100 PAG 1 T 1 PAG 1 T 2 PAG 1 T 3 PAG 1 T 4 PAG 1 T 5 PAG 1 T 6 PAG 1 T 7 PAG 1 T 8 PAG 1 T 9 PAG 1 T 10
tarea periódica)
20 (soltero 10 50 PAG 1 T 1 PAG 1 T 2 PAG 1 T 3 PAG 1 T 4 PAG 1 T 5
tarea periódica)
50 (soltero 10 20 PAG 1 T 1 PAG 1 T 2
tarea periódica)
20 y 50 (dos 10 y 20 50 + 40 = 90 PAG 1 T 1 PAG 1 T 2 PAG 1 T 3 PAG 1 T 4 PAG 1 T 5
tareas periódicas) respectivamente
+
PAG 2 T 1 PAG 2 T 2
Requisito previo
Las instrucciones a continuación asumen que ha obtenido Operating Systems Workbench y la documentación de
respaldo como se explica en Actividad P5 .
Lea el documento "Actividades (introductorias) de programación en tiempo real".
1. Configuración. Para el proceso 1, establezca el período en 33 ms y el tiempo de cálculo en 16 ms. Habilite el proceso 2 y establezca el
período en 20 ms y el tiempo de cálculo en 10 ms. Seleccione inicialmente el algoritmo de programación de fechas límite.
2. Observe el cálculo de utilización de CPU que se muestra. Mire la fórmula que se ha utilizado (esta se actualiza dinámicamente en
función de cuántos procesos estén activos). ¿Puedes ver cómo funciona esta fórmula? Vea la discusión anteriormente en el texto.
3. Cuando esté listo, presione el botón "Free Run" para iniciar la simulación; déjelo funcionar durante algún tiempo, el tiempo suficiente para apreciar
el comportamiento de programación. Repita la simulación con la misma configuración, pero utilizando en su lugar el algoritmo de programación de
tarifa monótona. Anote los resultados en las ventanas de estadísticas de tiempo de ejecución y estadísticas del sistema. Estos se utilizarán para
comparar el rendimiento de los diferentes algoritmos de programación.
4. Repita el experimento (para ambos programadores), pero esta vez, use las siguientes configuraciones de proceso:
• para el proceso 1, período = 30 ms, tiempo de cálculo = 7 ms,
• para el proceso 2, período = 15 ms, tiempo de cálculo = 10 ms.
¿Qué diferencias observa en términos de la utilización de la CPU, el tiempo de las tareas de los procesos y el tiempo de inactividad de la CPU (observe la
tercera línea de visualización horizontal con las barras verdes que representan el tiempo de inactividad de la CPU)?
5. Repita el experimento, para ambos programadores nuevamente, ahora usando las siguientes configuraciones de proceso:
La siguiente captura de pantalla proporciona una instantánea de la simulación de la fecha límite poco después de que se inició. Los dos procesos
han completado una tarea cada uno y estas tareas han cumplido con sus plazos (las tareas terminaron antes de las flechas verticales que indican sus
plazos). La segunda tarea del proceso 2 ha comenzado y ha tenido 4ms de tiempo de CPU (ver estadísticas de tiempo de ejecución), de los 10ms en
total que necesita esta tarea (ver la configuración del proceso).
Gastos esperados
Las simulaciones se ejecutan continuamente hasta que se detienen presionando los botones de pausa o listo. Esto se debe a que los tipos de procesos que se simulan
tienen un comportamiento periódico en el que las tareas, con sus propios plazos de corto plazo, son creadas periódicamente por cada proceso. Como las tareas se
pueden ejecutar una tras otra, una barra negra sólida marca el último paso de tiempo de cada tarea para mayor claridad visual. Tú
2.4 PROGRAMACIÓN DE SISTEMAS EN TIEMPO REAL 67
Verá las estadísticas de tiempo de ejecución y las estadísticas del sistema actualizadas en tiempo real a medida que se ejecuta la simulación, al igual que con las
simulaciones de programación de propósito general. Estas estadísticas facilitan el análisis del comportamiento de bajo nivel de los algoritmos y sus efectos en los
procesos presentes. Es muy importante prestar atención a estas estadísticas al evaluar el rendimiento relativo de los algoritmos. Con la programación en tiempo real,
los objetivos principales son evitar incumplimientos de fechas límite y mantener la sincronización periódica de las tareas. Por lo tanto, la cantidad de tareas que no
cumplen con sus plazos es generalmente la estadística más importante a tener en cuenta.
Reflexión
Ha examinado el comportamiento de dos algoritmos de programación en tiempo real con una variedad de configuraciones de proceso. Según las
estadísticas de tiempo de ejecución que ha recopilado, debería poder comparar su comportamiento. Aquí hay algunas preguntas para guiarlo:
1. ¿Cuáles son las principales diferencias en términos de la forma en que los dos algoritmos realizan la programación?
se utiliza como base para la elección del planificador de qué proceso ejecutar en cada paso de tiempo. Este conocimiento de los plazos es una
diferencia importante entre los programadores en tiempo real y los programadores de propósito general.
Asegúrese de apreciar cómo funciona el cálculo de la utilización de la CPU. Puede realizar experimentos adicionales con diferentes
configuraciones de proceso para ayudar a aclarar esto, según sea necesario.
Exploración adicional
Lleve a cabo experimentos adicionales con la simulación “Programación en tiempo real: Introducción” utilizando una variedad de configuraciones de
procesos diferentes para obtener una comprensión más profunda. En particular, busque evidencia de los diferentes enfoques de priorización de
tareas de los dos algoritmos de programación en los patrones de los programas de tareas producidos.
Es posible que desee utilizar el botón de paso único para ejecutar las simulaciones paso a paso, lo que le permitirá trabajar a su
propio ritmo.
Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido Operating Systems Workbench y la documentación de
respaldo como se explica en Actividad P5 . Deberías haber realizado Actividad P8 antes de este.
Método
Esta actividad utiliza la simulación "Programación en tiempo real: avanzada", que se encuentra en la pestaña "Programación" del entorno
de trabajo de sistemas operativos. La simulación admite hasta tres procesos en tiempo real con tareas periódicas. Para cada proceso, se
puede configurar el período entre tareas, el requisito de tiempo de CPU por tarea, el número de tareas y el desplazamiento de inicio de la
primera tarea, lo que permite la simulación de una amplia gama de circunstancias. Al igual que con la simulación "Introductoria", se
proporcionan dos algoritmos de programación en tiempo real: fecha límite y tasa monótona. La simulación calcula la utilización de la CPU
para el cronograma, basándose en la configuración de los procesos. Se proporciona una pantalla en movimiento en tiempo real, que
muestra la hora actual (la línea negra vertical), las fechas límite de la tarea que se acercan (las flechas verticales a la derecha),
Para el proceso 1, establezca el período, el tiempo de cálculo, el desplazamiento de inicio de la primera instancia y el número de tareas en {35,
16, 0, - 1}, respectivamente (el - 1 significa un número infinito de tareas, es decir, el proceso se ejecuta sin fin). Habilite el proceso 2 y configure de
manera similar con {20, 10, 0, - 1}. Habilite el proceso 3 y configure con {25, 2, 0, - 1}. Seleccione inicialmente el algoritmo de programación de fechas
límite.
2. Ejecute la simulación durante algún tiempo, el tiempo suficiente para apreciar el comportamiento de programación. Repita la simulación con la misma
configuración, pero utilizando en su lugar el algoritmo de programación de tarifa monótona. Anote los resultados en las ventanas de estadísticas de tiempo
de ejecución y estadísticas del sistema. Estos se utilizarán para comparar el rendimiento de los diferentes algoritmos de programación.
3. ¿Cuál es el problema fundamental con esta combinación de procesos? Examine el comportamiento hasta que pueda ver claramente por qué este
4. Aumente el período para la tarea 2 hasta que la programación se ejecute sin que se pierda la fecha límite. Es posible que deba probar algunos valores
Parte 2. Programación con una carga de trabajo en segundo plano "en ráfagas"
1. Los procesos 1 y 2 constituirán una carga de trabajo regular en tiempo real. El proceso 3 representará una carga de trabajo en ráfagas, que se inicia de forma
abrupta, se ejecuta durante un breve período de tiempo (provocando una perturbación en la programación en tiempo real) y luego se detiene. Configure los
Para el proceso 1, establezca el período, el tiempo de cálculo, el desplazamiento de inicio de la primera instancia y el número de tareas en {35,
16, 0, - 1}, respectivamente. Habilite el proceso 2 y configure de manera similar con {20, 10, 0, - 1}. Habilite el proceso 3 y configure con {25,
8, 100, 1}, lo que significa que generará una sola tarea, con un requisito de procesamiento de 8ms y con un plazo de 25ms, y lo hará
después de un retraso de 100ms. Seleccione inicialmente el algoritmo de programación de fechas límite.
2. Ejecute la simulación. Tenga en cuenta que inicialmente, el programador se ocupa de la carga de trabajo de manera adecuada. Luego, la tarea adicional comienza y
perturba la programación, lo que provoca que se pierdan algunos plazos, y luego el sistema se vuelve a establecer una vez que se completa el tercer proceso.
3. Repita el experimento con la misma configuración: excepto para el proceso 3, que ahora tiene {50, 8, 100, 1}.
Observe el comportamiento: el tercer proceso tiene el mismo requisito de procesamiento que tenía en el experimento anterior, pero esta vez, el
procesamiento se puede distribuir en un período de tiempo más largo, lo que significa una mejor oportunidad para que el programador use cualquier
CPU inactiva. tiempo que puede ocurrir. ¿El resultado general es diferente esta vez?
2.4 PROGRAMACIÓN DE SISTEMAS EN TIEMPO REAL 69
La siguiente captura de pantalla proporciona una instantánea de la simulación de la fecha límite en la parte 1, con tres procesos. La utilización
de la CPU se ha calculado en 1.037, lo que implica que no es posible cumplir con todos los plazos. Podemos ver que una de las tareas del proceso 1
ha incumplido su fecha límite muy levemente (las barras rojas significan una tarea atrasada), seguida de un sobrepaso más significativo de una tarea
del proceso 2.
Gastos esperados
Esta actividad ha ilustrado la complejidad adicional de programar tres procesos en tiempo real con tareas periódicas y también la situación
que ocurre cuando una programación de dos procesos finamente equilibrada se ve perturbada por la introducción de un proceso
adicional.
Reflexión
Al igual que con la programación de propósito general, la utilización de la CPU es una preocupación principal de la programación en
tiempo real. En el primero, un algoritmo ineficiente o un aumento de la carga de trabajo tendrá el efecto de una degradación del
rendimiento en general, ya que las tareas tardarán más en ejecutarse; sin embargo, dado que no existe una restricción de tiempo real, no
se traduce directamente en un desastre. En los sistemas en tiempo real, sin embargo, un algoritmo ineficiente o un sistema sobrecargado
conducirán a incumplimientos de plazos. La gravedad de esto dependerá del tipo de sistema. En una línea de producción automatizada,
podría interrumpir la sincronía necesaria con la que operan los distintos robots. En el comercio de acciones automatizado o el comercio
electrónico, podría significar perder el mejor precio en una oferta. En un automóvil o un avión fly-by-wire,
Investigamos el impacto que la carga de trabajo en segundo plano y las fluctuaciones en la carga de trabajo pueden tener en las tareas urgentes. Incluso con los
programadores en tiempo real, una carga de trabajo en segundo plano con ráfagas en la que la carga del sistema aumenta repentinamente puede afectar la
programación y provocar pérdidas de fechas límite. Este es un desafío interesante. Es muy poco probable que ocurra la tarea adicional, pero se trata de un evento de
muy alta prioridad, como cerrar una válvula en particular en una instalación de producción química automatizada en respuesta a una lectura de sensor fuera de
rango. Por lo tanto, el diseño de sistemas en tiempo real debe considerar todos los escenarios de tiempo posibles, incluidos los eventos más improbables, para
garantizar que los sistemas siempre puedan cumplir con los plazos de todas las tareas.
70 CAPITULO 2 LA VISTA DEL PROCESO
Exploración adicional
Lleve a cabo experimentos adicionales con la simulación "Programación en tiempo real: avanzada" utilizando una variedad de configuraciones de
procesos diferentes para obtener una comprensión más profunda.
ets en redes se explora en profundidad en el Capítulo 3. Sin embargo, hay algunas características importantes de los sockets
que se mencionan brevemente en este punto para que se reconozca su importancia a medida que avanza en esta sección.
En primer lugar, los sockets se han convertido en un concepto estándar de IPC que se implementa en casi todas las
plataformas de procesamiento (tanto en términos de hardware como en términos de los sistemas operativos que se
encuentran en las plataformas de hardware). Por lo tanto, el socket es una base ideal para IPC en entornos heterogéneos,
ya que proporciona un medio de interconectividad entre los diferentes sistemas y también mejora la portabilidad de las
aplicaciones. En segundo lugar, los sockets son compatibles con casi todos los lenguajes de programación; esto permite que
los componentes de una aplicación distribuida se desarrollen en diferentes lenguajes según sea apropiado para la
funcionalidad del componente. Por ejemplo, en una aplicación cliente-servidor, puede elegir C ++ para el procesamiento del
servidor back-end, pero preferir C # para los aspectos de la interfaz gráfica de usuario del lado del cliente. Finalmente, los
sockets operan en la capa de transporte del modelo de red de siete capas y, por lo tanto, el IPC basado en sockets se puede
implementar utilizando cualquiera de los protocolos de comunicación TCP o UDP.
Los enchufes permiten la comunicación a través de un canal dedicado en lugar de reutilizar los flujos de entrada
/ salida. Estamos creando efectivamente nuevos flujos para realizar la comunicación, facilitados por la biblioteca de
sockets.
El socket es un recurso virtual (es decir, es una estructura dentro de la memoria), que sirve como punto final
para la comunicación entre un par de procesos. Cada socket es un recurso que está asociado con un proceso
específico.
Cada proceso puede utilizar tantos sockets como sea necesario para permitir la comunicación con otros procesos. Los
enchufes son muy flexibles en cuanto a los modos de comunicación que admiten. Los sockets se pueden utilizar en
comunicaciones de difusión o unidifusión, y los procesos involucrados pueden estar en la misma computadora o en
diferentes computadoras.
Para configurar la comunicación, cada proceso primero debe crear un socket. La llamada al sistema socket () se
usa para hacer esto. El sistema operativo realmente crea el socket y devuelve el ID del socket al proceso, de modo
que pueda referirse al socket apropiado al enviar y recibir mensajes. Los sockets se pueden asociar entre sí para
proporcionar una conexión virtual o se pueden utilizar para operaciones discretas de envío y recepción, según los
parámetros proporcionados cuando se crea el socket. El mecanismo de usar sockets para lograr IPC entre procesos
se ilustra en Figura 2.27 .
El uso de las primitivas de la biblioteca de sockets se ilustra por medio de segmentos de código anotados
de los programas IPC_socket_Sender e IPC_socket_Receiver que se utilizan en el Actividad P10 , que sigue. En
este escenario particular, los dos procesos están en la misma computadora y se implementa la comunicación
unidifusión en una sola dirección.
Figura 2.28 presenta segmentos de código clave del programa IPC_socket_Sender. Las variables se crean
para contener una referencia al socket y la dirección del socket (la combinación de la dirección IP y
Proceso Proceso
Enchufe Enchufe
P1 P2
Se puede crear un canal virtual
dedicado entre los enchufes
FIGURA 2.27
Los sockets son puntos finales para la comunicación entre un par de procesos.
72 CAPITULO 2 LA VISTA DEL PROCESO
SOCKET m_SendSOCKET; // Crea una variable SOCKET para contener una referencia al socket creado
...
SOCKADDR_IN m_SendSockAddr; // Cree una variable SOCKADDR_IN para contener la dirección del destinatario
// es decir, la dirección a la que enviamos
...
si (INVALID_SOCKET == m_SendSOCKET)
{
cout << "socket () falló" << endl; falso retorno; // Informar error si no se pudo crear el socket
}
devuelve verdadero;
}
...
...
}
devuelve verdadero;
}
FIGURA 2.28
número de puerto) del proceso receptor; se utiliza una estructura especial SOCKADDR_IN para este propósito. El socket se
crea dentro del método CreateSocket, usando la primitiva socket. Luego, la estructura de la dirección de envío se completa
con la dirección IP y el número de puerto que identificará el proceso del destinatario. El mensaje del usuario (precedido en
una cadena en el código que no se muestra aquí) se envía luego, usando la primitiva sendto, que usa la estructura de
dirección y socket creada previamente.
Figura 2.29 presenta segmentos de código clave del programa IPC_socket_Receiver. Al igual que con el
programa remitente, las variables se crean para contener una referencia al conector y la dirección del
conector (se usa nuevamente una estructura SOCKADDR_IN, pero esta vez, se usa la dirección local, es decir,
es la dirección en la que recibir; se interpreta como recibir mensajes, que se envían a esta dirección).
El socket se crea dentro del método CreateSocket, usando la primitiva socket. La estructura de la dirección
local se completa con los detalles de la dirección de los destinatarios. La primitiva de vinculación se utiliza
para establecer una asociación entre el proceso y el número de puerto elegido. Esto informa al sistema
operativo que cualquier mensaje dirigido al número de puerto especificado debe entregarse a este proceso
específico. La primitiva recvfrom se utiliza para recuperar un mensaje (si ya ha llegado uno al búfer de
recepción) o, de lo contrario, el planificador bloqueará el proceso porque debe esperar a que llegue un
mensaje (que es una operación de E / S).
2.6 COMUNICACIÓN INTERPROCESO 73
SOCKET m_IPC_ReceiveSOCKET; // Crea una variable SOCKET para contener una referencia al socket creado
...
SOCKADDR_IN m_LocalSockAddr; // Cree una variable SOCKADDR_IN para contener la dirección local (para recibir)
...
...
...
...
FIGURA 2.29
Actividad P10 proporciona una ilustración de la comunicación basada en sockets entre procesos, utilizando los dos
programas discutidos anteriormente.
Los conceptos de sockets presentados en esta sección y Actividad P10 son resultados de aprendizaje muy
importantes al nivel del libro mismo y se han introducido aquí para vincularlos con otros aspectos de la visión de
proceso de los sistemas. Sin embargo, los enchufes se revisan y se tratan con mayor profundidad en el Capítulo 3.
74 CAPITULO 2 LA VISTA DEL PROCESO
Método
Esta actividad utiliza los programas IPC_socket_Sender e IPC_socket_Receiver para proporcionar una introducción al IPC basado en
sockets:
1. Examine el código fuente del programa IPC_socket_Sender.cpp. En particular, observe la forma en que se utilizan las primitivas de
socket; en este programa, la primitiva socket () se usa para crear un socket, y luego, la primitiva sendto () se usa para enviar un
mensaje desde el socket creado a otro socket en otro proceso. También examine la estructura de la dirección del socket, que está
configurada con la dirección IP de la computadora donde se encuentra el proceso de destino, y el número de puerto, que identifica el
proceso específico (de los posiblemente muchos) en esa computadora. Observe que en este caso, el proceso está ubicado en la
computadora local, por lo que se usa la dirección de loopback 127.0.0.1 y que el puerto específico que identifica el proceso de destino
es 8007.
2. Examine el código fuente del programa IPC_socket_Receiver.cpp. Observe la forma en que se utilizan las primitivas de socket, notando
algunas similitudes con el programa remitente, especialmente el hecho de que se crea un socket y se configura una estructura de
direcciones. Sin embargo, también hay tres diferencias importantes. En primer lugar, observe que se usa el mismo número de puerto,
pero esta vez, se usa la primitiva bind (); esto hace una asociación entre el proceso del receptor y el número de puerto especificado,
para que el sistema operativo sepa dónde entregar el mensaje. En segundo lugar, la dirección IP utilizada es diferente, en este caso
INADDR_ANY; esto significa que este proceso recibirá un mensaje no solo si se envía a cualquier dirección de la computadora, por
ejemplo, la dirección de bucle de retorno especial, que el utiliza el programa remitente, será suficiente, pero también si la
computadora ' Si se utilizó una dirección IP única (consulte el Capítulo 3 para obtener más detalles), el proceso aún recibiría el
mensaje. En tercer lugar, observe que recvfrom () se usa para recibir el mensaje que se ha enviado con la primitiva sendto () en el otro
programa.
3. Abra dos ventanas de comando. En cada uno, navegue hasta la subcarpeta "ProcessView" de la carpeta
SystemsProgramming.
4. En una de las ventanas de comandos, ejecute el programa IPC_socket_Receiver.exe. Este programa espera que se le envíe
un mensaje. Cuando llegue un mensaje, lo mostrará. El programa debería mostrar " Esperando mensaje… ”.
5. En la otra ventana de comandos, ejecute el programa IPC_socket_Sender.exe. Este programa espera a que el usuario escriba un
mensaje, que luego envía al otro programa, a través del IPC basado en socket. El programa debería mostrar inicialmente " Escribe
un mensaje para enviar… ”.
6. Coloque las dos ventanas de modo que pueda ver ambas al mismo tiempo. Escriba un mensaje en el programa IPC_socket_Sender y presione la
tecla Enter. El mensaje será enviado y debería ser mostrado por el programa IPC_socket_Receiver. Las capturas de pantalla a continuación
muestran lo que debería ver.
2,7 HILOS: INTRODUCCIÓN 75
Gastos esperados
El proceso IPC_socket_Receiver espera a que llegue el mensaje y luego lo muestra, como en el ejemplo que se muestra arriba. Experimente
con esto unas cuantas veces: en realidad está enviando un mensaje de un proceso a otro, utilizando la biblioteca de sockets como
mecanismo para enviar y recibir el mensaje y utilizando el sistema operativo como intermediario para pasar el mensaje.
Reflexión
Es importante darse cuenta de que esta comunicación entre procesos representa su primer paso hacia el desarrollo de aplicaciones y
sistemas distribuidos. Los enchufes son una forma flexible de IPC. Con sockets, la comunicación puede ser bidireccional, los procesos
pueden ser en diferentes computadoras y también podemos enviar un mensaje de difusión a muchos receptores.
Estudie el código fuente de los programas emisor y receptor y asegúrese de que puede conciliar la lógica de los dos programas con
el comportamiento en tiempo de ejecución exhibido, especialmente en términos de la interacción de comunicación que ocurre entre los
dos procesos independientes.
Considere las oportunidades que le brinda la IPC entre procesos en diferentes computadoras. Quizás su interés esté en el comercio
electrónico u otras aplicaciones comerciales distribuidas, en el desarrollo de juegos multijugador o en el acceso remoto a recursos
computacionales. Piense en cómo la comunicación entre procesos encaja como un componente fundamental en todas estas aplicaciones
distribuidas.
el proceso en sí. Estas características de los subprocesos representan ventajas significativas sobre sus homólogos de procesos más
toscos, dependiendo de la implementación del subproceso y la técnica de programación de subprocesos.
Una ventaja común a todos los esquemas de subprocesos es que la comunicación entre subprocesos se puede lograr
utilizando memoria compartida dentro del espacio de direcciones del proceso. Esto significa que los hilos se comunican
leyendo y escribiendo en variables compartidas a las que ambos tienen acceso directo. Esto requiere acceso sincronizado
para garantizar la coherencia de los datos compartidos, pero es muy rápido porque la comunicación se realiza a la velocidad
de acceso a la memoria y no se necesita un cambio de contexto para invocar mecanismos externos, como con IPC a nivel de
proceso.
La programación a nivel de kernel es generalmente el mejor enfoque, ya que cada subproceso se programa
individualmente y, por lo tanto, un proceso que comprende varios subprocesos no se bloquea cuando uno de sus
subprocesos realiza E / S; solo se bloquea el subproceso específico, lo que permite que los otros subprocesos continúen y,
por lo tanto, el proceso puede seguir respondiendo y también puede realizar más trabajo en un lapso de tiempo
determinado que si fuera de un solo subproceso. El subproceso múltiple a nivel de kernel también ofrece un rendimiento
mejorado si los subprocesos se programan en diferentes procesadores o núcleos dentro del sistema, lo que permite que el
proceso utilice más recursos que el equivalente de un solo subproceso.
El subproceso a nivel de usuario es útil cuando el kernel no admite subprocesos directamente. La programación de
subprocesos debe realizarse dentro del propio proceso. Cuando el proceso se está ejecutando (según lo ve el sistema
operativo), cualquiera de sus subprocesos internos puede estar ejecutándose. Los subprocesos se pueden apropiar usando
mecanismos de temporizador programables o pueden ceder el control a otros subprocesos cuando sea apropiado (esto se
conoce como ceder).
El sistema operativo programa los subprocesos a nivel de usuario colectivamente; en lo que a él respecta, son una sola
entidad (el proceso) y no es consciente de los diferentes subprocesos. Esto afecta el rendimiento de dos formas principales:
en primer lugar, si uno de los subprocesos realiza E / S, se bloqueará todo el proceso; y en segundo lugar, los subprocesos
no se pueden distribuir en varios núcleos de procesador para aprovechar al máximo los recursos de hardware que están
disponibles (como pueden hacerlo los subprocesos a nivel de kernel).
Los dos enfoques se comparan en Figura 2.30 .
Figura 2.30 muestra las diferentes secuencias de ejecución de subprocesos que surgen cuando el mismo par de
procesos multiproceso se programan mediante mecanismos de subprocesos a nivel de usuario y de kernel, 1
respectivamente. El proceso P comprende
1 11
tres
12
subprocesos:
13
P = {PT, PT,
2
PT}; el proceso P comprende2dos subprocesos:
21 22
P=
{PT, PT}. Como se puede ver en el lado izquierdo de la figura, el subproceso interno de los procesos en el mecanismo de
subprocesamiento a nivel de usuario es invisible para el sistema operativo y, por lo tanto, cuando uno de los subprocesos
dentro de un proceso realiza IO, todo el el proceso está bloqueado. Por el contrario, en el esquema de subprocesos a nivel
de kernel, el planificador puede ejecutar cualquier subproceso en la cola lista independientemente del proceso al que
pertenece (aunque la selección puede verse influenciada por otros factores no discutidos aquí como la prioridad del
proceso). Un hilo se mueve a través de los estados del proceso (ejecutar, listo,
2,7 HILOS: INTRODUCCIÓN 77
FIGURA 2.30
La multitarea preventiva permite que el sistema operativo determine cuándo ocurren los cambios de contexto (es decir, entregar el
control a un subproceso diferente), lo que puede ser mejor para la equidad general del sistema. Sin embargo, el subproceso múltiple
cooperativo permite que los subprocesos individuales transfieran el control en los puntos apropiados dentro de su procesamiento y,
por lo tanto, pueden ser más eficientes con respecto al rendimiento de los procesos individuales, mientras que la preferencia
impulsada por el sistema operativo puede ocurrir en cualquier momento independientemente del estado interno del subproceso. . El
subproceso múltiple cooperativo requiere un diseño lógico cuidadoso y puede ser problemático en términos de rendimiento y
eficiencia si un subproceso no cede el control de manera adecuada; puede
78 CAPITULO 2 LA VISTA DEL PROCESO
privar temporalmente a otros subprocesos de recursos de procesamiento, o puede retener la CPU mientras espera
que algún recurso esté disponible.
Independientemente de cuál de estos enfoques de programación utilice el sistema operativo, es el sistema
operativo el que elige el siguiente hilo para ejecutar, del conjunto que está disponible, es decir, listo.
Hilo A
{
...
B.unir ()
Realizar la acción Y
...
}
Hilo B
{
Realizar la acción X
}
FIGURA 2.31
Ejemplo de pseudocódigo que muestra el uso de la primitiva de unión para sincronizar subprocesos.
2,7 HILOS: INTRODUCCIÓN 79
int main ()
{
...
// Crea el primer hilo de trabajo (hilo 1)
thread thread1 (Función1 / * función que realizará el hilo * /,
"AAAAAAAAAAAA" / * parámetros para la función del hilo * /);
thread1.join (); // el hilo de llamada (principal) espera a que el hilo llamado (hilo1) termine
...
}
FIGURA 2.32
Figura 2.32 muestra las partes de control de subprocesos del código del programa de muestra Threads_Join. El hilo principal crea
el primer hilo de trabajo (hilo1) y luego une el hilo a sí mismo. Esto hace que el hilo principal se detenga hasta que termine el hilo1.
Thread1 luego crea thread2 y une thread2 a sí mismo, haciendo que thread1 se detenga hasta que thread2 termine. De manera
similar, thread2 crea thread3 y une thread3 a sí mismo; por lo tanto, thread2 hace una pausa hasta que thread3 termina. El efecto de
esta cadena de uniones es que los hilos completan su trabajo en el orden inverso al que fueron creados, el hilo 3 termina primero y el
hilo principal termina al final. El comportamiento sincrónico que surge se ilustra en
Figura 2.33 .
Alternativamente, cuando no exista un requisito de comportamiento de subprocesos sincronizados, puede ser deseable permitir
que todos los subprocesos se ejecuten simultáneamente (en lo que respecta al proceso) y dejar que el sistema operativo los
programe libremente, según su mecanismo de programación de subprocesos. La primitiva desprendimiento se puede utilizar para
lograr esto. detach () tiene esencialmente el significado opuesto a join (); significa que el subproceso que realiza la llamada no debe
esperar al subproceso llamado, sino que puede ejecutarse simultáneamente con él. Esto se ilustra en
Figura 2.34 , que muestra el pseudocódigo de un escenario en el que la acción X y la acción Y se pueden realizar al
mismo tiempo.
El uso de detach para ejecutar subprocesos de forma asincrónica se explora con más detalle en el programa
Threads_Detach (ver Figura 2.35 ).
Figura 2.35 muestra las partes de control de subprocesos del código del programa de muestra Threads_Detach.
El subproceso principal crea los tres subprocesos de trabajo y luego separa los tres de sí mismo. Esto hace que los
subprocesos se ejecuten de forma asincrónica entre sí (el subproceso de creación no espera a que se completen los
subprocesos creados; en cambio, se programa al mismo tiempo que ellos). Este comportamiento se ilustra en Figura
2.36 .
80 CAPITULO 2 LA VISTA DEL PROCESO
Hora
FIGURA 2.33
Comportamiento síncrono y orden de ejecución de subprocesos que surgen del uso de la primitiva de unión en el programa de
muestra Threads_ Join.
Hilo A
{
...
B.detach ()
Realizar la acción Y
...
}
Hilo B
{
Realizar la acción X
}
FIGURA 2.34
Ejemplo de pseudocódigo del uso de la primitiva de separación para permitir que los subprocesos se ejecuten simultáneamente.
Los subprocesos pueden ceder la CPU cuando han completado una tarea en particular o cuando necesitan esperar a
que se realice alguna otra actividad antes de continuar; esto se llama "rendimiento" y lo realiza el subproceso
correspondiente utilizando la primitiva rendimiento (). Sin embargo, el hilo no elige qué hilo se ejecuta a continuación; este
aspecto es una función del programador y lo realiza el sistema operativo en función del subconjunto de otros subprocesos
que están listos para ejecutarse.
El uso de las primitivas join () y detach () para lograr comportamientos de subprocesos sincrónicos y
asincrónicos, respectivamente, se investiga en Actividad P11 .
void Function1 (cadena sStr)
{
...
}
int main ()
{
...
// Crea tres hilos
thread thread1 (Función1 / * función que realizará el hilo * /,
"AAAAAAAAAAAA" / * parámetros para la función del hilo * /);
thread thread2 (Función2 / * función que realizará el hilo * /,
"BBBBBBBBBBBB" / * parámetros para la función del hilo * /);
thread thread3 (Función3 / * función que realizará el hilo * /,
"0000-CCCCCC" / * parámetros para la función del hilo * /);
FIGURA 2.35
Hora
FIGURA 2.36
Comportamiento de ejecución de subprocesos asíncronos que surge del uso de la primitiva desconectar en el programa de muestra
Threads_Detach.
82 CAPITULO 2 LA VISTA DEL PROCESO
Método
Esta actividad utiliza un par de programas Threads_Join y Threads_Detach para ilustrar cómo los subprocesos de trabajo creados se
pueden ejecutar de forma sincrónica o asincrónica. La actividad se desarrolla en dos partes.
muestra el comportamiento cuando se usa join () para sincronizar el comportamiento de los subprocesos; aquí, el subproceso que llama (el que usa la primitiva de
unión) espera a que termine su subproceso llamado antes de continuar (es decir, los subprocesos se ejecutan sincrónicamente unos con respecto a otros). La imagen
de la pantalla derecha muestra el comportamiento cuando se usa detach () para iniciar los subprocesos; en este caso, los subprocesos se ejecutan de forma
asincrónica.
Reflexión
Los subprocesos son un medio para garantizar la capacidad de respuesta dentro de los programas, al tener diferentes "subprocesos" de lógica que se ejecutan de
forma independiente y en paralelo con otros subprocesos. Esto no solo introduce el potencial para una operación eficiente, sino que también puede conducir a un
comportamiento general, que es difícil de predecir, especialmente con respecto a la sincronización, como ha demostrado esta actividad, y por lo tanto enfatiza la
Esta actividad ha ilustrado la naturaleza fundamental de los subprocesos, aunque es importante señalar que este es un ejemplo simple y que las
aplicaciones multiproceso pueden exhibir un comportamiento significativamente más complejo que el que se muestra aquí.
Incluso una vez que el diseño es correcto, las pruebas necesarias para confirmar la corrección y la coherencia son en sí mismas
complejas porque deben garantizar que, en todos los comportamientos posibles de sincronización y secuenciación, el resultado final
siga siendo correcto. El uso erróneo de mecanismos como semáforos y mutex y la primitiva de unión puede conducir a situaciones de
punto muerto en las que todos los subprocesos están bloqueados, cada uno esperando a que otro subproceso realice alguna acción.
La etapa de diseño debe garantizar que no se produzcan tales problemas, pero debido a las complejas relaciones de tiempo que
pueden existir entre varios subprocesos en tiempo de ejecución, este aspecto en sí puede ser un desafío. Por lo tanto, el diseño,
desarrollo y prueba de versiones de aplicaciones multiproceso puede ser significativamente más complejo que para las versiones de
un solo subproceso.
84 CAPITULO 2 LA VISTA DEL PROCESO
un solo subproceso, si el único subproceso realiza una actividad de E / S, como leer un archivo grande o esperar un mensaje de otro proceso,
como es un escenario común en aplicaciones distribuidas, el planificador bloquea el proceso en sí. Esto tiene el efecto indeseable de que la
aplicación se "congela" y no responde a los comandos del usuario. Si el código está diseñado de manera que las tareas que probablemente se
bloqueen estén cada una dentro de subprocesos de trabajo separados que se ejecutan simultáneamente entre sí, entonces la aplicación puede
seguir respondiendo a la entrada del usuario. Por ejemplo, considere un juego en red de acción rápida que tiene una interfaz gráfica de usuario
basada en eventos, envía y recibe mensajes regularmente a través de una red. y también requiere acceso local a imágenes y clips de película, a
los que se accede como archivos desde un almacenamiento secundario local, como un disco duro. En una aplicación de este tipo, un requisito no
funcional principal es una alta capacidad de respuesta a los comandos del usuario y las actualizaciones de pantalla correspondientes; de hecho,
esto probablemente sería un factor determinante principal en cuanto a la usabilidad y la calificación de satisfacción del usuario del juego. El
acceso al disco duro para recuperar un archivo o esperar a que lleguen mensajes a través de la red interferiría con la capacidad de respuesta
rápida y fluida de la interfaz de usuario que se requiere. Esta es una situación en la que un enfoque multiproceso sería ventajoso, ya que el
servicio de la interfaz de usuario podría funcionar de forma asincrónica con las otras actividades de IO. un requisito principal no funcional es una
alta capacidad de respuesta a los comandos del usuario y las correspondientes actualizaciones de pantalla; de hecho, esto probablemente sería
un factor determinante principal en cuanto a la usabilidad y la calificación de satisfacción del usuario del juego. El acceso al disco duro para
recuperar un archivo o esperar a que lleguen mensajes a través de la red interferiría con la capacidad de respuesta rápida y fluida de la interfaz de usuario que se requiere
Sin embargo, el juego más simple que se utiliza como caso de estudio a lo largo de este libro no requiere acceso al
almacenamiento secundario local mientras se ejecuta el juego. En este caso, una solución multiproceso aún podría usarse
para realizar la E / S de red en un subproceso separado de la interfaz de usuario, pero dado que no es un juego de acción
rápida, los eventos del usuario ocurren en el marco de tiempo de varios segundos en lugar de varios. en un solo segundo,
se ha adoptado un enfoque más simple que implica una solución de un solo subproceso con un conector sin bloqueo para
que el búfer de mensajes pueda inspeccionarse sin que el proceso tenga que esperar si no hay ningún mensaje disponible.
Esto logra el nivel adecuado de capacidad de respuesta sin la complejidad de diseño adicional de una solución multiproceso.
El subproceso múltiple es ventajoso para aprovechar al máximo los recursos computacionales de un procesador. En una
aplicación paralela, se crearán varios subprocesos de trabajo, cada uno de los cuales realiza el mismo cálculo pero trabaja
con diferentes subconjuntos de datos. Si la aplicación se ejecuta en una plataforma multinúcleo (asumiendo el modelo de
subprocesos a nivel de kernel), su proceso puede usar tantos núcleos como subprocesos, sujeto a la política de
programación del sistema operativo. Incluso en una plataforma de un solo núcleo, el subproceso múltiple puede aumentar
el rendimiento de dichas aplicaciones porque los subprocesos individuales pueden realizar E / S (lo que hace que se
bloqueen), mientras que otros dentro del mismo proceso continúan ejecutándose.
use porque cada una de las dos actividades se bloquearía mientras espera las actividades de IO (la actividad de envío espera
la entrada del usuario desde el teclado, mientras que la actividad de recepción espera que los mensajes IPC lleguen al
conector). Por el contrario, el proceso IPC multiproceso espera un mensaje mientras continúa enviando mensajes escritos
cada vez que el usuario ingresa una nueva línea. El envío y la recepción se ejecutan en subprocesos separados, utilizando
sockets de bloqueo. Se utilizan enchufes separados para que no haya condiciones de carrera dentro del comportamiento del
proceso. El programa tiene dos argumentos de línea de comandos (puerto de envío y puerto de recepción) de modo que se
pueden configurar dos instancias para comunicarse juntas, como se explica en
Actividad P12 .
Como se ilustra en Figuras 2.37 y 2,38 , la función de recepción se coloca en un hilo de trabajo,
así que cuando este hilo se bloquea mientras espera un mensaje de IPC, no bloquea el hilo principal. En este
ejemplo simple, la actividad de envío se ejecuta en el hilo principal, ya que no existe una lógica empresarial como
tal. En una aplicación más compleja con lógica empresarial específica, sería posible colocar la actividad de envío en
un subproceso de trabajo adicional para que la lógica empresarial pueda funcionar mientras el subproceso de envío
está bloqueado esperando que el usuario escriba la entrada; Las secciones clave del código relacionadas con el
aspecto del subproceso se muestran, con anotaciones, en Figuras 2.39 (el hilo principal) y 2,40 (el hilo de recepción).
Actividad P12 utiliza dos instancias de proceso del programa Multithreaded_IPC para explorar el
comportamiento de comunicación asincrónica logrado por el diseño de multiproceso.
Actividad P13 utiliza la simulación “Subprocesos — Introductorio” dentro del entorno de trabajo de los sistemas
operativos para explorar el comportamiento de los subprocesos e investigar las diferencias entre la ejecución de
subprocesos síncronos y asincrónicos.
Hilo principal
Comienzo
Crear sockets
Configurar estructuras de direcciones
Enlazar a la dirección local (para facilitar la recepción) Iniciar el hilo de
trabajo (ReceiveThread)
ReceiveThread.detatch ()
Recibir hilo
Comienzo
Círculo
Recibir mensaje de IPC
Mostrar mensaje al usuario Fin del
ciclo
Final
FIGURA 2.37
Realiza
inicialización
Se creó el hilo de trabajo
hilo
bloques
hilo
bloques Gestiona IPC
Gestiona IPC
enviar actividad. hilo recibir actividad.
Bloquea cuando bloques Bloquea cuando
hilo esperando
esperando un
entrada del usuario
bloques Mensaje de IPC
desde el teclado
hilo
bloques
hilo
bloques
Hora
FIGURA 2.38
...
bSuccess = CreateSendSocket ();
...
bSuccess = CreateReceiveSocket ();
...
m_bMainThreadRunning = true;
// Usa detach () para hacer que ReceiveThread se ejecute de forma asíncrona con main ()
// Esto significa que el hilo de envío 'principal' y el hilo de recepción se ejecutan en paralelo
ReceiveThread.detach ();
// El hilo de envío simplemente continúa desde aquí (es el hilo de ejecución 'principal' original) mientras (1)
{
para (int iIndex = 0; iIndex <SEND_BUFFER_SIZE; iIndex ++) {
// Limpiar el búfer de entrada
m_szSendBuf [iIndex] = 0;
}
cout << endl << "SendThread: Escriba un mensaje para enviar (\" Salir \ "para salir):"; cin >>
m_szSendBuf;
Si( 0 == strncmp (m_szSendBuf, "SALIR", 4) || 0 ==
strncmp (m_szSendBuf, "Salir", 4) || 0 ==
strncmp (m_szSendBuf, "salir", 4))
{
m_bMainThreadRunning = falso; // Señal para recibir subproceso para detener su funcionamiento
cout << endl << "Saliendo" << endl;
CloseSocketsAndSalir ();
}
iNumberOfBytesSent = Enviar mensaje ();
cout << endl << "SendThread: Enviado" << m_szSendBuf << "(" << iNumberOfBytesSent << "bytes)" << endl;
}
FIGURA 2.39
Secciones clave anotadas del Programa IPC multiproceso, hilo principal ( Código C ++).
2,7 HILOS: INTRODUCCIÓN 87
FIGURA 2.40
Secciones clave anotadas del Programa IPC multiproceso, recibir hilo ( Código C ++).
funcionalidad de envío, mientras que se crea un subproceso de trabajo para ocuparse de la funcionalidad de recepción).
2. Ejecute dos instancias del programa, cada una en una ventana de comandos separada, recordando intercambiar el orden de los puertos en los
argumentos de la línea de comandos para que el puerto de envío de la primera instancia sea el mismo que el puerto de recepción de la
segunda instancia, y viceversa . Por ejemplo,
IPC de subprocesos múltiples 8002 8001 (enviar al puerto 8002, recibir en el puerto 8001)
3. Experimente con los dos procesos. Intente enviar y recibir en varios pedidos, para confirmar que el proceso en sí sigue respondiendo,
incluso cuando esté esperando una entrada del teclado o esperando que llegue un mensaje.
Las capturas de pantalla a continuación proporcionan ejemplos de los procesos IPC de multiproceso en acción durante el experimento. Tenga en
cuenta que la lógica del programa está configurada específicamente por los parámetros de número de puerto proporcionados en la línea de comando.
Por lo tanto, los dos procesos que se muestran a continuación se comunican entre sí utilizando dos canales de comunicación unidireccionales
separados (a nivel de hilo) para proporcionar comunicación bidireccional a nivel de proceso.
Gastos esperados
Debería ver que las dos instancias del programa funcionan de forma independiente porque son procesos separados programados
individualmente por el sistema operativo. Realizan IPC a través de sockets y esta comunicación funciona de forma independiente en cada
dirección. Los procesos siguen siendo receptivos porque utilizan subprocesos separados para realizar las acciones de envío y recepción y,
por lo tanto, estos subprocesos se bloquean de forma individual, según sea necesario.
Reflexión
Esta actividad proporciona un ejemplo de subprocesos a nivel de kernel. Los subprocesos de envío y recepción de cada bloque mientras
esperan una entrada. En el caso del hilo de recepción, el bloqueo se produce cuando espera a que llegue un mensaje de la otra copia del
programa. En el caso del hilo principal (que realiza la función de envío), el bloqueo ocurre cuando se espera que el usuario escriba un
mensaje en el teclado que será enviado a la otra copia del programa.
2,7 HILOS: INTRODUCCIÓN 89
Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido Operating Systems Workbench y la documentación de
respaldo como se explica en Actividad P5 .
Lea el documento "Actividades (introductorias) de subprocesos".
Método
Esta actividad utiliza la simulación "Introducción a los subprocesos", que se encuentra en la pestaña "Subprocesos" del entorno de trabajo
de sistemas operativos. La simulación admite hasta tres subprocesos que se ejecutan simultáneamente. Los subprocesos colorean píxeles
elegidos al azar en un área de visualización, cada subproceso usa un color específico (rojo, verde o azul) para los tres subprocesos,
respectivamente. El progreso de los hilos puede ser seguido visualmente por la densidad de color del área de la pantalla, por lo que si el
hilo "rojo" recibe más tiempo de cálculo que los otros dos, la "intensidad del rojo" será mayor que las intensidades del verde y el azul.
Parte 1. Programación de subprocesos asíncronos (el proceso no controla activamente la secuencia de ejecución de
subprocesos)
1. Crea e inicia un solo hilo. Observe cómo el hilo pinta gradualmente el área de dibujo (inicialmente negra) con su propio
color, la intensidad de un píxel en particular se incrementa cada vez que el píxel se elige al azar. Deje esto funcionando
durante unos minutos o más y debería ver una coloración casi total.
2. Restablezca el lienzo de dibujo. Cree e inicie los tres subprocesos juntos con la misma prioridad. Deje esto activo durante unos
minutos o más y debería ver una coloración casi total, que se aproxima al blanco, es decir, una mezcla aproximadamente uniforme de
píxeles rojos, verdes y azules.
3. Restablezca el lienzo de dibujo. Cree y comience los tres hilos juntos; esta vez, establezca los hilos en una mezcla de
prioridad más alta y más baja (la diferenciación entre los otros niveles puede ser bastante fina y el efecto difícil de ver). Deje
esto funcionando durante unos minutos o más y debería ver los hilos de mayor prioridad dominando con su cobertura de
color.
Parte 2. Programación de subprocesos síncronos (la secuencia de ejecución de subprocesos se controla dentro del propio
proceso)
1. Seleccione el botón de opción "Imponer prioridad externamente". Utilice el botón "Crear e iniciar subprocesos" para iniciar la simulación con todos
los subprocesos con la misma prioridad. Notará que cada hilo se ejecuta durante un breve período de tiempo en secuencia, lo que produce un
efecto de ciclo de color. Experimente con diferentes prioridades de los hilos (use las prioridades más altas y más bajas para obtener los efectos
más visibles). Observe que en este ejemplo, la prioridad se traduce en la parte del tiempo de ejecución que obtiene cada hilo, por lo que el color
promedio del área de dibujo estará dominado por los colores atribuidos a los hilos de mayor prioridad.
Las cuatro capturas de pantalla a continuación proporcionan instantáneas de la simulación de subprocesos para la parte 1 (en la secuencia: arriba a la izquierda,
arriba a la derecha, abajo a la izquierda, abajo a la derecha, respectivamente): solo se ejecuta el hilo rojo, el hilo verde tiene la prioridad más alta, el rojo y los
subprocesos azules tienen la máxima prioridad y, finalmente, todos los subprocesos tienen la misma prioridad.
90 CAPITULO 2 LA VISTA DEL PROCESO
Las capturas de pantalla a continuación muestran la transición del hilo rojo al hilo verde en la parte 2, a la izquierda cuando todos los hilos tienen la
misma prioridad y a la derecha cuando el hilo rojo tiene la prioridad más baja y, por lo tanto, la pantalla permanece principalmente azul incluso después del
Gastos esperados
Al hacer que cada hilo se dibuje en un color primario diferente, la simulación proporciona un medio de visualización de la parte del tiempo de
ejecución que recibe cada hilo.
En la parte 1, los subprocesos se ejecutan al mismo tiempo y, debido a que el entrelazado de subprocesos es muy rápido, parece que se ejecutan en paralelo.
La parte 2 muestra el efecto de la programación síncrona en la que solo se ejecuta un hilo en cualquier instante, lo que genera un efecto de
ciclo de color. Es importante darse cuenta de que la cantidad de tiempo durante el cual se ejecuta cada hilo se ha exagerado para
2.9 USO DE TEMPORIZADORES DENTRO DE LOS PROGRAMAS 91
Asegúrese de que el efecto funcione visualmente para fines de demostración. En los sistemas reales que programan subprocesos en secuencia,
normalmente pueden ejecutarse durante 10 o quizás 100 milisegundos a la vez.
Reflexión
Esta simulación ilustra la programación de subprocesos dentro de un proceso. Puede haber muchos procesos en un sistema, como hemos
comentado anteriormente. Dentro de cada proceso, puede haber muchos hilos. Los subprocesos individuales son programados en su totalidad por
el propio sistema operativo en algunos sistemas y están controlados hasta cierto punto a nivel de proceso (por primitivas en el código del proceso)
en otros sistemas.
Desde el punto de vista del diseño de la aplicación, intente pensar en situaciones en las que hilos separados dentro de un proceso sea
una solución adecuada, en lugar de tener procesos separados.
Exploración adicional
Experimente más con la simulación "Introducción a los subprocesos" para obtener una comprensión más profunda.
Actividad P11 ha introducido subprocesos y ha demostrado su principal beneficio, que es que los subprocesos
operan en paralelo entre sí, pero esto también revela una debilidad potencial: puede ser difícil predecir la secuencia
de operaciones en varios subprocesos y puede ser difícil garantizar que un evento específico ocurre antes, o con
mayor frecuencia, que otro. Los temporizadores permiten que los bloques de código se ejecuten de forma
asíncrona a la función principal y pueden ser una alternativa útil a los subprocesos para garantizar la capacidad de
respuesta sin los posibles gastos generales de complejidad que pueden surgir con las soluciones multiproceso. La
diferencia fundamental es que los subprocesos son programados por el sistema operativo y, por lo tanto, el
desarrollador no tiene control sobre su orden o tiempo relativo. Los temporizadores, por otro lado, hacen que sus
controladores de eventos se invoquen a intervalos decididos por el desarrollador y, por lo tanto,
Un ejemplo simple del uso de actividad basada en temporizador es una aplicación de monitoreo
ambiental, que tiene un sensor de temperatura y mide el valor de temperatura en un intervalo de tiempo
fijo. Distributed Systems Workbench contiene un ejemplo de esto (la aplicación de sensor de temperatura
distribuida), que se analiza en el Capítulo 3.
Figura 2.41 ilustra la secuenciación en tiempo de ejecución de un programa que realiza tres funciones basadas en
temporizador. La lógica del programa se inicia en su función principal. Se establecen tres temporizadores programables, y
cuando cada uno de ellos expira, su función relacionada se invoca automáticamente. Una vez finalizada la función, el control
se devuelve a la función principal en el punto en el que se detuvo anteriormente, de la misma manera que funciona regresar
de una llamada de función estándar. Las secciones comentadas del programa Timers_Demonstration se proporcionan en Figura
2.42 .
Figura 2.43 muestra la salida producida por el proceso Timers_Demonstration. Los tres temporizadores se ejecutan de forma
independiente, de manera efectiva como subprocesos separados, de forma asincrónica entre sí y con el subproceso principal. Al inspeccionar de
cerca la salida, puede ver que en el punto donde han pasado los 6, los tres temporizadores expiran al mismo tiempo, y en tal situación, no es
predecible al mirar el código cuál será realmente el primero en ser reparado. . En este caso, el temporizador 3 se revisa primero, seguido del
temporizador 2 y luego el temporizador 1. De manera similar, también puede ver que después de que hayan pasado 2 segundos, el controlador
de eventos del segundo temporizador recibe servicio antes que el controlador del primer temporizador.
I2
función1 activa
I3 función2 activa
Clave
Hora
FIGURA 2.41
...
System.Timers.Timer Timer1 = nuevo System.Timers.Timer (); // Crear primer temporizador
Timer1.Elapsed + = nuevo System.Timers.ElapsedEventHandler (OnTimedEvent_Timer1); // Establecer el controlador de eventos para el 1er temporizador
Timer1.Interval = 1000; // Establecer el intervalo de tiempo en 1 segundo (1000 ms)
Timer1.Enabled = falso; // No inicie la ejecución del temporizador todavía
FIGURA 2.42
FIGURA 2.43
Es posible lograr un comportamiento similar a un hilo dentro de un proceso mediante el uso de temporizadores y sus controladores
de eventos para lograr múltiples "hilos" de control. Los controladores de eventos del temporizador son similares a los subprocesos en
el sentido de que se ejecutan independientemente de los otros controladores de eventos y también independientemente del
subproceso principal de control. Sin embargo, la principal diferencia es que con los temporizadores, el tiempo real de ejecución de los
controladores de eventos está bajo el control del programador. Este enfoque es mejor para tareas periódicas dentro de un proceso.
94 CAPITULO 2 LA VISTA DEL PROCESO
y se puede usar para lograr un efecto en tiempo real incluso sin el uso de un programador en tiempo real (aunque la precisión de temporización
de los temporizadores no es confiable para intervalos de tiempo pequeños, generalmente menos de aproximadamente 100 ms).
La cuenta regresiva del temporizador es administrada por el kernel del sistema operativo y, por lo tanto, la cuenta regresiva
continúa independientemente de si el proceso está en el estado de ejecución o no (es decir, el proceso no está disminuyendo el
temporizador en sí mismo, esto ocurre automáticamente en la medida en que el proceso está preocupado y el controlador de eventos
se invoca automáticamente cuando el temporizador expira, lo que significa que su cuenta regresiva llega a cero).
Una situación de ejemplo en la que utilizo temporizadores con regularidad para lograr un
comportamiento similar al de un subproceso es en sistemas integrados de gama baja donde el
código se ejecuta "en el metal" (es decir, no hay un sistema operativo para administrar los
recursos). Mi aplicación tiene que administrar directamente los recursos que usa y se ejecuta de
forma nativa como un único hilo de control, siguiendo las diversas ramas del código en función
de una combinación de la lógica del programa y las entradas contextuales de los sensores. Estos
sistemas tienen varios módulos de hardware que pueden generar interrupciones, incluidos
típicamente varios temporizadores programables. Los manejadores de interrupciones tienen
mayor prioridad que el hilo principal de ejecución de instrucciones, por lo que puedo crear un
"hilo" de alta prioridad colocando su lógica en una rutina de interrupción del temporizador y
configurando el temporizador para que interrumpa a una velocidad adecuada. Por ejemplo,
En algunos sistemas es posible mover un proceso en ejecución; se ha hecho en algunos esquemas de carga compartida,
por ejemplo. Sin embargo, esto es mucho más complejo que mover un recurso. Para mover un proceso, primero debe
detenerse (congelarse), luego su estado de proceso se captura y se transfiere a una nueva ubicación, y el proceso se reinicia
en ejecución para que continúe desde el lugar donde lo dejó. El proceso no debe darse cuenta
2.11 EL ESTUDIO DE CASO DESDE LA PERSPECTIVA DEL PROCESO 95
algo diferente su estado debe haber sido preservado para que su cálculo sea consistente a pesar del
movimiento. Esto es análogo a una persona que se mueve entre habitaciones de hotel idénticas mientras
duerme, de modo que cuando se despierta, ve el mismo entorno y continúa desde donde lo dejó sin saber
que ha sido reubicado.
Se requieren temporizadores programables dentro de la aplicación del juego. Se utiliza un temporizador en los componentes del
cliente y del servidor para comprobar si hay mensajes entrantes a intervalos regulares de 100 ms. Esencialmente, el controlador de
interrupciones del temporizador se utiliza para ejecutar la funcionalidad de recepción de forma asincrónica con respecto a la
96 CAPITULO 2 LA VISTA DEL PROCESO
thread, que maneja la interfaz de usuario y la funcionalidad de envío (que se maneja directamente desde la entrada
del usuario). Mediante el uso de un conector sin bloqueo para recibir, en combinación con el temporizador, que
comprueba los mensajes recibidos con frecuencia, se logra un efecto de respuesta múltiple sin necesidad de una
solución multiproceso.
No hay elementos que dependan del tiempo en la propia lógica del juego.
se recibe un mensaje cada vez que se realiza un movimiento de cliente, y el servidor tiene que calcular el nuevo estado del juego y enviar un
mensaje a cada uno de los dos clientes. Dada la baja tasa de esta operación y la baja complejidad de la propia lógica del juego, el servidor no
Sin embargo, la lógica del juego podría rediseñarse como una solución multiproceso si es necesario, por ejemplo, si se
agrega un aspecto de acción rápida con requisitos de tiempo específicos a la acción del juego o si se agregan restricciones
de tiempo de comunicación más complejas. En tal caso, sería apropiado en el lado del cliente separar la funcionalidad de
envío y la funcionalidad de recepción, cada una en sus propios subprocesos y tener un tercer subproceso para manejar los
eventos de la interfaz de usuario.
La falta de requisitos de subprocesos múltiples tanto en el lado del cliente como en el del servidor hace que el juego sea muy
adecuado como caso de estudio para los propósitos de este libro porque el funcionamiento del programa es fácil de entender, lo que
permite que la discusión se centre en los aspectos de arquitectura, distribución y comunicación. .
1 aplicación El juego
2 canales de IPC
FIGURA 2.44
del juego comprende dos instancias de proceso del programa de cliente único (es decir, no dos programas de cliente
diferentes). Como muestra la figura, el IPC se encuentra entre el proceso de cada cliente y el proceso del servidor. Los
canales de IPC mostrados pueden considerarse comunicación privada entre cada par de procesos. Al seguir el paradigma
cliente-servidor, es habitual que los clientes no se comuniquen directamente. La combinación de la arquitectura del juego
cliente-servidor y el IPC basado en sockets da lugar a dos configuraciones de hardware, es decir, dos mapeos diferentes
entre los procesos y las computadoras en las que se ejecutan. El primero de ellos es que cada proceso puede residir en una
computadora separada. Sin embargo, solo la ubicación de los clientes es importante (es decir, su ubicación depende de
dónde se encuentren los jugadores reales), pero el servidor puede estar en cualquier lugar de la red y, por lo tanto, también
puede estar en la misma computadora que uno de los clientes. Técnicamente, sería posible poner los tres procesos en la
misma computadora, y en algunas aplicaciones, esto podría tener sentido, pero este mapeo no es aplicable en un juego de
dos jugadores en el que cada jugador necesita su propio teclado y pantalla en orden. para interactuar con el juego.
Figura 2.44 también ayuda a reforzar el concepto muy importante de que un servidor es un proceso y no una
computadora. Un error común es que un servidor es una computadora; esto se debe a que, a menudo, se proporciona una
computadora dedicada para ejecutar un servicio en particular (es decir, para albergar un proceso de servidor) y luego la
computadora misma recibe el nombre de "el servidor"; pero como se ilustra en la figura, el servidor es claramente un
proceso y, de hecho, podría moverse de una computadora a otra sin afectar la forma en que funciona el juego.
1. Considere un sistema de procesador de un solo núcleo con tres procesos activos {A, B, C}. Para cada uno de los
configuraciones de estado de proceso af a continuación, identifique si la combinación puede ocurrir o no. Justifica tus
respuestas.
98 CAPITULO 2 LA VISTA DEL PROCESO
a B C D mi F
2. Para un proceso dado, ¿cuál de las siguientes secuencias de estado (ae) es posible? Justifica tu
respuestas.
están presentes. Calcule cuánto tiempo tomarían cuatro de estas tareas si se iniciaran al mismo tiempo (indique las
suposiciones).
4. Una tarea de cálculo intensivo en tiempo no real se ejecuta tres veces en un sistema, en diferentes momentos del
día. Los tiempos de ejecución son 60, 61 y 80, respectivamente. Dado que la tarea realiza exactamente el mismo cálculo
cada vez que se ejecuta, ¿cómo se contabilizan los diferentes tiempos de ejecución?
5. Considere el comportamiento de programación investigado en Actividad P7 .
(a) ¿Qué se puede hacer para evitar que las tareas en tiempo real tengan su comportamiento en tiempo de ejecución afectado por
(B) ¿Qué necesita saber un programador en tiempo real sobre los procesos que un programador
el planificador no lo hace?
Los siguientes ejercicios se basan en el " Algoritmos de programación: avanzado ”Simulación dentro del
Operating Systems Workbench.
Ejercicio 1. Comparación de los algoritmos de programación SRJN y RR. BothSRJNandRRare
preventivas. El objetivo de esta actividad es comparar la eficiencia y equidad de los dos algoritmos
Tamaño cuántico = 10 ms
Configuración del programador = Round Robin
2.12 EJERCICIOS DE FIN DE CAPÍTULO 99
Q1. ¿Qué algoritmo fue más eficiente en estas simulaciones? ¿Se debe esta diferencia a un conjunto de procesos
cuidadosamente seleccionados o se aplicará en general?
Un planificador "justo" proporcionará tiempos de espera aproximadamente similares a todos los procesos.
Q2. ¿Qué algoritmo fue más justo en estas simulaciones? ¿Se debe esta diferencia a un conjunto de procesos
cuidadosamente seleccionados o se aplicará en general?
El bajo tiempo de espera y el bajo "tiempo total en el sistema" son medidas de la capacidad de respuesta de los
procesos.
5. Calcule el tiempo medio de espera y el tiempo medio total en el sistema para el conjunto de procesos, para
cada simulación.
Q3. ¿Qué algoritmo dio lugar a la mayor capacidad de respuesta de los procesos en estas simulaciones? ¿Se debe esta
diferencia a un conjunto de procesos cuidadosamente seleccionados o se aplicará en general?
Tamaño cuántico = 10 ms
4. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS DEL PROCESO.
5. Modifique la configuración de la siguiente manera:
Tamaño cuántico = 15 ms
6. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS DEL PROCESO.
7. Modifique la configuración de la siguiente manera:
Tamaño cuántico = 25 ms
10. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS DEL PROCESO.
Q1. ¿Cómo afecta el tamaño cuántico a la equidad (es decir, qué tamaño cuántico hace que los procesos encuentren
tiempos de espera similares y qué tamaño cuántico causa la mayor diferencia en los tiempos de espera)? ¿Por qué
esto es tan?
100 CAPITULO 2 LA VISTA DEL PROCESO
11. Para cada conjunto de resultados, calcule el tiempo medio de espera y el tiempo medio total en el sistema para
los procesos.
Q2. ¿Cómo afecta el tamaño cuántico al tiempo medio de espera? ¿Por qué esto es tan?
Q3. ¿Cómo afecta el tamaño cuántico al tiempo total medio en el sistema? ¿Por qué esto es tan?
Ejercicio 3. Investigación de los efectos de cambiar la latencia del dispositivo IO
2. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS DEL PROCESO.
3. Modifique la configuración de la siguiente manera:
4. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS DEL PROCESO.
5. Modifique la configuración de la siguiente manera:
6. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS DEL PROCESO.
7. Modifique la configuración de la siguiente manera:
10. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS DEL PROCESO.
Q1. A medida que aumenta la latencia del dispositivo de E / S, ¿qué sucede con el tiempo total en el sistema (el tiempo
que tarda en ejecutarse para completarse) de cada proceso?
¿Alguno de los procesos tarda más en ejecutarse? Si es así, ¿por qué?
¿Alguno de los procesos tarda menos en ejecutarse? Si es así, ¿por qué?
11. Para cada conjunto de resultados, calcule el tiempo medio de espera y el tiempo medio total en el sistema para el
Procesos.
Q2. ¿Cómo afecta la latencia del dispositivo IO al tiempo total medio en el sistema? ¿Por qué esto es tan?
¿Es esto lo que esperabas?
Q3. ¿Cómo afecta la latencia del dispositivo IO al tiempo medio de espera? ¿Por qué es esto así (piense
con cuidado)? ¿Es esto lo que esperabas?
Ejercicio 4. Predecir y analizar el comportamiento del algoritmo de programación SRJN (parte 1)
1. Configure la simulación de la siguiente manera:
Configuración A:
Proceso 1: Entre llegadas 30, tiempo de cálculo 12, retraso de inicio 0 e infinitas tareas.
Proceso 2: Entre llegadas 20, tiempo de cálculo 10, retraso de inicio 0 e infinitas tareas.
Proceso 3: Entre llegadas 25, tiempo de cálculo 2, retraso de inicio 0 e infinitas tareas.
Configuración B:
Proceso 1: Entre llegadas 33, tiempo de cálculo 16, retraso de inicio 0 e infinitas tareas.
Proceso 2: Entre llegadas 20, tiempo de cálculo 10, retraso de inicio 0 e infinitas tareas.
Proceso 3: Entre llegadas 25, tiempo de cálculo 2, retraso de inicio 0 e infinitas tareas.
Configuración C:
Proceso 1: Entre llegadas 33, tiempo de cálculo 11, retraso de inicio 0 e infinitas tareas.
Proceso 2: Entre llegadas 33, tiempo de cálculo 11, retraso de inicio 0 e infinitas tareas.
Proceso 3: Entre llegadas 33, tiempo de cálculo 11, retraso de inicio 0 e infinitas tareas.
Configuración D:
Proceso 1: Entre llegadas 33, tiempo de cálculo 6, retraso de inicio 0 e infinitas tareas.
Proceso 2: Entre llegadas 5, tiempo de cálculo 2, retraso de inicio 0 e infinitas tareas.
Proceso 3: Entre llegadas 5, tiempo de cálculo 2, retraso de inicio 0 e infinitas tareas.
Configuración E:
Proceso 1: Entre llegadas 30, tiempo de cálculo 12, retraso de inicio 0 e infinitas tareas.
Proceso 2: Entre llegadas 20, tiempo de cálculo 10, retraso de inicio 0 e infinitas tareas.
Proceso 3: Entre llegadas 25, tiempo de cálculo 5, retraso de inicio 50 y 5 tareas.
Configuración F:
Proceso 1: Entre llegadas 30, tiempo de cálculo 13, retraso de inicio 0 e infinitas tareas.
Proceso 2: Entre llegadas 20, tiempo de cálculo 10, retraso de inicio 0 e infinitas tareas.
Proceso 3: Entre llegadas 25, tiempo de cálculo 5, retraso de inicio 50 y 5 tareas.
Cuando haya realizado las simulaciones comparativas, intente estas preguntas (en cada caso, justifique su respuesta
basándose en la evidencia de la simulación):
1. ¿Qué algoritmo es el mejor en general para cumplir con los plazos de las tareas?
2. ¿Qué algoritmo es el mejor en general para lograr una alta utilización de la CPU?
3. ¿Los procesos de corta duración (aquellos que tienen solo unas pocas tareas) interfieren o estropean la programación,
¿O los algoritmos se adaptan lo suficiente?
4. ¿Cuál es el mejor algoritmo general para la programación en TIEMPO REAL?
5. ¿Existen circunstancias en las que el peor algoritmo general tenga ventajas?
6. ¿Es posible afirmar que un algoritmo es SIEMPRE mejor que el otro?
2.12 EJERCICIOS DE FIN DE CAPÍTULO 103
Tenga en cuenta que todos los códigos para estos pasos ya existen en el programa del receptor original. Las distintas
secciones de código se pueden copiar en el nuevo programa Sender, con solo pequeños cambios necesarios.
Las principales modificaciones que debe realizar para el nuevo programa Receiver son las siguientes:
Tenga en cuenta que todo el código para estos pasos ya existe en el programa Sender original. Las diversas
secciones de código se pueden copiar en el nuevo programa Receiver, con solo cambios menores necesarios.
En los programas se proporciona una solución de muestra a este problema. IPC_socket_Sender_with_Reply y
IPC_socket_Receiver_with_Reply.
Ejercicio de programación # P2: La IPC multiproceso El programa ejecuta la actividad de recepción como un hilo de
trabajo, mientras que el hilo principal realiza la actividad de envío. Reorganice el código para que las actividades de envío y
recepción se ejecuten en subprocesos de trabajo y, por lo tanto, el subproceso principal quede libre para otras actividades
(que, en una aplicación real, podría ser la lógica empresarial central).
Para lograr esto, deberá refactorizar el código para que la actividad de envío se ejecute como un hilo separado,
usando detach () para que el nuevo hilo se ejecute de forma asincrónica con el hilo principal.
Se proporciona una solución de ejemplo en la aplicación Multithreaded_IPC_two_worker_threads.
Ejercicio de programación # P3: La IPC multiproceso El programa cierra el proceso local cuando se
ingresa el comando "Salir". Modifique el programa para que, además de este apagado local, también se
apague el proceso de comunicación remota.
104 CAPITULO 2 LA VISTA DEL PROCESO
Para lograr esto, deberá enviar el mensaje "Salir" a través de la red al otro proceso y luego realizar la
acción local "salir". También es necesario modificar la lógica de recepción para que el proceso se apague si
se recibe el comando “Quit” desde el socket de comunicación, de la misma manera que en la actualidad
opera si el comando se ve desde la entrada del usuario local.
Se proporciona una solución de ejemplo en la aplicación Multithreaded_IPC_remote_shutdown.
Q1. Respuesta
Suponiendo un sistema de procesador de un solo núcleo, como máximo, un proceso puede estar en estado de ejecución en cualquier momento:
Q2. Respuesta
La secuencia a no es posible porque un obstruido a corriendo no se permite la transición. La
secuencia b es posible.
La secuencia c no es posible porque un proceso en el Listo el estado no puede convertirse obstruido.
La secuencia d no es posible porque un proceso en el Listo el estado no puede convertirse obstruido y porque un obstruido
a corriendo no se permite la transición.
La secuencia e no es posible porque un obstruido a corriendo no se permite la transición.
Q3. Respuesta
Se necesita cuatro veces más tiempo de procesamiento de CPU. Entonces, si la primera tarea recibió el 100% de la CPU, entonces podemos
esperar que las cuatro tareas tomen 400 segundos, ya que cada una obtiene ahora el 25% del recurso de la CPU. Sin embargo, si la tarea original
había recibido una parte del recurso de CPU inferior al 100%, entonces el cálculo es más complejo; por ejemplo, consulte Actividad P4 en la que la
primera tarea obtiene el 50%, pero cuando se ejecutan cuatro, obtienen el 25% cada una. En tal caso, esperaríamos que el tiempo de ejecución
sea de aproximadamente 200 segundos cuando las cuatro tareas se ejecuten a la vez.
Q4. Respuesta
No se garantiza que las tareas que no son en tiempo real se ejecuten en ningún período de tiempo en particular. Por lo
tanto, la variación en los tiempos de ejecución reales es la norma, no una excepción. Las variaciones surgen debido a la
presencia de otros procesos en el sistema, que compiten por el recurso de procesamiento. El planificador toma decisiones a
corto plazo sobre qué proceso ejecutar según el conjunto de procesos presentes y sus estados de proceso individuales. El
estado del sistema en su conjunto está en constante cambio a medida que los procesos llegan, realizan su trabajo y luego se
completan; por lo que es poco probable que se apliquen exactamente las mismas condiciones para más de una breve
secuencia de decisiones de programación consecutivas.
El tiempo mínimo de ejecución se puede lograr para un proceso en particular solo cuando se le otorga acceso exclusivo
a la CPU. En este caso, el tiempo de ejecución de 60 segundos podría ser el tiempo de ejecución mínimo, pero no se
proporciona suficiente información para confirmarlo.
Q5. Respuesta
Parte (a) Se necesita un planificador en tiempo real que tenga en cuenta la periodicidad o los plazos.
Parte (b) Fecha límite de los procesos o fecha límite o periodicidad de las subtareas generadas por el
proceso.
2.12 EJERCICIOS DE FIN DE CAPÍTULO 105