Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Procesos: Justificación Y Resumen

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 85

22 CAPITULO 2 LA VISTA DEL PROCESO

2.10.1 Transparencia de simultaneidad ............................................. ........................................... 94


2.10.2 Transparencia migratoria ............................................. ................................................ 94
2.11 El estudio de caso desde la perspectiva del proceso .......................................... ........................................ 95
2.11.1 Requisitos de programación ............................................. ............................................ 95
2.11.2 Uso de temporizadores ............................................ .................................................. ............ 95
2.11.3 Necesidad de subprocesos ............................................ .................................................. ....... 96
2.11.4 IPC, puertos y enchufes ......................................... .................................................. . 96
2.12 Ejercicios de fin de capítulo ........................................... .................................................. ................... 97
2.12.1 Preguntas .............................................. .................................................. ................ 97
2.12.2 Ejercicios con los bancos de trabajo ........................................... ..................................... 98
2.12.3 Ejercicios de programación ............................................. ............................................ 103
2.12.4 Respuestas a las preguntas al final del capítulo ....................................... ................................ 104
2.12.5 Lista de actividades en el texto ......................................... ................................................. 105
2.12.6 Lista de recursos complementarios ........................................... .................................. 105

2.1 JUSTIFICACIÓN Y RESUMEN


El proceso es el último punto final de la comunicación en sistemas distribuidos. Dentro de un sistema o aplicación en
particular, varios procesos se ejecutan como entidades separadas, programadas por el sistema operativo. Sin embargo,
para que estos procesos actúen de manera cooperativa y coherente para resolver problemas de aplicación, necesitan
comunicarse. Comprender la naturaleza de los procesos y la forma en que interactúan con el sistema operativo es un
prerrequisito clave para diseñar sistemas de procesos que se comunican para construir estructuras de nivel superior y así
resolver problemas a nivel de aplicación de sistemas distribuidos.
La abreviatura IO se utilizará cuando se analicen entradas y salidas en contextos genéricos.

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.

2.2.1 CONCEPTOS BÁSICOS


Primero es necesario introducir los conceptos básicos de procesos y su interacción con los sistemas. Esto prepara el
escenario para una investigación más profunda posterior.
Comenzamos con la definición de un programa.

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.

Consideremos ahora la definición de proceso.

Un proceso es la instancia en ejecución de un programa.


2.2 PROCESOS 23

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.

2.2.2 CREANDO UN PROCESO


El primer paso es escribir un programa que exprese la lógica necesaria para resolver un problema en particular. El
programa se escribirá en un lenguaje de programación de su elección, por ejemplo, C ++ o C #, lo que le permitirá utilizar
una sintaxis que sea adecuadamente expresiva para representar ideas de alto nivel, como leer la entrada de un dispositivo
de teclado, manipular valores de datos y mostrando la salida en una pantalla de visualización.
Luego se usa un compilador para convertir sus instrucciones de alto nivel amigables con los humanos en
instrucciones de bajo nivel que el microprocesador entiende (por lo tanto, la secuencia de instrucciones de bajo
nivel que genera el compilador se llama código de máquina). Las cosas pueden salir mal en este punto de dos
formas principales. Los errores de sintaxis son aquellos errores que rompen las reglas del lenguaje que se utiliza.
Los ejemplos de errores de sintaxis incluyen nombres de variables o palabras clave mal escritos, tipos de
parámetros incorrectos o números incorrectos de parámetros que se pasan a los métodos y muchos otros errores
similares (si ha realizado un poco de programación, probablemente pueda enumerar al menos tres tipos más de
sintaxis error que ha cometido usted mismo). El compilador detecta estos errores automáticamente y se
proporcionan mensajes de error para permitirle localizar y solucionar los problemas. Por lo tanto,

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

Pseudocódigo para un proceso simple.

Comienzo
Obtener información
Hacer un poco de procesamiento
Producir salida
Final

FIGURA 2.2

Pseudocódigo de un proceso simple con entrada y salida.


2.2 PROCESOS 25

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

Leer un número del teclado, almacenarlo en la variable X Leer


un número del teclado, almacenarlo en la variable Y Agregar X {X, Y} {Z}
Teclado Sumador Pantalla de visualización
+ Y y almacenar el resultado en la variable Z
Escriba Z en la pantalla de visualización
Fin

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

Leer un número del flujo de entrada, almacenarlo en la variable X Leer un


{X, Y} {Z} Salida
número del flujo de entrada, almacenarlo en la variable Y Agregar X + Y y Aporte
Sumador
almacenar el resultado en la variable Z Arroyo Arroyo
Escriba Z en el final del flujo de
salida

FIGURA 2.4

El proceso Adder con sus flujos de entrada y salida.

Comienzo

Leer un número del flujo de entrada, almacenarlo en la variable V


Aporte
{V} {W} Salida
Multiplicar V * 2 y almacenar el resultado en la variable W Doblador
Arroyo Arroyo
Escriba W en el flujo de salida
End

FIGURA 2.5

El proceso Doubler; pseudocódigo y representación con flujos de entrada y salida.


26 CAPITULO 2 LA VISTA DEL PROCESO

El concepto de flujos de IO da lugar a la posibilidad de que el IO se pueda conectar a muchas fuentes o


dispositivos diferentes y ni siquiera tenga que involucrar al usuario directamente. En lugar de que la variable
W se muestre en la pantalla, podría usarse como entrada para otro cálculo, en otro proceso. Por tanto, la
salida de un proceso se convierte en la entrada de otro. Esto se denomina canalización (ya que es un
conector entre dos procesos, a través del cual pueden fluir los datos) y es una forma de IPC. Usamos el
símbolo de la tubería | para representar esta conexión entre procesos. La canalización se implementa
conectando el flujo de salida de un proceso al flujo de entrada de otro. Tenga en cuenta que es el sistema
operativo el que realmente realiza esta conexión y no los procesos en sí.
Los procesos Adder y Doubler podrían conectarse de manera que la salida de Adder se convierta en la entrada
de Doubler, conectando los vapores apropiados. En este escenario, el flujo de entrada para el proceso Adder se
asigna al teclado y el flujo de salida para el proceso Doubler se asigna a la pantalla de visualización. Sin embargo, el
flujo de salida para Adder ahora está conectado al flujo de entrada para Doubler, a través de una canalización.
Usando la notación de tubería, podemos escribir Sumador |
Doubler. Esto se ilustra en Figura 2.6 .
Si escribo el número 3 y luego el número 6 (los dos valores de entrada), la salida en la pantalla será
18. La salida del proceso Adder sigue siendo 9, pero esto no está expuesto al mundo exterior
(podemos llamar esto un resultado parcial ya que el cálculo general aún no está completo). Process
Doubler toma el valor 9 del proceso Adder y lo duplica, dando el valor 18, que luego se muestra en la
pantalla.
Hemos llegado a la primera actividad práctica de este capítulo. Cada una de las actividades se presenta en forma de
experimento, con resultados de aprendizaje identificados y un método a seguir.
He adoptado específicamente el enfoque de incluir tareas prácticas cuidadosamente definidas que el lector
puede realizar a medida que lee el texto porque los resultados del aprendizaje se refuerzan mejor al tener la
experiencia práctica. Las tareas prácticas están diseñadas para reforzar los conceptos teóricos introducidos en el
texto principal. Muchas de las tareas prácticas también apoyan y fomentan su propia exploración cambiando los
parámetros o la configuración para hacer preguntas hipotéticas.
La presentación de las actividades también informa los resultados esperados y proporciona una guía para la reflexión y,
en algunos casos, sugerencias para una mayor exploración. Esto es para que los lectores que no puedan ejecutar los
experimentos en el momento de la lectura puedan seguir el texto.
El primero Actividad P1 brinda la oportunidad de investigar IPC utilizando tuberías. El ejemplo en Actividad
P1 es simple, pero sumamente valioso. Demuestra varios conceptos importantes que se tratan en
secciones posteriores:

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

Los procesos Adder y Doubler conectados por una tubería.


2.2 PROCESOS 27

ACTIVIDAD P1 EXPLORANDO PROGRAMAS SIMPLES, CORRIENTES DE ENTRADA Y SALIDA


Y TUBERÍAS
Prerrequisitos
Las instrucciones a continuación asumen que ha realizado previamente la Actividad I1 en el Capítulo 1. Esta actividad coloca
los recursos complementarios necesarios en su unidad de disco predeterminada (generalmente C :) en el directorio SystemsProgramming.
Alternativamente, puede copiar manualmente los recursos y ubicarlos en un lugar conveniente en su computadora y modificar las instrucciones a
continuación de acuerdo con el nombre de ruta de instalación utilizado.

Los resultados del aprendizaje


Esta actividad ilustra varios conceptos importantes que se han discutido hasta este punto:
1. Cómo se crea un proceso como la instancia en ejecución de un programa al ejecutar el programa en la línea de comando
2. Cómo asigna el sistema operativo los dispositivos de entrada y salida al proceso
3. Cómo se pueden encadenar dos procesos mediante una canalización

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

ACTIVIDAD P1 EXPLORACIÓN DE PROGRAMAS SIMPLES, CORRIENTES DE ENTRADA Y


SALIDA Y TUBERÍAS — Cont.

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.

2.3 PROGRAMACIÓN DEL PROCESO


Esta sección examina el papel que juega el sistema operativo en la administración de los recursos del sistema y los procesos
de programación.
Un proceso se "ejecuta" haciendo que sus instrucciones se ejecuten en la unidad central de procesamiento
(CPU). Tradicionalmente, las computadoras de uso general han tenido una sola CPU, que tenía un solo núcleo (el
núcleo es la parte que realmente ejecuta un proceso). Se está volviendo común en los sistemas modernos tener
múltiples CPU y / o que cada CPU tenga múltiples núcleos. Dado que cada núcleo puede ejecutar un solo proceso,
un sistema de múltiples núcleos puede ejecutar varios procesos al mismo tiempo. Con el fin de limitar la
complejidad de la discusión en este libro, asumiremos la arquitectura de un solo núcleo más simple, lo que significa
que solo un único proceso puede ejecutar instrucciones en cualquier momento (es decir, solo un proceso se está
ejecutando a la vez). ).
El papel más fundamental de un sistema operativo es administrar los recursos del sistema. La CPU es el recurso
principal, ya que no se puede realizar ningún trabajo sin ella. Por lo tanto, un aspecto importante de la administración de
recursos es controlar el uso de la CPU (más conocido como programación de procesos). Los sistemas operativos modernos
tienen un componente especial, el programador, que es específicamente responsable de administrar
2.3 PROGRAMACIÓN DEL PROCESO 29

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

ACTIVIDAD P2 EXAMINAR LISTA DE PROCESOS EN LA COMPUTADORA (PARA SISTEMAS


OPERATIVOS WINDOWS)

Prerrequisitos
Requiere un sistema operativo Microsoft Windows.

Los resultados del aprendizaje


1. Comprender el número típico y la variedad de procesos presentes en una computadora típica de uso
general.
2. Adquirir una apreciación de la complejidad de la actividad en un entorno de multiprocesamiento.
3. Obtener una apreciación de la necesidad y la importancia de programar

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.

Resultado esperado para la parte 1


Verá una nueva ventana que contiene una lista de las aplicaciones que se ejecutan en la computadora, o una lista de los procesos
presentes, dependiendo de la pestaña seleccionada. Se muestran ejemplos de mi propia computadora (que tiene el sistema operativo
Windows 7 Professional). La primera captura de pantalla muestra la lista de aplicaciones y la segunda captura de pantalla muestra la lista
de procesos.
2.3 PROGRAMACIÓN DEL PROCESO 31

ACTIVIDAD P2 EXAMINAR LA LISTA DE PROCESOS EN LA COMPUTADORA (PARA SISTEMAS


OPERATIVOS WINDOWS) —Continuado

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

ACTIVIDAD P2 EXAMINAR LA LISTA DE PROCESOS EN LA COMPUTADORA (PARA SISTEMAS


OPERATIVOS WINDOWS) —Continuado

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.

2.3.1 CONCEPTOS DE PROGRAMACIÓN

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).

2.3.1.1 Rebanadas de tiempo y cuantos


Un programador preventivo permitirá que un proceso en particular se ejecute durante un corto período de tiempo llamado cuanto (o
segmento de tiempo). Después de esta cantidad de tiempo, el proceso se vuelve a colocar en la cola lista y otro proceso se coloca en
el estado de ejecución (es decir, el planificador se asegura de que los procesos se turnen para ejecutarse).
El tamaño de un cuanto debe seleccionarse con cuidado. Cada vez que el sistema operativo toma una decisión de programación,
él mismo utiliza el procesador. Esto se debe a que el sistema operativo comprende uno o más procesos y tiene que usar el tiempo de
procesamiento del sistema para hacer sus propios cálculos con el fin de decidir qué proceso ejecutar y moverlos de un estado a otro;
esto se llama cambio de contexto y el tiempo necesario es la sobrecarga de programación. Si los cuantos son demasiado cortos, el
sistema operativo tiene que realizar actividades de programación con más frecuencia y, por lo tanto, los gastos generales son más
altos como proporción del recurso total de procesamiento del sistema.
Por otro lado, si los cuantos son demasiado largos, los otros procesos en la cola de listos deben esperar más
entre turnos, y existe el riesgo de que los usuarios del sistema noten una falta de capacidad de respuesta en las
aplicaciones a las que pertenecen estos procesos. .
Figura 2.9 ilustra los efectos del tamaño de los cuantos en los gastos generales de programación totales. La
parte (a) de la figura muestra la situación que surge con cuantos muy cortos. El entrelazado de procesos es muy
fino, lo que es bueno para la capacidad de respuesta. Esto significa que la cantidad de tiempo que debe esperar un
proceso antes de su siguiente cuantos de tiempo de ejecución es breve y, por lo tanto, el proceso parece estar
ejecutándose continuamente para un observador, como un usuario humano, que opera en una línea de tiempo
mucho más lenta. Para comprender este aspecto, considere, como analogía, cómo funciona una película. Se
muestran muchos fotogramas por segundo (normalmente 24 o más) ante el ojo humano, pero el sistema visual
humano (el ojo y su interfaz cerebral) no puede diferenciar entre los fotogramas separados a esta velocidad y, por lo
tanto, interpretar la serie de fotogramas como un movimiento continuo. imagen.

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

Clave: = gastos generales de programación

FIGURA 2.9

Tamaño cuántico y gastos generales de programación.


34 CAPITULO 2 LA VISTA DEL PROCESO

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.

ACTIVIDAD P3 EXAMEN PROGRAMACIÓN DEL COMPORTAMIENTO CON


PROCESOS REALES — INTRODUCTORIO

Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido los recursos complementarios necesarios como se explica en Actividad P1 .

Los resultados del aprendizaje


Esta actividad explora la forma en que el planificador facilita la ejecución de varios procesos al mismo tiempo:
1. Comprender que muchos procesos pueden estar activos en un sistema al mismo tiempo.
2. Comprender cómo el programador crea la ilusión de que en realidad se están ejecutando varios procesos al mismo tiempo,
intercalandolos en una pequeña escala de tiempo.
3. Adquirir experiencia en argumentos de línea de comandos.
4. Adquirir experiencia en el uso de archivos por lotes para ejecutar aplicaciones.

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

ACTIVIDAD P3 EXAMEN PROGRAMAR EL COMPORTAMIENTO CON


PROCESOS REALES — INTRODUCTORIO — Cont.

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.

Resultado esperado para la parte 1


La siguiente captura de pantalla muestra la salida de un par de configuraciones de parámetros diferentes. Hasta ahora, solo hemos ejecutado un proceso a la vez.

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.

Resultado esperado para la parte 2


Debería ver los tres procesos ejecutándose simultáneamente, evidenciado por el hecho de que sus salidas están intercaladas de modo que vea un
patrón "ABCABC" (el intercalado perfecto no está garantizado y, por lo tanto, el patrón puede variar). La captura de pantalla a continuación muestra el
resultado típico que debería obtener.
36 CAPITULO 2 LA VISTA DEL PROCESO

ACTIVIDAD P3 EXAMEN PROGRAMAR EL COMPORTAMIENTO CON


PROCESOS REALES — INTRODUCTORIO — Cont.

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

de la CPU ha aumentado con el tiempo, el tamaño cuántico de programación típico no lo ha hecho.

2.3.1.2 Estados del proceso


Esta sección se relaciona principalmente con los esquemas de programación preventiva, ya que estos se utilizan en los sistemas
típicos que se utilizan para construir sistemas distribuidos.
Si asumimos que solo hay una unidad de procesador, solo un proceso puede usar el procesador en cualquier momento.
Se dice que este proceso es corriendo, o en el estado de ejecución. Es el trabajo del planificador seleccionar qué proceso se corriendo
en un momento determinado y, por implicación, qué hacer con los demás procesos del sistema.
Algunos procesos que no están actualmente corriendo podrá correr; esto significa que todos los recursos que
necesitan están disponibles para ellos, y si fueron colocados en el estado de ejecución, podrían realizar un trabajo
útil. Cuando un proceso puede ejecutarse, pero en realidad no se ejecuta, se coloca en la cola lista. Por tanto, se dice
que es Listo, o en el estado listo.
Algunos procesos no pueden ejecutarse inmediatamente porque dependen de algún recurso que no está
disponible para ellos. Por ejemplo, considere una aplicación de calculadora simple que requiere la entrada del
usuario antes de poder calcular un resultado. Suponga que el usuario ha introducido la secuencia de teclas 5 x;
Claramente, esta es una instrucción incompleta y la aplicación de la calculadora debe esperar más entradas antes
de poder calcular 5 veces el segundo número que ingrese el usuario. En este escenario, el recurso en cuestión es el
dispositivo de entrada del teclado y el programador será consciente de que el proceso no puede continuar porque
está esperando una entrada. Si el programador dejara ejecutar este proceso, no podría realizar un trabajo útil de
inmediato; por este motivo, el proceso se traslada a la obstruido Expresar. Una vez que se haya aclarado el motivo
de la espera (p. Ej., El dispositivo IO ha respondido), el proceso se moverá al Listo cola. En el ejemplo descrito
anteriormente, esto sería cuando el usuario ha escrito un número en el teclado.
Tomando el programa Adder utilizado en Actividad P1 como ejemplo; considere el punto en el que el proceso espera la entrada
de cada usuario. Incluso si el usuario escribe inmediatamente, digamos, quizás un número por segundo, se necesitan 2 segundos
para ingresar los dos números; y esto deja un margen mínimo para el tiempo de pensamiento del usuario, que tiende a ser
relativamente masivo en comparación con el tiempo computacional necesario para procesar los datos ingresados.
La CPU funciona muy rápido en relación con la velocidad de escritura humana; incluso el microcontrolador más lento utilizado en
sistemas embebidos ejecuta 1 millón o más de instrucciones por segundo. 2 La tecnología de microprocesador de teléfonos móviles de
gama alta (en el momento de escribir este artículo) contiene cuatro núcleos de procesamiento, cada uno de los cuales funciona a 2,3
GHz, mientras que un microprocesador de PC de escritorio de gama alta funciona a 3,6-4,0 GHz, también con cuatro núcleos de
procesamiento. Estas cifras proporcionan una indicación de la cantidad de potencia computacional que se perdería si el programador
permitiera que la CPU permaneciera inactiva durante un par de segundos mientras un proceso como Adder está esperando una
entrada. Como se mencionó anteriormente, los cuantos de programación suelen estar en el rango de 10 a 200 ms, lo que, dada la
velocidad de funcionamiento de las CPU modernas, representa una gran cantidad de potencia computacional.
Los tres estados del proceso discutidos anteriormente forman el conjunto básico requerido para describir el comportamiento general de un

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,

Los procesos parten


El proceso está usando la CPU
desde el estado Ejecutar
Clave: Expresar
cuando haya terminado

Transición TERMINAR
Correr

El proceso realiza entrada o salida. El


Sistema operativo
dispositivo IO es más lento que la CPU,
elige el proceso por lo que el proceso se bloquea para
para la ejecución
permitir que otro proceso use la CPU y
(envío)
Preventos de O / S

Recién creado el proceso así que mantenlo ocupado

cuando sea hora


los procesos son
inicializado en quántum expira
el estado Listo El proceso espera en
este estado hasta
COMIENZO Listo Obstruido
el dispositivo IO
Ocurrió un evento IO. El proceso ahora
Colas de proceso en está listo para ejecutarse de nuevo,
ha respondido
este estado con otros pero debe unirse a la cola con otros
procesos listos procesos listos

FIGURA 2.10

El diagrama de transición de estados de tres estados.


2.3 PROGRAMACIÓN DEL PROCESO 39

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

ilustra en Figura 2.11 .

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

cuando el usuario está escribiendo y la oración clave actual está incompleta.

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

R Clave: R Estado de funcionamiento

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

Ilustración de comportamiento en tiempo de ejecución para un proceso intensivo de E / S.

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 .

2.3.1.3 Comportamiento del proceso


Procesos intensivos en IO
Algunos procesos realizan IO con frecuencia y, por lo tanto, a menudo se bloquean antes de agotar su cuanto; se dice que
son procesos intensivos en IO. Un programa intensivo en IO no solo podría ser un programa interactivo en el que el IO
ocurre entre un usuario y el proceso (a través de, por ejemplo, el teclado y la pantalla de visualización)
2.3 PROGRAMACIÓN DEL PROCESO 41

Porción de tiempo

R D R D R ...

Hora

Clave: R Estado de funcionamiento

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

Clave: R Estado de ejecución


R
D estado de lectura
B B Estado bloqueado
Proporción indicativa
del tiempo empleado en cada

uno de los tres estados


D
principales del proceso

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.

ACTIVIDAD P4 EXAMEN PROGRAMACIÓN DEL COMPORTAMIENTO CON PROCESOS REALES:


COMPETENCIA PARA LA CPU

Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido los recursos complementarios necesarios como se explica en Actividad P1 .

Los resultados del aprendizaje


Esta actividad explora la forma en que el programador comparte el recurso de la CPU cuando varios procesos en competencia consumen mucha
CPU:
42 CAPITULO 2 LA VISTA DEL PROCESO

ACTIVIDAD P4 EXAMINANDO PROGRAMACIÓN DEL COMPORTAMIENTO CON PROCESOS


REALES - COMPETENCIA PARA LA CPU - Cont.

1. Entender que el recurso de la CPU es finito


2. Comprender que el desempeño de un proceso puede verse afectado por los otros procesos del sistema (que desde su punto de vista
constituyen una carga de trabajo de fondo con la que debe competir por los recursos).

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

ACTIVIDAD P4 EXAMINANDO PROGRAMACIÓN DEL COMPORTAMIENTO CON PROCESOS


REALES - COMPETENCIA PARA LA CPU - Cont.

Resultado esperado para la parte 2


En mi experimento, los procesos se ejecutaron durante aproximadamente 40 segundos, que es lo que esperaba, sobre la base de que cada proceso
tenía muy poco menos del 50% (en promedio) del recurso de la CPU, que era esencialmente el mismo que tenía el proceso único. tenía en la parte 1. Es
importante el hecho de que los procesos tardaron un poco más en ejecutarse de lo que había tardado el proceso único. Esta ligera diferencia surge
porque los otros procesos en el sistema, aunque relativamente inactivos, todavía usan algún recurso de procesamiento y, lo que es más importante, la
actividad de programación en sí incurre en gastos generales cada vez que cambia entre los procesos activos. Estos gastos generales se absorbieron
cuando la CPU estuvo inactiva durante casi el 50% del tiempo, pero aparecen cuando el recurso se utiliza por completo.

Parte 3 (Hacer hincapié en el sistema)


No satisfaría mi naturaleza inquisitiva dejarlo allí. Hay una pregunta obvia que debemos hacer y tratar de responder: Si una
sola instancia de CPU_Hog toma el 50% del recurso de la CPU cuando se ejecuta y dos copias de CPU_Hog toman el 50% del
recurso de la CPU cada una, ¿qué sucede cuando se ejecutan tres o incluso más copias al mismo tiempo?
1. Haga sus predicciones: para cada uno de los tres procesos, ¿cuál será la parte del recurso de la CPU? ¿Y cuál será el tiempo de ejecución?

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

obtener una indicación de lo que está sucediendo).

La siguiente captura de pantalla muestra los resultados típicos que obtengo.

Resultado esperado para la parte 3


Curiosamente, el primer proceso todavía obtiene el 50% del recurso de la CPU y los otros dos comparten el 50% restante.
Antes del experimento, esperaba que cada uno obtuviera el 33% del recurso. Es casi seguro que haya otros programadores
por ahí para lo que ese hubiera sido el caso; pero con los sistemas distribuidos, no siempre sabrá o controlará en qué computadora se ejecutará su
proceso, o qué programador estará presente, o la configuración exacta del tiempo de ejecución, incluida la carga de trabajo, por lo que este
experimento nos enseña a ser cautelosos al predecir la ejecución. rendimiento temporal incluso en un solo sistema, y mucho menos en sistemas
distribuidos heterogéneos.
44 CAPITULO 2 LA VISTA DEL PROCESO

ACTIVIDAD P4 EXAMINANDO PROGRAMACIÓN DEL COMPORTAMIENTO CON PROCESOS


REALES - COMPETENCIA PARA LA CPU - Cont.

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.

Parte 4 (Explorando más)


He proporcionado un archivo por lotes adicional "CPU_Hog_4Starter.bat" para iniciar cuatro copias de CPU_Hog al mismo tiempo. Puede usar esto
con el mismo método que en la parte tres para explorar más.
La siguiente captura de pantalla muestra el comportamiento en mi computadora.

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

consistente con los resultados anteriores.

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

La Verano Programa intensivo de IO (interactivo) (calcula la suma de 100 números).

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 {

Leer el siguiente bloque del archivo A


Escribir el bloque en el archivo B
}
Final

FIGURA 2.15

La Copia de archivo Programa intensivo de IO (acceso al disco) (copia de un archivo a otro).

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

NetworkPingService Programa de IO intensivo (comunicación de red) (enviar un mensaje recibido al


remitente).

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.

Procesos intensivos en computación (o intensivos en CPU)


Los procesos de computación intensiva son aquellos que rara vez realizan E / S, tal vez para leer algunos datos iniciales de
un archivo, por ejemplo, y luego pasan largos períodos procesando los datos antes de producir el resultado al final, lo que
requiere una salida mínima. La mayoría de la informática científica que utiliza técnicas como la dinámica de fluidos
computacional (CFD) y la programación genética en aplicaciones como la predicción meteorológica y las simulaciones por
ordenador se incluyen en esta categoría. Dichas aplicaciones pueden ejecutarse durante períodos prolongados, quizás
muchas horas sin realizar necesariamente ninguna actividad de IO. Por lo tanto, casi siempre usan todos los intervalos de
tiempo asignados y rara vez se bloquean.
Figura 2.17 muestra un pseudocódigo para un programa de cálculo intensivo en el que la E / S se realiza solo al
principio y al final. Una vez que se ha realizado la E / S inicial, se esperaría que este proceso use sus cuantos
completamente y sea reemplazado por el sistema operativo, moviéndose predominantemente entre los estados en
ejecución y listo.

Comienzo
Cargar un conjunto de datos masivo desde archivos (IO)

Realizar cálculos CFD (que implica millones de iteraciones de


cálculo, cada una de las cuales actualiza posiblemente una gran
parte del conjunto de datos)

Producir salida (IO)


Final

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.

2.3.1.4 Comportamiento, componentes y mecanismos del programador


El acto de mover un proceso del estado listo al estado en ejecución se denomina "despacho" y lo realiza un
subcomponente del planificador llamado despachador. El despachador debe asegurarse de que se lleve a cabo la
preparación adecuada para que el proceso pueda funcionar correctamente. Esto implica restaurar el estado del
proceso (por ejemplo, la pila y también el contador del programa y otras estructuras del sistema operativo) para
2.3 PROGRAMACIÓN DEL PROCESO 47

Comienzo
Cargar archivo (disk-IO)
Repita hasta completar la edición {

Espere la pulsación de la tecla del usuario (IO interactivo)


Ejecutar automáticamente autocompletar funcionan como
necesario (computación intensa)
Ejecutar automáticamente corrector ortográfico funcionan como
necesario (computación intensa)
Ejecutar automáticamente corrector gramatical funcionan como
necesario (computación intensa)
}
Guardar archivo (disk-IO)
Final

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.

2.3.1.5 Estados de proceso adicionales: suspendido-bloqueado y suspendido-listo


El modelo de comportamiento del proceso de tres estados discutido anteriormente es una simplificación del
comportamiento real en la mayoría de los sistemas; sin embargo, es muy útil como base para explicar los principales
conceptos de programación. Hay algunos otros estados del proceso que son necesarios para permitir un comportamiento
más sofisticado del planificador. El conjunto de estados de ejecución, listo y bloqueado son suficientes para administrar la
programación desde el ángulo de elegir qué proceso debe ejecutarse (es decir, administrar el recurso de la CPU), pero no
brindan flexibilidad con respecto a otros recursos como la memoria. En el modelo de tres estados, cada proceso, una vez
creado, se mantiene en una imagen de memoria que incluye las instrucciones del programa y todo el almacenamiento
requerido, como las variables utilizadas para contener la entrada y los datos calculados. También hay información adicional
de “estado” creada por el sistema operativo para administrar la ejecución del proceso; esto incluye, por ejemplo, detalles de
qué recursos y periféricos se están utilizando y detalles de la comunicación con otros procesos, ya sea a través de tuberías
como hemos visto anteriormente o mediante protocolos de red, como se examinará en el Capítulo 3.
48 CAPITULO 2 LA VISTA 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.

2.3.1.6 Objetivos de los algoritmos de programación


Como se mencionó anteriormente, la eficiencia en términos de mantener la CPU ocupada y, por lo tanto, garantizar que el sistema
realice un trabajo útil es un objetivo principal de la programación de propósito general. Los objetivos adicionales de la programación
incluyen los siguientes:
2.3 PROGRAMACIÓN DEL PROCESO 49

El proceso está usando la CPU

Clave: Expresar

TERMINAR
Transición
Correr

Sistema operativo El proceso realiza entrada o salida. El


elige el proceso dispositivo IO es más lento que la CPU,
para la ejecución por lo que el proceso se bloquea para
(envío) permitir que otro proceso use la CPU y
La operacion
Colas de proceso el sistema se adelanta
así que mantenlo ocupado
en este estado con el proceso cuando
otros Ready es el tiempo cuántico
procesos expira
El proceso espera en
este estado hasta
COMIENZO Listo Obstruido el dispositivo IO
El proceso ahora está listo para
ha respondido
ejecutarse nuevamente, pero debe unirse

a la cola con otros procesos listos

Activar proceso Suspender proceso Activar proceso Suspender proceso


(mover su 'estado' de (mover su "estado" de (mover su "estado" de (mover su "estado" de
disco a memoria) memoria a disco) disco a memoria) memoria a disco)

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

ya no está bloqueado, pero


el bloqueo IO
en la memoria)
evento ocurre
permanece suspendido

FIGURA 2.19

El modelo de transición de estado de proceso extendido que incluye estados de proceso suspendidos.

• Garantizar la equidad en todos los procesos.

Cuando se comparan algoritmos de programación, el concepto de "equidad" suele estar muy


cerca de la eficiencia en términos de métricas de rendimiento. Esto es en el contexto de dar a los
diversos procesos una “parte justa” del recurso de procesamiento. Sin embargo, como verá más
adelante cuando realice experimentos con las simulaciones en el Operating Systems Workbench,
el concepto de equidad es subjetivo y difícil de precisar en un significado universal único. Si
todos los procesos tienen la misma prioridad e importancia, entonces es fácil considerar la
equidad en términos de brindar a los procesos las mismas oportunidades de procesamiento. Sin
embargo, las cosas se vuelven mucho más complejas cuando hay diferentes prioridades o
cuando algunos o todos los procesos tienen restricciones en tiempo real.

• 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

• Para minimizar el tiempo de espera 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.

2.3.1.7 Algoritmos de programación


Primero llegado, primero servido (FCFS)
FCFS es el algoritmo de programación más simple. Hay una sola regla; programe la llegada del primer proceso y
deje que se complete. Este es un algoritmo de programación no preventivo, lo que significa que solo se puede
ejecutar un proceso a la vez, independientemente de si utiliza los recursos del sistema de manera efectiva, y
también independientemente de si hay una cola de otros procesos esperando, y el importancia relativa de esos
procesos. Debido a estas limitaciones, el algoritmo no se usa ampliamente, pero se incluye aquí como una línea de
base sobre la cual comparar otros algoritmos simples (ver Actividad P5 ).

El trabajo más corto primero (SJF)


SJF es una modificación de FCFS, en la que a continuación se ejecuta el trabajo más corto de la cola. Este algoritmo tiene el
efecto de reducir los tiempos de espera promedio, ya que los trabajos más cortos se ejecutan primero y los trabajos más
largos pasan menos tiempo esperando para comenzar que si fuera al revés. Esta es una mejora significativa, pero este
algoritmo tampoco es preventivo y, por lo tanto, sufre las mismas debilidades generales con respecto a la eficiencia en el
uso de recursos que FCFS.

Ronda Robin (RR)


El algoritmo de programación preventiva más simple es el round-robin, en el que los procesos reciben turnos durante la
ejecución, uno tras otro en una secuencia repetida, y cada uno se reemplaza cuando ha agotado su intervalo de tiempo.
Entonces, por ejemplo, si tenemos tres procesos {A, B, C}, entonces el programador puede ejecutarlos en la secuencia A, B,
C, A, B, C, A, y así sucesivamente, hasta que estén todos terminados. . Figura 2.20 muestra una posible secuencia de estado
del proceso para este conjunto de procesos, asumiendo un único sistema central de procesamiento. Observe que el ejemplo
que se muestra es de máxima eficiencia porque el procesador está continuamente ocupado; siempre hay un proceso en
ejecución.
2.3 PROGRAMACIÓN DEL PROCESO 51

Proceso Cuántico 1 Cuántico 2 Cuántico 3 Cuántico 4 Quantum 5 Cuántico 6


A Corriendo Listo Listo Corriendo Listo Listo
B Listo Corriendo Listo Listo Corriendo Listo
C Listo Listo Corriendo Listo Listo Corriendo

FIGURA 2.20

Una posible secuencia de estado del proceso para la programación por turnos con tres procesos.

ACTIVIDAD P5 UTILIZAR EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR EL COMPORTAMIENTO DE


PROGRAMACIÓN (INTRODUCTORIO): COMPARACIÓN DE LOS PRIMEROS QUE LLEGAN, LOS PRIMEROS SERVICIOS, EL
TRABAJO MÁS CORTO PRIMERO Y LOS PROGRAMACIONES DE HORARIO REDONDO

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".

Los resultados del aprendizaje


1. Comprender el algoritmo de programación por orden de llegada.
2. Comprender el algoritmo de programación del primer trabajo más corto
3. Comprender el algoritmo de programación por turnos.
4. Comprender el modelo de tres estados del comportamiento del proceso.
5. Para reforzar la comprensión de los conceptos generales de programación, que se analizan en este capítulo.
Esta actividad utiliza la simulación “Programación de algoritmos — Introducción”, que se encuentra en la pestaña “Programación” del entorno de trabajo
de sistemas operativos. Usamos la misma configuración inicial de dos procesos intensivos en CPU en cada una de las tres etapas siguientes.
Comparamos el comportamiento utilizando cada uno de 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.
Esta actividad se divide en tres etapas para proporcionar estructura.

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.

Habilite el proceso 2, configúrelo en "CPU intensivo" y establezca el tiempo de ejecución en


30 ms. Cuando esté listo, presione el botón "Free Run" para iniciar la simulació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

programación FCFS.
52 CAPITULO 2 LA VISTA DEL PROCESO

ACTIVIDAD P5 UTILIZAR EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR EL COMPORTAMIENTO DE


PROGRAMACIÓN (INTRODUCTORIO): COMPARACIÓN DE LOS PRIMEROS QUE LLEGAN, LOS PRIMEROS SERVICIOS, EL
TRABAJO MÁS CORTO PRIMERO Y LA PROGRAMACIÓN DE RUTAS REDONDAS — 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. 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.

Trabajo restante más corto siguiente (SRJN)


SRJN es un algoritmo de programación preventiva que combina los beneficios de SJF como se discutió anteriormente (en el que el trabajo más

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

Colas de retroalimentación multinivel (MLFQ)


Este enfoque se basa en la flexibilidad introducida con las colas de varios niveles. Aquí, los procesos se pueden mover entre
las colas para permitir que la programación logre un equilibrio entre la programación a corto plazo para lograr la capacidad
de respuesta de las tareas y la programación a más largo plazo para garantizar la equidad y la eficiencia del sistema.
Las colas se organizan en términos de prioridad, siendo la cola superior la que tiene la prioridad más alta y, por lo tanto,
se programa primero. Dentro de cada cola, se utiliza la programación FIFO. Un nuevo proceso entra en este
54 CAPITULO 2 LA VISTA DEL PROCESO

Varias colas listas

Procesos del sistema


(más alta prioridad)

Procesos interactivos UPC

Procesos informáticos intensivos


(prioridad más baja)

FIGURA 2.21

Programación de colas multinivel.

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í.

El algoritmo de cola de retroalimentación multinivel se ilustra en Figura 2.22 .


Figura 2.23 muestra el algoritmo MLFQ en funcionamiento en un sistema con una combinación de procesos interactivos y de
procesamiento intensivo. Inicialmente, dos procesos están en la cola de mayor prioridad (instantánea 1). P1 es un proceso interactivo
de larga duración, por lo que tenderá a permanecer en el nivel superior. Sin embargo, P2 requiere un uso intensivo de la computación
y se reemplaza, por lo que cae al nivel de prioridad 2, mientras que un nuevo proceso P3 se une al sistema (instantánea 2). P3 es una
tarea interactiva de corta duración y, por lo tanto, permanece en el nivel de prioridad más alta, mientras que P2 desciende más
(instantánea 3). P4, que es un proceso de cómputo intensivo de corta duración, se une (instantánea 4), mientras que P2 desciende al
nivel de cola más bajo. P4 desciende un nivel en la instantánea 5. P4 y P3 se completan en las instantáneas 6 y 7, respectivamente.
2.3 PROGRAMACIÓN DEL PROCESO 55

Nivel de cola

3 (máxima prioridad)

Nuevos procesos Enviado Corre por uno


cuántico

2
Enviado Corre por uno
cuántico

1
Enviado Corre por uno
cuántico

Clave

El proceso fue interrumpido (se degrada) El 0


proceso se bloqueó (se promovió) Llega un
Enviado Corre por uno
cuántico
nuevo proceso

FIGURA 2.22

El algoritmo de programación MLFQ.

Nivel de cola Instantáneas secuenciales de los procesos constituyentes de la cola

1 2 3 4 5 6 7

3 P1, P2 P3 (nuevo), P1 P1, P3 P4 (nuevo), P3, P1 P1, P3 P3, P1 X ( P3 completa)


P1, P3
(más alta prioridad)

2 P2 P4 X ( P4 completa)
P4

1 P2

0 P2 P2 P2 P2
(prioridad más baja)

Clave para las características del proceso


P1 Proceso interactivo de larga duración
P2 Proceso intensivo de cómputo de ejecución
prolongada P3 Proceso interactivo de ejecución corta
P4 Proceso de cómputo intensivo de ejecución corta

FIGURA 2.23

Ejemplo de MLFQ con dos procesos interactivos y dos procesos de cálculo intensivo.

ACTIVIDAD P6 UTILIZAR EL 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

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.

Los resultados del aprendizaje


1. Para comprender el algoritmo de programación del siguiente trabajo restante más corto
2. Para comparar el algoritmo de programación del siguiente trabajo restante más corto con los algoritmos de programación del trabajo más corto primero y por

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.

Habilite el proceso 2, configúrelo en "CPU intensivo". y establezca el tiempo de ejecución en 50 ms.

Habilite el proceso 3, configúrelo en "CPU intensivo" y establezca el tiempo de ejecución en 40 ms.

Habilite el proceso 4, configúrelo en "CPU intensivo" y establezca el tiempo de ejecución en 30 ms.

Habilite el proceso 5, configúrelo en “CPU intensivo” y establezca el tiempo de ejecución en 20 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

programación por turnos.


2.3 PROGRAMACIÓN DEL PROCESO 57

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

2.4 PROGRAMACIÓN DE SISTEMAS EN TIEMPO REAL


La programación en tiempo real es el término utilizado para describir los algoritmos de programación que basan sus decisiones de
programación principalmente en los plazos de las tareas, o la periodicidad de las tareas, siendo las tareas actividades individuales
producidas por procesos en el sistema.
Ejemplos de aplicaciones en tiempo real incluyen transmisión de audio y video, monitoreo y control (p. Ej.,
Sistemas fly-by-wire, automatización de fábricas y robótica), así como sistemas comerciales como el comercio de
acciones, que son muy sensibles a los retrasos.
Los procesos en tiempo real a menudo tienen un comportamiento periódico, en el que deben atender eventos a una
velocidad determinada; por ejemplo, una aplicación de transmisión de video que transmite cuadros de video a una
velocidad de 24 cuadros por segundo debe (como sugiere la descripción) producir 24 cuadros de video por segundo. De
hecho, la restricción es aún más estricta; no es adecuado solo requerir 24 fotogramas por segundo porque eso podría
conducir a agrupamientos y luego huecos; esta variación en el tiempo se conoce como jitter y tiene un impacto serio en la
calidad del flujo de video. Lo que realmente se requiere es una distribución uniforme de los fotogramas en el tiempo, por lo
que, en realidad, existe el requisito de que los fotogramas estén espaciados 1/24 de segundo. En un sistema de este tipo, el
programador debe conocer este requisito, de modo que el proceso de transmisión de video tenga suficiente tiempo de
procesamiento para producir los cuadros.
Las limitaciones de tiempo de las tareas en tiempo real requieren un diseño cuidadoso de todo el sistema. En el ejemplo de video anterior,

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,

2.4.1 LIMITACIONES DE LOS PROGRAMADORES DE PROPÓSITO GENERAL PARA SISTEMAS EN


TIEMPO REAL

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

ACTIVIDAD P7 EXAMEN PROGRAMACIÓN DEL COMPORTAMIENTO CON PROCESOS REALES:


CONSIDERACIONES EN TIEMPO REAL

Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido los recursos complementarios necesarios como se explica en Actividad P1 .

Los resultados del aprendizaje


Esta actividad explora la naturaleza del comportamiento en tiempo real y las limitaciones de los programadores de propósito general en términos de

garantizar que se cumplan los plazos:

1. Comprender que las tareas en tiempo real tienen limitaciones de tiempo.


2. Comprender que los programadores de propósito general no satisfacen los requisitos de tiempo de las tareas en tiempo real.
3. Comprender cómo el comportamiento de las tareas en tiempo real puede verse afectado por las cargas de trabajo en segundo plano.

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

ACTIVIDAD P7 EXAMEN PROGRAMACIÓN DEL COMPORTAMIENTO CON PROCESOS REALES —


CONSIDERACIONES EN TIEMPO REAL — Cont.

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

Mando Mando Tarea Piloto recibe comentarios


aporte actuado plazo realimentación plazo

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

Límite de tiempo de respuesta del sistema físico para un funcionamiento seguro

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

aporte FALTA FECHA LÍMITE DEMASIADO TARDE


Tarea
Plazo
T Respuesta
Hora
T R es pon se

El tiempo requerido para el físico


Comando adicional
sistema para responder al
entrada porque el piloto no
comando y para que el piloto
ha visto una respuesta -
recibir comentarios
desestabiliza el sistema

Límite de tiempo de respuesta del sistema físico para un funcionamiento seguro

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

Fórmulas de utilización de CPU para tareas periódicas.

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

2.4.1.1 El algoritmo de programación de fechas límite


La programación de fechas límite ordena las tareas según sus fechas límite, y la tarea con la fecha límite más temprana se ejecuta
primero. Esto tiene el efecto de que la fecha límite de la tarea es su prioridad. El enfoque de programación de fechas límite no presta
atención al intervalo entre las tareas que un proceso genera periódicamente. Por lo tanto, si bien la programación de fechas límite es
muy buena para cumplir con los plazos (porque este es su único criterio al elegir tareas), no es tan buena para preservar un espaciado
regular de tareas, lo cual es necesario en, por ejemplo, aplicaciones audiovisuales donde el espaciado entre cuadros es un aspecto de
QoS o, por ejemplo, en una aplicación de control o monitoreo de alta precisión donde las entradas de muestreo y / o control deben
estar espaciadas uniformemente en la dimensión de tiempo.

2.4.1.2 El algoritmo de programación monótono de velocidad


La programación monótona de tarifas asigna un valor de prioridad a cada proceso, en función del período de las tareas que
genera, teniendo el proceso con el período entre tareas más corto la prioridad más alta. Este énfasis en el período, más que
en la fecha límite, conduce a comportamientos claramente diferentes de los de la programación de fechas límite en algunas
circunstancias. Este enfoque es mucho más adecuado para sistemas que tienen flujos repetitivos de tareas generadas por
procesos a intervalos regulares, donde el intervalo en sí es un aspecto importante del rendimiento del sistema. Sin
embargo, al enfatizar el valor de preservar el intervalo, este algoritmo no es tan bueno como la programación de fechas
límite para cumplir con las fechas límite en algunas situaciones.
Actividad P8 investiga el comportamiento de la fecha límite y evalúa los algoritmos monótonos de programación en tiempo real utilizando

una variedad de diferentes combinaciones de tareas en tiempo real.


2.4 PROGRAMACIÓN DE SISTEMAS EN TIEMPO REAL sesenta y cinco

Cuadro 2.1 Ejemplos de utilización de CPU

Programa ilustrativo de tareas en un intervalo de 100 ms


Período entre Utilización de CPU de cálculo que muestra las tareas (etiquetadas) y los períodos en los que el
tareas (ms) tiempo (ms) (%) La CPU no se usa

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

= PAG 1 T 1 PAG 2 TPAG


1 1 T 2 PAG 2 T
PAG
1 1 T 3 PAG 2 T
PAG
2 1 T 4 PAG 2 T
PAG
2 1 T5

20 y 100 (dos 10 y 20 50 + 20 = 70 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 1 T 1 PAG 2 TPAG


1 1 T 2 PAG 2 T
PAG
1 1 T3 PAG 1 T 4 PAG 1 T 5

ACTIVIDAD P8 UTILIZAR EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR LA PROGRAMACIÓN


DE SISTEMAS EN TIEMPO REAL (INTRODUCTORIO): COMPARACIÓN DE ALGORITMOS DE PROGRAMACIÓN
MONOTÓNICA DE FECHA LÍMITE Y TASA

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".

Los resultados del aprendizaje


1. Comprender la naturaleza de los procesos en tiempo real, especialmente con respecto a los plazos.
2. Apreciar las diferencias entre los programadores en tiempo real y los programadores de propósito general.
3. Comprender el algoritmo de programación de tarifas monótonas
4. Comprender el algoritmo de programación de fechas límite.
Método
Esta actividad utiliza la simulación "Programación en tiempo real: Introducción", que se encuentra en la pestaña "Programación" del entorno de trabajo
de sistemas operativos. La simulación admite hasta dos procesos en tiempo real con tareas periódicas. Para cada proceso, se puede configurar el
período entre tareas y el requisito de tiempo de CPU por tarea. Se proporcionan dos algoritmos de programación en tiempo real: fecha límite y tarifa
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 móvil 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) y un historial reciente de las tareas programadas (a la izquierda):
66 CAPITULO 2 LA VISTA DEL PROCESO

ACTIVIDAD P8 USANDO EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR LA PROGRAMACIÓN DE


SISTEMAS EN TIEMPO REAL (INTRODUCTORIO): COMPARACIÓN DE ALGORITMOS DE PROGRAMACIÓN
MONOTÓNICA DE FECHA LÍMITE Y TASA — Cont.

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:

• para el proceso 1, período = 30 ms, tiempo de cálculo = 16 ms,


• para el proceso 2, período = 20 ms, tiempo de cálculo = 10 ms.
¿Qué sucede con la utilización de la CPU, el tiempo de las tareas de los procesos y el tiempo de inactividad de la CPU en estas condiciones? Tenga en cuenta que las

barras rojas indican que la tarea está vencida.

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

ACTIVIDAD P8 USANDO EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR LA PROGRAMACIÓN DE


SISTEMAS EN TIEMPO REAL (INTRODUCTORIO): COMPARACIÓN DE ALGORITMOS DE PROGRAMACIÓN
MONOTÓNICA DE FECHA LÍMITE Y TASA — Cont.

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?

2. ¿Cuál es mejor para evitar incumplimientos de plazos?


3. ¿Qué algoritmo parece proporcionar un horario más regular (es decir, con períodos regulares entre tareas)?
4. ¿Un nivel de utilización de CPU inferior a 1.0 garantiza que nunca se pierdan los plazos?
Tenga en cuenta que tan pronto como expira la fecha límite de un proceso, el programador calcula la próxima fecha límite para el mismo proceso; esto

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.

Presentamos cargas de trabajo variables y en ráfagas


En cualquier sistema abierto, la carga de trabajo en segundo plano puede ser variable. Esta carga de trabajo puede afectar la
efectividad del programador, especialmente si la utilización de la CPU es alta y todas las tareas presentes tienen restricciones en
tiempo real. También es importante darse cuenta de que desde el punto de vista de cualquier proceso específico p, todos los demás
procesos constituyen la carga de trabajo de fondo con la que compite por los recursos y, por lo tanto, desde el punto de vista de esos
otros procesos, p en sí mismo es parte del proceso. carga de trabajo de fondo.
Actividad P9 brinda la oportunidad de investigar el impacto de las cargas de trabajo variables en los
programas en tiempo real. La versión avanzada de la simulación de programación en tiempo real de
Operating Systems Workbench admite tres procesos. En la parte 1 de la actividad, investigamos la
programación con tres procesos periódicos en tiempo real. En la parte 2 de esta actividad, usamos uno de
los procesos para asumir el rol de una carga de trabajo en segundo plano con ráfagas (pero cuyas tareas
tienen restricciones de tiempo real), lo que impacta en la capacidad del programador para programar
correctamente un par de procesos periódicos en tiempo real. . La simulación admite la configuración de una
compensación de tiempo de inicio para cada proceso. En la segunda parte de este experimento,
establecemos esto para el tercer proceso de manera que comience en medio de un programa establecido de
un par de procesos periódicos.
68 CAPITULO 2 LA VISTA DEL PROCESO

ACTIVIDAD P9 UTILIZAR EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR LA PROGRAMACIÓN


DE SISTEMAS EN TIEMPO REAL (AVANZADO): COMPARACIÓN DE ALGORITMOS DE PROGRAMACIÓN MONOTÓNICA
DE FECHA LÍMITE Y TASA

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.

Lea el documento "Actividades de programación en tiempo real (avanzadas)".

Los resultados del aprendizaje


1. Obtener una comprensión más detallada de la naturaleza de la programación en tiempo real, especialmente con respecto a la sensibilidad a las cargas de

trabajo en segundo plano.

2. Mejorar la comprensión del algoritmo de programación de tarifas monótonas


3. Mejorar la comprensión del algoritmo de programación de fechas límite

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),

Parte 1. Programación con tres procesos


1. Configure los procesos de la siguiente manera:

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

cronograma siempre conducirá a incumplimientos de fechas límite.

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

diferentes hasta que alcance una configuración aceptable.

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

procesos de la siguiente manera:

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

ACTIVIDAD P9 USANDO EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR LA PROGRAMACIÓN DE


SISTEMAS EN TIEMPO REAL (AVANZADO): COMPARACIÓN DE ALGORITMOS DE PROGRAMACIÓN MONOTÓNICA DE
FECHA LÍMITE Y TASA — Cont.

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

ACTIVIDAD P9 USANDO EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR LA PROGRAMACIÓN DE


SISTEMAS EN TIEMPO REAL (AVANZADO): COMPARACIÓN DE ALGORITMOS DE PROGRAMACIÓN MONOTÓNICA DE
FECHA LÍMITE Y TASA — Cont.

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.

2.5 ALGORITMOS Y VARIANTES DE PROGRAMACIÓN ESPECÍFICOS, UTILIZADOS


EN SISTEMAS OPERATIVOS MODERNOS
Casi todos los sistemas operativos populares utilizados en sistemas informáticos de propósito general implementan
variantes de MLFQ debido a la flexibilidad que ofrece al programar tareas de diferentes tipos. Los sistemas operativos
basados en Windows NT utilizan MLFQ con 32 niveles de prioridad. Los niveles de prioridad 0-15 son para procesos que no
son en tiempo real y los niveles 16-31 son para procesos con requisitos de programación en tiempo real suaves. Linux ha
utilizado una variedad de algoritmos de programación, que incluyen un programador MLFQ con un rango de niveles de
prioridad de 0 a 140, de los cuales los niveles en el rango 0-99 estaban reservados para tareas en tiempo real. Además de
reservar los niveles de cola de mayor prioridad para tareas en tiempo real, también se les asignan tamaños cuánticos
considerablemente más grandes (aproximadamente 200 ms), mientras que las tareas que no son de tiempo real obtienen
cuantos de aproximadamente 10 ms. AIX (una versión de Unix) ha admitido una variedad de algoritmos de programación en
sus diversas versiones, incluidas FIFO y RR. La familia de sistemas operativos Mac OS ha utilizado varios algoritmos de
programación diferentes, basados en subprocesos preventivos a nivel de kernel. OS X usa MLFQ con cuatro niveles de
prioridad (normal (prioridad más baja), prioridad alta del sistema, solo modo kernel y tiempo real (prioridad más alta)).

2.6 COMUNICACIÓN INTERPROCESO


La comunicación entre procesos en sistemas distribuidos es necesaria por una amplia variedad de razones. Las
aplicaciones pueden comprender una gran cantidad de procesos diferentes, distribuidos en varias computadoras
físicas. Algunas comunicaciones serán locales, entre procesos en la misma computadora, y algunas serán entre
procesos en diferentes computadoras, quizás con diferentes arquitecturas de procesador y / o diferentes sistemas
operativos.
Anteriormente en este capítulo, la canalización se introdujo como un medio de comunicación entre procesos al
mapear el flujo de salida de uno con el flujo de entrada de otro. Esto puede ser muy útil para construir conductos
lógicos donde los datos fluyen a través de varios procesos, siendo manipulados en cada paso, como en el Adder |
Ejemplo de doblador utilizado. Sin embargo, esta forma de IPC opera en una sola dirección y los procesos están
acoplados directamente por la tubería, es decir, operan como una unidad síncrona, y el segundo proceso espera la
salida del primero. Otra limitación significativa del mecanismo de tubería es que está mediado por el sistema
operativo local del proceso y, por tanto, por implicación, ambos procesos deben estar ubicados en la misma
computadora física.

2.6.1 INTRODUCCIÓN A LOS ENCHUFES


La canalización es solo uno de los varios mecanismos de IPC disponibles; Los enchufes son otro mecanismo muy
importante. Aquí, presentamos sockets en el contexto de IPC entre procesos locales. El uso de calcetines
2.6 COMUNICACIÓN INTERPROCESO 71

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

Los mensajes se pueden enviar en


cualquier dirección entre un par de sockets

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
...

bool CreateSocket (vacío)


{
m_SendSOCKET = conector (AF_INET, SOCK_DGRAM, PF_INET);
// Crea un conector y conserva una referencia a él. AF_INET indica que se utilizará el direccionamiento de Internet // SOCK_DGRAM indica que se utilizarán
mensajes discretos (a diferencia de un flujo de datos) // PF_INET indica que se utilizarán protocolos de Internet

si (INVALID_SOCKET == m_SendSOCKET)
{
cout << "socket () falló" << endl; falso retorno; // Informar error si no se pudo crear el socket

}
devuelve verdadero;
}

...

vacío SetUpSendAddressStructFor_LocalLoopback (vacío)


{ // Inicializar una estructura SOCKADDR_IN con los detalles de la dirección del socket del destinatario
m_SendSockAddr.sin_addr.S_un.S_un_b.s_b1 = (carácter sin firmar) 127; // Establecer los cuatro bytes de la dirección de Internet
m_SendSockAddr.sin_addr.S_un.S_un_b.s_b2 = (carácter sin firmar) 0; // ( esta es la dirección del destinatario).
m_SendSockAddr.sin_addr.S_un.S_un_b.s_b3 = (carácter sin firmar) 0; // La dirección de bucle invertido se utiliza porque tanto
m_SendSockAddr.sin_addr.S_un.S_un_b.s_b4 = (carácter sin firmar) 1; // los procesos estarán en la misma computadora.
m_SendSockAddr.sin_family = AF_INET;
m_SendSockAddr.sin_port = htons (8007); // Configure el número de puerto. Esto será utilizado por el sistema operativo.
// para identificar el proceso del destinatario, una vez que el destinatario se ha enlazado // al
puerto utilizando la primitiva bind ()
}

...

bool SendMessage (vacío)


{
m_iSendLen = m_UserInput.length ();
// Envía el mensaje escrito por el usuario (que está en la variable de cadena m_UserInput)
// La primitiva sendto () usa la referencia m_SendSOCKET para identificar el conector en uso, // y la estructura de direcciones
m_SendSockAddr para identificar dónde enviar el mensaje
int iBytesSent = sendto (m_SendSOCKET, (char FAR *) m_UserInput.c_str (), m_iSendLen, 0,
(const struct sockaddr FAR *) & m_SendSockAddr, sizeof (m_SendSockAddr));
si (INVALID_SOCKET == iBytesSent)
{
cout << "¡Sendto () Falló!" << endl; falso retorno; // Informar error si el envío no fue exitoso

}
devuelve verdadero;
}

FIGURA 2.28

Segmentos de código C ++ anotados del programa IPC_socket_Sender.

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)

...

bool CreateSocket (vacío)


{
m_IPC_ReceiveSOCKET = enchufe (AF_INET, SOCK_DGRAM, PF_UNSPEC);
// Crea un conector y conserva una referencia a él. AF_INET indica que se utilizará el direccionamiento de Internet //
SOCK_DGRAM indica que se utilizarán mensajes discretos.
// PF_INET indica que se utilizarán los protocolos de Internet.
si (INVALID_SOCKET == m_IPC_ReceiveSOCKET)
{
cout << "CreateSocket () falló" << endl; // Informe de error si no se pudo crear el socket
falso retorno;
}
devuelve verdadero;
}

...

bool SetUpLocalAddressStruct (vacío)


{ // Inicializar una estructura SOCKADDR_IN con la dirección local (para recibir)
m_LocalSockAddr.sin_addr.S_un.S_addr = htonl (INADDR_ANY);
// INADDR_ANY indica que se recibirá un mensaje si se envió a CUALQUIER // de las
direcciones IP de la computadora local (puede tener una o más)
m_LocalSockAddr.sin_family = AF_INET;
m_LocalSockAddr.sin_port = htons (8007); // Configure el número de puerto, que se utilizará para
// mapea un mensaje entrante a este proceso
devuelve verdadero;
}

...

bool BindToLocalAddress (vacío)


{ // El proceso asocia su socket y, por lo tanto, a sí mismo con un puerto, usando la primitiva bind ().
// El sistema operativo establece una asociación entre el proceso y el número de puerto al que se vincula. // A partir
de este momento, el sistema operativo entregará mensajes dirigidos por el // número de puerto seleccionado, a este
proceso
int iError = bind (m_IPC_ReceiveSOCKET, (const SOCKADDR FAR *) & m_LocalSockAddr,
sizeof (m_LocalSockAddr));
si (SOCKET_ERROR == iError)
{
cout << "bind () Falló!"; // Informe de error si falla la vinculación (es posible que el puerto ya esté en uso)
falso retorno;
}
devuelve verdadero;
}

...

vacío ReadMessageFromReceiveBufferOrWait (vacío)


{ // El proceso usa la función receivefrom () para comenzar a esperar un mensaje. Si un mensaje ya está en // el búfer
de recepción, se entregará inmediatamente al proceso. Si un mensaje aún no ha llegado //, el proceso esperará (el
sistema operativo lo moverá al estado 'bloqueado').
int iBytesRecd = recvfrom (m_IPC_ReceiveSOCKET, (char FAR *) m_szRecvBuf,
RECEIVE_BUFFER_SIZE, 0, NULL, NULL);
si (SOCKET_ERROR == iBytesRecd)
{
cout << "Error al recibir" << endl; // Informar error si la operación de recepción falló
CloseSocketAndSalir ();
}
demás
{
m_szRecvBuf [iBytesRecd] = 0; // Asegurar la terminación nula de la cadena del mensaje en el búfer
}
}

FIGURA 2.29

Segmentos de código C ++ anotados del programa IPC_socket_Receiver.

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

ACTIVIDAD P10 INTRODUCCIÓN A LA COMUNICACIÓN INTERPROCESO


BASADA EN ENCHUFES (IPC)
Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido los recursos complementarios necesarios como se explica en Actividad P1 .

Los resultados del aprendizaje


Esta actividad presenta los sockets como un medio de comunicación entre procesos.
1. Entender el concepto de socket como punto final de comunicación.
2. Comprender que la comunicación se puede lograr entre un par de enchufes, utilizando el enviar a y recvfrom
primitivas
3. Comprender cómo se puede utilizar la dirección IP especial de bucle invertido local para identificar la computadora local (sin tener que
conocer su dirección IP única real)
4. Comprender cómo se asocia un número de puerto con un proceso en particular, de modo que se pueda enviar un mensaje a ese proceso
específico cuando hay muchos procesos en la misma dirección IP

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

ACTIVIDAD P10 INTRODUCCIÓN A LA COMUNICACIÓN INTERPROCESO BASADA


EN ENCHUFES (IPC) —Contenido

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.

2,7 HILOS: INTRODUCCIÓN


2.7.1 CONCEPTOS GENERALES
Se puede decir que un programa que tiene una sola lista de instrucciones que se ejecutan una a la vez en el orden que dicta
la lógica del programa es de un solo subproceso (se dice que el programa tiene un solo "subproceso de control"). Por el
contrario, un proceso multiproceso tiene dos o más subprocesos de control. Estos subprocesos pueden estar todos activos
al mismo tiempo y pueden operar de forma asincrónica con respecto a los otros subprocesos (es decir, una vez iniciados,
cada subproceso continúa con su propio trabajo hasta que se completa; no tiene que sincronizar su actividad con los otros
subprocesos ). Alternativamente, su comportamiento puede estar sincronizado; Normalmente, esto puede implicar que un
hilo pase el control (ceder) a otro hilo o que un hilo se pause hasta que se complete otro, según los requisitos lógicos del
programa y el mecanismo de enhebrado real utilizado.
El subproceso que se ejecuta inicialmente y crea los otros subprocesos se denomina subproceso principal de ejecución. Los otros
subprocesos se denominan subprocesos de trabajo. Los subprocesos dentro de un proceso comparten los recursos del proceso,
como la memoria, los archivos abiertos y posiblemente las conexiones de red. Una vez iniciados, es posible que sea necesario
sincronizarlos para pasar datos de uno a otro o para garantizar que los accesos a los recursos compartidos se realicen de tal manera
que se evite la incoherencia; por ejemplo, si dos subprocesos actualizan una variable compartida de forma independiente, es posible
que uno sobrescriba la salida del otro. Por tanto, el acceso a los recursos compartidos debe estar regulado, normalmente mediante
un mecanismo de sincronización. Los subprocesos en algunos sistemas pueden ser bloqueados (independientemente de los otros
subprocesos) por el sistema operativo, lo que mejora la capacidad de respuesta de
76 CAPITULO 2 LA VISTA DEL PROCESO

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.

2.7.2 IMPLEMENTACIONES DE HILO


Hay dos categorías distintas de mecanismos de subprocesos: aquellos en los que el subproceso es visible y compatible con
el programador del sistema operativo (y se denominan subprocesos a nivel de kernel) y aquellos que están integrados en
bibliotecas de espacio de usuario y existen solo en el proceso. level (que se conocen como subprocesos a nivel de usuario).

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

Subprocesos a nivel de usuario Subprocesamiento a nivel de kernel

Sistema operativo Proceso interno Sistema operativo


vista vista vista

PAG 1 PAG 1 T 1 PAG 1 T 2 PAG 1 T 3 PAG 1 T 1 PAG 1 T 2 PAG 1 T 3 PAG 2 T 1 PAG 2 T 2

Proceso reemplazado por


sistema operativo

PAG 2 PAG 2 T 1 PAG 2 T 2

PAG 2 T 2 realiza IO,


sistema operativo
PAG 1 T 3 continúa
bloquea todo el proceso
de donde viene
Parado
PAG 1 PAG 1 T 1 PAG 1 T 2 PAG 1 T 3

Proceso reemplazado por


sistema operativo Hora

FIGURA 2.30

Comparación de subprocesos a nivel de usuario y subprocesos a nivel de kernel.

2.7.3 ENFOQUES DE PROGRAMACIÓN DE HILOS


Hay dos formas en que los sistemas operativos realizan la programación de subprocesos:

1. El sistema operativo le da a un hilo un cuanto y se adelanta al hilo cuando el cuanto tiene


Caducado. Esto se denomina multitarea preventiva y es similar a la programación de procesos preventiva.
2. Los subprocesos son iniciados por el sistema operativo y ceden el control cuando necesitan detenerse
procesamiento, por ejemplo, cuando han completado una tarea o deben esperar para sincronizarse con
otro hilo. Esto se denomina multiproceso cooperativo.

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.

2.7.4 OPERACIÓN DE HILO SINCRÓNICO (SECUENCIAL) VERSUS ASÍNCRONO


(CONCURRENTE)
Los enfoques de programación de subprocesos discutidos anteriormente se aplican al nivel del sistema operativo y se relacionan con
la forma en que el sistema realmente programa los subprocesos. Cualquier hilo que pueda ejecutarse puede seleccionarse para
ejecutarse a continuación; el desarrollador del programa no controla este aspecto.
Sin embargo, dependiendo de la función real y la lógica de las aplicaciones, hay situaciones en las que el desarrollador
necesita influir en el orden de ejecución de subprocesos como parte de la lógica del programa, es decir, restringiendo el
conjunto de subprocesos que se pueden ejecutar en un momento particular por el sistema operativo, según los criterios
específicos de la lógica del programa. Un ejemplo común es requerir que un hilo en particular se pause hasta que se haya
completado otro hilo específico. El desarrollador puede necesitar hacer cumplir la sincronización en la forma en que se
ejecutan los subprocesos para garantizar la corrección en términos de, por ejemplo, la forma en que se accede a los
recursos o el orden en que se realizan las funciones.
La operación de subproceso sincrónico requiere que un subproceso en particular espere a que otro subproceso
específico se complete o ceda (pasar el control a otro subproceso) antes de continuar. La programación de subprocesos
asincrónica permite que los subprocesos se ejecuten simultáneamente, es decir, uno no espera a que se complete el otro.
En este caso, los subprocesos se ejecutan cuando se les da tiempo de CPU (programado por el sistema operativo) sin que el
proceso controle activamente la secuencia. El grado de sincronicidad en el comportamiento de los subprocesos dentro de
un proceso se controla mediante el uso de primitivas de subprocesos.
La primitiva de unión se puede utilizar para lograr la sincronización. El hilo que llama, el que llama al método join (), se
bloquea hasta que se completa el hilo llamado, el hilo cuyo método join () fue llamado. Por ejemplo, se puede hacer que el
subproceso A espere a que el subproceso B se complete mediante el subproceso A que llama al método join () del
subproceso B. Esto se ilustra en Figura 2.31 , que muestra el pseudocódigo para un escenario en el que se utiliza la
sincronización de subprocesos para garantizar que la acción X ocurra antes que la acción Y.
La sincronización de subprocesos con unión se explora con más detalle en el programa Threads_Join (ver
Figura 2.32 ).

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

void Function1 (cadena sStr)


{
size_t tThreadID = std :: this_thread :: get_id (). hash ();
thread thread2 (Función2 / * función que realizará el hilo * /,
"BBBBBBBBBBBB" / * parámetro para la función del hilo * /);
thread2.join (); // el hilo de llamada (thread1) espera a que termine el hilo llamado (thread2)
...
}

void Function2 (cadena sStr)


{
size_t tThreadID = std :: this_thread :: get_id (). hash ();
thread thread3 (Función3 / * función que realizará el hilo * /,
"0000-CCCCCC" / * parámetros para la función del hilo * /);
thread3.join (); // el hilo de llamada (thread2) espera a que termine el hilo llamado (thread3)
...
}

void Function3 (cadena sStr)


{
size_t tThreadID = std :: this_thread :: get_id (). hash ();
...
}

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

Secciones de código seleccionadas: ejemplo de Threads_Join (C ++).

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

Principal Hilos de trabajador Clave


hilo Thread1 Thread2 thread3 Hilo inactivo
Hilo activo

Trabajador thread1 creado


Trabajador thread1 Unido al hilo principal
Worker thread2 creado
Trabajador thread2 Unido al hilo1
Trabajador hilo3 creado
Trabajador thread3 Unido para enhebrar2

Trabajador thread3 completa, trabajador thread2 reanuda

Trabajador thread2 completa, trabajador thread1 reanuda

Trabajador thread1 completa, Hilo principal reanuda

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)
{
...
}

void Function2 (cadena sStr)


{
...
}

void Function3 (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 * /);

// Separar los hilos


thread1.detach ();
thread2.detach ();
thread3.detach ();
...
}

FIGURA 2.35

Secciones de código seleccionadas: ejemplo de Threads_Detach (C ++).

Principal Hilos de trabajador Clave

hilo Hilo1 Hilo2 Hilo3 Hilo inactivo


Hilo activo

Hilos de trabajador creado

Hilos de trabajador separado de hilo principal

Se ejecutan el hilo principal


y todos los hilos del trabajador
al mismo tiempo

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

ACTIVIDAD P11 INVESTIGACIÓN EMPÍRICA DEL COMPORTAMIENTO DE HILOS


Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido los recursos complementarios necesarios como se explica en Actividad P1 .

Los resultados del aprendizaje


1. Comprender los conceptos de subprocesos.
2. Obtener una comprensión básica de las formas en que se utilizan los subprocesos en los programas.
3. Obtener un conocimiento básico de la ejecución de subprocesos sincrónicos y asincrónicos.

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.

Parte 1. Ejecución de subprocesos con la operación join ()


1. Examine el código fuente del programa Threads_Join. Asegúrese de comprender la lógica de este programa,
especialmente la forma en que se crean tres hilos, cada uno dentro del anterior (el hilo1 se crea dentro
la función principal, thread2 se crea dentro de thread1, y thread3 se crea dentro de thread2); en cada caso, los
hilos se "unen" con su hilo de creación.
2. Ejecute el programa Threads_Join en una ventana de comandos. Navegue a la subcarpeta "ProcessView" y luego ejecute el
programa Threads_Join escribiendo Threads_Join.exe seguido de la tecla Intro.
3. Observe el comportamiento de los hilos. Intente relacionar lo que ve que sucede con la lógica del código fuente.
Las capturas de pantalla siguientes proporcionan ejemplos de los patrones de ejecución de subprocesos que se producen. La imagen de la pantalla izquierda

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.

Parte 2. Ejecución de subprocesos con la operación detach ()


1. Utilice el mismo método que en la parte 1, pero esta vez utilizando el programa Threads_Detach.
2. Debería ver que el comportamiento es diferente cada vez que ejecuta el programa Threads_Detach. Verá que las salidas de los tres
subprocesos están entrelazadas entre sí y también con las salidas de main, lo que indica que todas se están ejecutando en paralelo
(es decir, el subproceso que llama no espera a que termine su subproceso llamado antes de continuar).
2,7 HILOS: INTRODUCCIÓN 83

ACTIVIDAD P11 INVESTIGACIÓN EMPÍRICA DEL COMPORTAMIENTO DE HILOS — Cont.


Gastos esperados
Debería ver que los tres subprocesos se inician como parte del mismo proceso, pero los subprocesos en sí ejecutan sus instrucciones
independientemente de los otros subprocesos. Debería ver los diferentes efectos de usar join () y detach () para determinar si la lógica en la
función de llamada debe sincronizarse con la terminación del hilo llamado o si los hilos pueden ejecutarse al mismo tiempo. Debe notar que
a los subprocesos se les asigna una identificación única (dentro del proceso en sí) y que esta identificación se asigna dinámicamente y
puede cambiar cada vez que se ejecuta el programa. También debería ver que con detach (), el tiempo relativo real del comportamiento de
los subprocesos (y por lo tanto el orden de su salida) no es predecible y puede diferir cada vez que se ejecuta el programa.

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

necesidad de un diseño y pruebas cuidadosos.

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í.

2.7.5 COMPLEJIDAD ADICIONAL QUE ACOMPAÑA AL ROSCADO


Un solo hilo de control es predecible, fundamentalmente porque solo una acción (como leer una variable o escribir una
variable) puede ocurrir en cualquier momento y, por lo tanto, se asegura la consistencia de su espacio de datos privados. Sin
embargo, con varios subprocesos que comparten el acceso al espacio de datos del proceso, existe la posibilidad de que la
sincronización de las acciones de los diferentes subprocesos dé lugar a inconsistencias. Por ejemplo, si un subproceso
escribe en una variable y otro lee la misma variable, la sincronización precisa de estas acciones se vuelve fundamental para
garantizar que las actualizaciones se realicen en el orden correcto y que los valores de los datos no se sobrescriban antes de
que se utilicen. Este es un ejemplo de lo que se denomina "condiciones de carrera". Sin una regulación cuidadosa, Las
aplicaciones con subprocesos no son deterministas en el sentido de que la secuencia general de eventos y, por lo tanto, el
comportamiento real y los resultados obtenidos, son sensibles al comportamiento real de sincronización detallada en el
sistema, de modo que si el mismo proceso se ejecuta dos veces, puede dar resultados diferentes. incluso si los valores de
datos reales utilizados son los mismos cada vez. La regulación necesaria para garantizar que el comportamiento de los
procesos siga siendo determinista, al menos en términos de consistencia de datos, y que en última instancia produzca el
resultado correcto se logra mediante la combinación de un diseño cuidadoso del programa y el uso de mecanismos de
control en tiempo de ejecución como semáforos y " mutex ”, que proporciona acceso mutuamente exclusivo a los recursos
para que las operaciones realizadas por diferentes subprocesos no puedan superponerse.

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

2.7.5.1 Escenarios de aplicación para subprocesos múltiples


El subproceso múltiple se puede utilizar para permitir que un proceso siga respondiendo a pesar de las actividades de E / S. En un programa de

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.

2.7.6 UN EJEMPLO DE IPC MULTIHILO


Como se mencionó anteriormente, IPC es una forma de IO y, por lo tanto, mientras espera un mensaje entrante, un proceso
se bloqueará. El envío y la recepción simultáneos se pueden lograr a nivel de proceso al tener subprocesos separados para
cada una de las actividades de envío y recepción (lo que permite que cada una se programe y bloquee de forma
independiente). En esta sección, usamos un ejemplo de IPC multiproceso para ampliar la discusión de los beneficios del
multiproceso y también para profundizar un poco más en los problemas de IPC.
El programa Multithreaded_IPC combina subprocesos e IPC y demuestra cómo, mediante el uso de
subprocesos asíncronos, las actividades de envío y recepción pueden operar simultáneamente. Tenga en
cuenta que en una aplicación de un solo subproceso, estos se serializarían y la aplicación sería difícil de
2,7 HILOS: INTRODUCCIÓN 85

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 ()

Continuar como hilo de envío


Bucle (hasta que se ingrese el comando "Salir")
Obtener mensaje del usuario
Enviar mensaje de IPC
Final de bucle
Final

Recibir hilo
Comienzo
Círculo
Recibir mensaje de IPC
Mostrar mensaje al usuario Fin del
ciclo
Final

FIGURA 2.37

Pseudocódigo del programa IPC multiproceso.


Hilo principal Hilo de trabajador Clave
(recibir hilo) Hilo inactivo
Hilo activo

Realiza
inicialización
Se creó el hilo de trabajo

separar () 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

El subproceso principal y el subproceso de trabajo se ejecutan simultáneamente en Multithreaded_IPC.

...
bSuccess = CreateSendSocket ();

...
bSuccess = CreateReceiveSocket ();
...

bSuccess = SetUpLocalAddressStruct ();


...
SetUpSendAddressStructFor_LocalLoopback ();
...

m_bMainThreadRunning = true;

// Crear e iniciar el hilo de trabajo (realiza la recepción en bucle) hilo


ReceiveThread (ReceiveMessages);

// 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

// ******************************* Código de inicio de hilo de recepción ************ ******************* void


ReceiveMessages (void) // El cuerpo del hilo de recepción
{
cout << endl << "ReceiveThread: Esperando mensaje ... "<< endl;

while (verdadero == m_bMainThreadRunning)


{
ReadMessageFromReceiveBufferOrWait ();
}
}

vacío ReadMessageFromReceiveBufferOrWait (vacío)


{
// La llamada recvfrom se modifica proporcionando la dirección y la longitud de una estructura SOCKADDR_IN // para
contener la dirección del remitente del mensaje (para que se pueda enviar una respuesta).
int iBytesRecd = recvfrom (m_IPC_ReceiveSOCKET, (char FAR *) m_szRecvBuf, RECEIVE_BUFFER_SIZE,
0, NULO, NULO);
si (verdadero == m_bMainThreadRunning)
{
si (SOCKET_ERROR == iBytesRecd)
{
cout << endl << "ReceiveThread: Error al recibir" << endl;
}
demás
{
m_szRecvBuf [iBytesRecd] = 0; // Asegúrese de que la terminación sea
nula cout << endl << "ReceiveThread: Mensaje recibido: "<< m_szRecvBuf << endl;
}
}
}
// ******************************** Código de fin de hilo de recepción ********** ***********************

int SendMessage (void) // Llamado desde el hilo principal {

int iLength = strlen (m_szSendBuf);

// Envía un datagrama UDP que contiene el mensaje de respuesta


int iBytesSent = sendto (m_IPC_SendSOCKET, (char FAR *) & m_szSendBuf, iLength, 0,
(const struct sockaddr FAR *) & m_SendSockAddr, sizeof (m_SendSockAddr));
si (INVALID_SOCKET == iBytesSent)
{
cout << "SendThread: sendto () Failed!" << endl;
CloseSocketsAndSalir ();
}
return iBytesSent;
}

FIGURA 2.40

Secciones clave anotadas del Programa IPC multiproceso, recibir hilo ( Código C ++).

ACTIVIDAD P12 INVESTIGACIÓN ADICIONAL DE HILOS E IPC UTILIZANDO EL


IPC MULTIHILO PROGRAMA
Prerrequisitos
Las instrucciones a continuación asumen que ha obtenido los recursos complementarios necesarios como se explica en
Actividad P1 .

Los resultados del aprendizaje


1. Reforzar la comprensión de los conceptos de subprocesos.
2. Reforzar la comprensión del uso de enchufes para lograr IPC
3. Comprender cómo el subproceso múltiple asincrónico facilita tener actividades de bloqueo simultáneas (en este
caso, enviar y recibir), que de otro modo sería serializado
88 CAPITULO 2 LA VISTA DEL PROCESO

ACTIVIDAD P12 INVESTIGACIÓN ADICIONAL DE HILOS E IPC UTILIZANDO EL


IPC MULTIHILO PROGRAMA — Cont.
Método
Esta actividad utiliza dos instancias de proceso del programa. IPC multiproceso comunicarse juntos para ilustrar la comunicación full
dúplex en la que el envío y la recepción operan simultáneamente en cada dirección:
1. Examine el código fuente de la IPC multiproceso programa. Asegúrese de comprender la lógica de este programa,
especialmente la forma en que la actividad de comunicación se separa entre los dos subprocesos (de modo que el subproceso principal se ocupa de la

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,

Multiproceso_IPC 8001 8002 (enviar al puerto 8001, recibir en el puerto 8002)

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

ACTIVIDAD P13 UTILIZAR EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR EL


COMPORTAMIENTO DE HILOS

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".

Los resultados del aprendizaje


1. Mejorar la comprensión de los conceptos de subprocesos.
2. Mejorar la comprensión de la ejecución de subprocesos sincrónicos y asincrónicos

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

ACTIVIDAD P13 USANDO EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR EL


COMPORTAMIENTO DE LOS HILOS — Cont.

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

hilo rojo. ha corrido.

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.

Puede ver el entrelazado resultante del comportamiento de coloración de píxeles logrado.

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

ACTIVIDAD P13 USANDO EL BANCO DE TRABAJO DE SISTEMAS OPERATIVOS PARA EXPLORAR EL


COMPORTAMIENTO DE LOS HILOS — Cont.

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.

2.8 OTRAS FUNCIONES DEL SISTEMA OPERATIVO


Además de la programación de procesos como se discutió en detalle anteriormente, los sistemas operativos necesitan
realizar una variedad de tareas relacionadas. Éstos surgen de la necesidad de administrar los recursos del sistema y
ponerlos a disposición de los procesos en ejecución, según sean necesarios.
Aquí se identifican brevemente otros dos roles, ya que tienen impactos en la forma en que operan los procesos
y en la eficiencia y efectividad general de las plataformas en las que se ejecutan los procesos. Dado que estas
actividades tienen un impacto significativo en el uso de los recursos, se revisan con más detalle en el Capítulo 4.
Gestión de la memoria. El sistema operativo debe garantizar que los procesos tengan acceso a un área
reservada de memoria durante su ejecución. Debe limitar la cantidad de memoria utilizada por un solo
proceso y garantizar que los procesos estén separados (y protegidos) entre sí, evitando que un proceso lea o
modifique la parte de la memoria que está actualmente en uso por otro proceso.
Asignación de recursos. Los procesos solicitan acceso a ciertos recursos de forma dinámica, a veces
impredecible (ciertamente al sistema operativo). El sistema operativo debe garantizar que se otorguen recursos a
los procesos según sea necesario mientras se mantiene la separación de los procesos y se evita que surja un
conflicto cuando dos procesos intentan utilizar un recurso simultáneamente. Si el sistema operativo deniega una
solicitud de acceso a un recurso, el proceso puede esperar (bloquearse).

2.9 USO DE TEMPORIZADORES DENTRO DE LOS PROGRAMAS


Hay muchas situaciones en las que las tareas deben ejecutarse periódicamente a un cierto ritmo, y posiblemente, varios
tipos diferentes de eventos o tareas deben ocurrir a diferentes ritmos dentro del mismo programa. Los temporizadores
proporcionan un medio para ejecutar código dentro de un proceso en un período determinado o después de un intervalo
específico. Estos son particularmente útiles en aplicaciones distribuidas en combinación con operaciones de sockets sin
bloqueo, por ejemplo, verificar si ha llegado un mensaje, sin bloquear la aplicación para esperar el mensaje; esto se analiza
en profundidad en el Capítulo 3, y se ilustra el uso de temporizadores. Aquí, el concepto de usar temporizadores se
introduce para completar, ya que tiene algunas similitudes y puede considerarse una alternativa al uso de subprocesos en
algunas circunstancias.
La expiración de un temporizador puede considerarse un evento al que el programa debe responder. La función que se invoca se
denomina "controlador de eventos" para el evento del temporizador.
92 CAPITULO 2 LA VISTA DEL PROCESO

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.

principal función1 función2 función3

Iniciar temporizador1 (intervalo I1, ejecutar función1 en tiempo de


I1
espera) Iniciar temporizador2 (intervalo I2, ejecutar función2 en tiempo de espera)

I2
función1 activa

Iniciar timer3 (intervalo I3, ejecutar function3 en timeout)

I3 función2 activa

Clave

function3 activa Función inactiva


Función activa
Intervalo de tiempo

Hora
FIGURA 2.41

Comportamiento controlado por temporizador.


2.9 USO DE TEMPORIZADORES DENTRO DE LOS PROGRAMAS 93

...
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

System.Timers.Timer Timer2 = nuevo System.Timers.Timer (); // Crea un segundo temporizador


Timer2.Elapsed + = nuevo System.Timers.ElapsedEventHandler (OnTimedEvent_Timer2); // Establecer el controlador de eventos para el segundo
temporizador Timer2.Interval = 2000; // Establecer el intervalo de tiempo en 2 segundos (2000 ms)
Timer2.Enabled = falso; // No inicie la ejecución del temporizador todavía

System.Timers.Timer Timer3 = nuevo System.Timers.Timer (); // Crear tercer temporizador


Timer3.Elapsed + = nuevo System.Timers.ElapsedEventHandler (OnTimedEvent_Timer3); // Establecer el controlador de eventos para el tercer temporizador
Timer3.Interval = 3000; // Establecer el intervalo de tiempo en 3 segundos (3000 ms)

// Iniciar los tres temporizadores


Timer1.Enabled = true;
Timer2.Enabled = true;
Timer3.Enabled = true;
...

// Controladores de eventos para los tres temporizadores


vacío estático privado OnTimedEvent_Timer1 (Object myObject, EventArgs myEventArgs) {

Console.WriteLine ("Un segundo");


}

vacío estático privado OnTimedEvent_Timer2 (Object myObject, EventArgs myEventArgs) {

Console.WriteLine (" Dos segundos");


}

vacío estático privado OnTimedEvent_Timer3 (Object myObject, EventArgs myEventArgs) {

Console.WriteLine (" Tres segundos ");


}

FIGURA 2.42

Secciones comentadas del programa Timers_Demonstration c #.

FIGURA 2.43

El proceso Timers_Demonstration en ejecución.

2.9.1 USO DE TEMPORIZADORES PARA SIMULAR EL COMPORTAMIENTO DE HILOS

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,

2.10 TRANSPARENCIA DESDE EL PUNTO DE VISTA DEL PROCESO


De las diversas formas de transparencia que se requieren en los sistemas distribuidos, dos formas en particular son
importantes desde el punto de vista del proceso. Estos se presentan brevemente aquí y se tratan con más profundidad en el
Capítulo 6.

2.10.1 TRANSPARENCIA CONCURRENCIA


En situaciones en las que varios procesos simultáneos pueden acceder a recursos compartidos, el sistema operativo debe
garantizar que el sistema se mantenga coherente en todo momento. Esto requiere el uso de mecanismos como bloqueos
(discutidos en el Capítulo 4) y transacciones (discutidos en los Capítulos 4 y 6) para asegurar que los procesos se mantengan
aislados entre sí y no corrompan los resultados de los demás, lo que dejaría el sistema en un estado de alerta. estado
inconsistente.

2.10.2 TRANSPARENCIA MIGRATORIA


Si un recurso se mueve mientras está en uso, cualquier proceso que utilice el recurso debe poder continuar accediendo a él
sin efectos secundarios; esto es bastante desafiante. Más comúnmente, los recursos solo se mueven cuando no están en
uso, y luego, los servicios de directorio se actualizan para que los recursos se puedan encontrar en su nueva ubicación.

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.

2.11 EL ESTUDIO DE CASO DESDE LA PERSPECTIVA DEL PROCESO


La aplicación de estudio de caso es un juego (tic-tac-toe) que se juega a través de una red. La discusión a
continuación se centra en la perspectiva del proceso, y los otros aspectos, como la red y la distribución y el
uso de recursos, se analizan en los otros capítulos pertinentes.
La aplicación del juego consta de dos tipos de componentes: un programa cliente y un programa servidor. El cliente se
ejecutará en la computadora de cada usuario (jugador del juego), proporcionando su interfaz al juego y permitiéndoles
jugar el juego de forma remota. El servidor se ejecutará en cualquier computadora de la red. Puede colocarse en una de las
computadoras de los usuarios o puede estar en una computadora separada. El servidor proporciona conectividad entre los
dos clientes (que no se comunican directamente entre sí) y también proporciona el control y la sincronización de la lógica del
juego y almacena el estado del juego.
La ejecución del juego requiere que los distintos componentes se ejecuten en las distintas computadoras. Tenga en
cuenta que solo hay un programa cliente, pero este se ejecutará en dos equipos, lo que dará lugar a dos instancias de
proceso cliente. La lógica del programa de cada uno de estos es idéntica, pero el comportamiento real de estos será
diferente porque está configurado por las entradas de los usuarios (en el juego de ceros y cruces, esta entrada incluye la
selección del espacio disponible para colocar su token, en cada turno).
Los procesos del cliente se comunicarán con el proceso del servidor utilizando sockets como una técnica de IPC. El
servidor administrará el estado del juego y, por lo tanto, cuando dos clientes se relacionen a través de un juego en vivo, el
servidor mediará en términos de turnos de los usuarios; por lo que el servidor permitirá que uno de los clientes ingrese un
movimiento mientras previene al otro.

2.11.1 REQUISITOS DE PROGRAMACIÓN


La aplicación tiene requisitos de tiempo flexible en el sentido de que los usuarios esperan que su entrada se procese rápidamente y el
tiempo de respuesta (en términos de actualización de ambas interfaces de usuario del cliente para reflejar el último movimiento)
debe ser bajo, quizás dentro de 1 segundo es aceptable.
Los requisitos de tiempo no son precisos; no hay plazos específicos y el aspecto delimitado por el tiempo es muy débil
sin consecuencias reales. Alguna variación (que surja, por ejemplo, debido a una gran carga de trabajo en una o más de las
computadoras host) es aceptable sin disminuir apreciablemente la satisfacción del usuario; por lo tanto, el juego no está en
la categoría de tiempo real suave.
Estos requisitos de temporización son típicos de muchas aplicaciones de usuario de uso general controladas por eventos y deben
ser atendidos adecuadamente por cualquiera de los programadores utilizados actualmente en los sistemas operativos populares,
discutidos anteriormente. No hay ningún requisito para la programación en tiempo real.

2.11.2 USO DE TEMPORIZADORES

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.

2.11.3 NECESIDAD DE HILOS


Lado del cliente: Debido al juego por turnos en esta aplicación en particular, la interfaz de usuario no necesita ser muy
receptiva (compárese, por ejemplo, con un juego de estilo shoot 'em up donde los usuarios están haciendo entradas de
comando y necesitan actualizaciones de pantalla a un nivel muy alto Velocidad). Dado el estilo de juego y la tasa
relativamente baja de interacción del usuario con la interfaz, el aspecto de la interfaz de usuario del cliente del juego no
necesita ser multiproceso (mientras que, para un juego de acción rápida, sería necesario). No se requiere una solución
multiproceso desde el punto de vista de la comunicación porque el requisito de tiempo de comunicación no es estricto. La
combinación de un temporizador y enchufes sin bloqueo se presta a una solución más simple en este caso.
Lado del servidor: El servidor de juegos admitirá una pequeña cantidad de juegos activos y la carga de mensajes por juego activo es baja;

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

necesita ser multiproceso.

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. .

2.11.4 IPC, PUERTOS Y ENCHUFES


El IPC entre procesos en el juego se llevará a cabo a través de una red. Si bien es posible ejecutar dos clientes en la
misma computadora, no tiene sentido desde el punto de vista de un juego distribuido; implicaría que dos jugadores
estaban sentados en la misma computadora. Los procesos utilizarán sockets como puntos finales de comunicación.
El servidor se vinculará a un puerto cuyo número es conocido o detectable por los procesos del cliente. El capítulo 3
tratará en detalle los aspectos relacionados con las redes.
Figura 2.44 ilustra la estructura del juego en varios niveles. Esta representación es muy útil para ayudar a
aclarar la terminología que debemos utilizar. Es importante reconocer que el juego en sí es la aplicación, que
comprende dos programas diferentes: el cliente y el servidor. Estos programas no pueden realmente llamarse
aplicaciones por derecho propio, porque no hacen nada significativo por sí mismos; ambas partes son necesarias
para jugar realmente el juego. Este juego en particular es un juego de dos jugadores y, por lo tanto, el programa
cliente deberá ejecutarse en dos computadoras, una para cada jugador. Tenga en cuenta que el programa cliente
proporciona la interfaz de usuario, que permite al usuario interactuar con la lógica del juego, que en este caso
reside y es controlada por el servidor. Los dos jugadores requerirán la misma interfaz y, por lo tanto, ambos
utilizarán el mismo programa cliente. Es muy importante darse cuenta de que una instancia en juego
2.12 EJERCICIOS DE FIN DE CAPÍTULO 97

1 aplicación El juego

2 programas Cliente del juego Servidor de juegos

3 Procesos Cliente1 Cliente2 Servidor

2 canales de IPC

2 o 3 computadoras Cliente1 Cliente2 Servidor


(La línea punteada representa
límite de la computadora) Cliente1 Cliente2 Servidor

2 usuarios Jugador 1 Jugador2

FIGURA 2.44

Mapeo estructural de los componentes del juego, en los distintos niveles.

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.

2.12 EJERCICIOS DE FIN DE CAPÍTULO


2.12.1 PREGUNTAS

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

Proceso Combinación de estados de proceso

a B C D mi F

A Corriendo Listo Corriendo Obstruido Listo Obstruido

B Listo Obstruido Corriendo Listo Listo Obstruido

C Listo Listo Listo Corriendo Listo Obstruido

2. Para un proceso dado, ¿cuál de las siguientes secuencias de estado (ae) es posible? Justifica tu
respuestas.

(a) Listo → Corriendo → Obstruido → Listo → Corriendo → Obstruido → Corriendo


(B) Listo → Corriendo → Listo → Corriendo → Obstruido → Listo → Corriendo
(C) Listo → Corriendo → Obstruido → Listo → Obstruido → Listo → Corriendo
(D) Listo → Obstruido → Listo → Corriendo → Obstruido → Corriendo → Obstruido
(mi) Listo → Corriendo → Listo → Corriendo → Obstruido → Corriendo → Listo
3. Considere una tarea de procesamiento intensivo que tarda cientos de segundos en ejecutarse cuando no hay otras tareas de procesamiento intensivo

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

cargas de trabajo en segundo plano?

(B) ¿Qué necesita saber un programador en tiempo real sobre los procesos que un programador
el planificador no lo hace?

2.12.2 EJERCICIOS CON LOS BANCOS DE TRABAJO


Utilice el entorno de trabajo de sistemas operativos para investigar el comportamiento de la programación de procesos.

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

1. Configure la simulación de la siguiente manera:

Proceso 1: Tipo = CPU intenso, tiempo de ejecución = 40 ms


Proceso 2: Tipo = CPU intenso, tiempo de ejecución = 40 ms
Proceso 3: Tipo = CPU intenso, tiempo de ejecución = 40 ms
Proceso 4: Tipo = IO intenso, tiempo de ejecución = 30 ms
Proceso 5: Tipo = IO intenso, tiempo de ejecución = 30 ms
Latencia del dispositivo IO = 10 ms

Tamaño cuántico = 10 ms
Configuración del programador = Round Robin
2.12 EJERCICIOS DE FIN DE CAPÍTULO 99

2. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS.


3. Modifique la configuración de la siguiente manera:

Configuración del programador = Trabajo restante más corto Siguiente


4. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS.
Menos tiempo de inactividad de la CPU implica una mejor eficiencia de programación.

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?

Ejercicio 2. Investigación de los efectos de cambiar el tamaño del Quantum de programación

1. Configure la simulación de la siguiente manera:

Proceso 1: Tipo = CPU intenso, tiempo de ejecución = 40 ms


Proceso 2: Tipo = equilibrado, tiempo de ejecución = 50 ms

Proceso 3: Tipo = IO intenso, tiempo de ejecución = 40 ms


Proceso 4: Tipo = equilibrado, tiempo de ejecución = 30 ms

Proceso 5: Tipo = IO intenso, tiempo de ejecución = 30 ms


Latencia del dispositivo IO = 10 ms

Configuración del programador = Round Robin


Tamaño cuántico = 5 ms
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:

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 = 20ms


8. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS DEL PROCESO.
9. 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

1. Configure la simulación de la siguiente manera:

Proceso 1: Tipo = CPU intenso, tiempo de ejecución = 40 ms


Proceso 2: Tipo = equilibrado, tiempo de ejecución = 50 ms

Proceso 3: Tipo = IO intenso, tiempo de ejecución = 40 ms


Proceso 4: Tipo = equilibrado, tiempo de ejecución = 30 ms

Proceso 5: Tipo = IO intenso, tiempo de ejecución = 30 ms


Tamaño cuántico = 15 ms
Configuración del programador = Round Robin
Latencia del dispositivo IO = 5 ms

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:

Latencia del dispositivo IO = 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:

Latencia del dispositivo IO = 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:

Latencia del dispositivo IO = 20ms


8. Ejecute la simulación y MANTENGA UN REGISTRO DE TODOS LOS VALORES ESTADÍSTICOS DEL PROCESO.
9. Modifique la configuración de la siguiente manera:

Latencia del dispositivo IO = 25 ms

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:

Proceso 1: Tipo = equilibrado, tiempo de ejecución = 40 ms

Proceso 2: Tipo = equilibrado, tiempo de ejecución = 50 ms

Proceso 3: Tipo = IO intenso, tiempo de ejecución = 30 ms


Proceso 4: Tipo = No seleccionado
2.12 EJERCICIOS DE FIN DE CAPÍTULO 101

Proceso 5: Tipo = No seleccionado


Latencia del dispositivo IO = 20ms
Tamaño cuántico = 10 ms
Configuración del programador = Trabajo restante más corto
Siguiente0 Q1. Predecir qué proceso terminará primero.
Q2. Predecir qué proceso terminará en último lugar.
Q3. Predecir cuánto tiempo pasará el proceso 1 en el estado bloqueado.
2. Ejecute la simulación para verificar sus predicciones. Si se equivocó, asegúrese de comprender
por qué.

3. Modifique la configuración de la siguiente manera:

Proceso 1: Tipo = CPU intensa, tiempo de ejecución = 70 ms


Proceso 2: Tipo = equilibrado, tiempo de ejecución = 50 ms

Proceso 3: Tipo = IO intenso, tiempo de ejecución = 30 ms


Proceso 4: Tipo = No seleccionado
Proceso 5: Tipo = No seleccionado
Q4. Predecir qué proceso terminará primero. Q5.
Predecir qué proceso terminará en último lugar.
Q6. Predecir cuánto tiempo pasará el proceso 1 en el estado bloqueado.
4. Ejecute la simulación para verificar sus predicciones. Si se equivocó, asegúrese de comprender
por qué.

Ejercicio 5. Predecir y analizar el comportamiento del algoritmo de programación SRJN (parte 2)


1. Configure la simulación de la siguiente manera:

Proceso 1: Tipo = IO intenso, tiempo de ejecución = 40 ms


Proceso 2: Tipo = IO intenso, tiempo de ejecución = 50 ms
Proceso 3: Tipo = CPU intenso, tiempo de ejecución = 100 ms
Proceso 4: Tipo = IO intenso, tiempo de ejecución = 30 ms
Proceso 5: Tipo = IO intenso, tiempo de ejecución = 40 ms
Tamaño cuántico = 25 ms
Configuración del programador = Trabajo restante más corto Siguiente
Latencia del dispositivo IO = 25 ms

Q1. Predecir qué proceso comenzará primero. Q2.


Predecir qué proceso terminará primero. Q3.
Predecir qué proceso terminará en último lugar.
Q4. Predecir cuánto tiempo pasará el proceso 1 en el estado bloqueado. Q5.
Predecir cuánto tiempo pasará el proceso 4 en el estado de ejecución.
2. Ejecute la simulación para verificar sus predicciones. Si se equivocó, asegúrese de comprender
por qué.

3. Modifique la configuración de la siguiente manera:

Latencia del dispositivo IO = 20ms


Q6. Predecir qué proceso comenzará primero. Q7.
Predecir qué proceso terminará primero. Q8.
Predecir qué proceso terminará en último lugar.
Q9. Predecir cuánto tiempo pasará el proceso 2 en el estado bloqueado. Q10.
Predecir cuánto tiempo pasará el proceso 3 en el estado de ejecución.
102 CAPITULO 2 LA VISTA DEL PROCESO

4. Ejecute la simulación para verificar sus predicciones. Si te equivocaste, asegúrate de


entender porqué.
El siguiente ejercicio se basa en el " Algoritmos de programación en tiempo real: avanzado ”Simulación
dentro del Operating Systems Workbench.
Ejercicio 6. Comparación en profundidad entre los algoritmos de programación RATEMONOTONIC (RM)
y DEADLINE (DL)
Usando cada una de las siguientes configuraciones de proceso, experimente con los dos algoritmos de programación.
Mantenga notas detalladas de las características de comportamiento y cualquier observación o problema "interesante" que
ocurra. El objetivo es realizar una comparación científica de los dos algoritmos de programación.

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

2.12.3 EJERCICIOS DE PROGRAMACIÓN


Ejercicio de programación # P1: Creación de una aplicación IPC basada en socket bidireccional (amplíe el código de
muestra proporcionado para enviar un mensaje y devolver una respuesta entre un par de procesos).
Paso 1: Examine el código fuente de la Remitente_socket_IPC y Receptor_socket_IPC programas, que
comprenden la aplicación introductoria de sockets que usamos en Actividad P10 .
Paso 2: Reorganice los programas existentes para crear una nueva versión de la aplicación en la que el
mensaje original enviado al receptor (que se envía al puerto 8007) se modifica y se envía de vuelta al
remitente original (esta vez utilizando el puerto 8008). La modificación puede ser algo simple, como invertir
el orden de los caracteres en el mensaje.
Las principales modificaciones que debe realizar para el nuevo programa remitente son las siguientes:

• Cree un nuevo conector para recibir el mensaje de respuesta.


• Cree una nueva estructura de dirección de socket, que contenga la dirección de la computadora local y el número de
puerto 8008.
• Vincula el nuevo conector de recepción a la estructura de direcciones locales.
• Después de la declaración de envío actual, agregue una declaración de recepción, utilizando el nuevo conector de recepción,
que esperará el mensaje de respuesta.
• Muestra el mensaje de respuesta recibido en la salida de la consola.

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:

• Cree un nuevo conector para devolver el mensaje de respuesta.


• Modifique el método de recepción existente para que almacene la dirección del proceso del remitente del mensaje. Cree una
• nueva estructura de dirección de socket, que contenga la dirección del proceso del remitente (para que se le pueda enviar una
respuesta) y el número de puerto 8008.
• Escriba un método para revertir el mensaje recibido (o realizar alguna otra traducción simple). Después de la
• declaración de recepción actual, agregue una declaración de envío, utilizando el nuevo conector de envío, para
devolver el mensaje de respuesta al remitente del primer mensaje.

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.

2.12.4 RESPUESTAS A LAS PREGUNTAS AL FINAL DEL CAPÍTULO

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:

Son posibles las configuraciones a, d y f.


Las configuraciones bye solo pueden existir momentáneamente porque hay procesos en el estado listo y la
CPU no se está utilizando, por lo que el programador debe enviar inmediatamente uno de los procesos de
estado listo.
La configuración c no puede ocurrir porque hay un solo núcleo de procesamiento.

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

2.12.5 LISTA DE ACTIVIDADES EN EL TEXTO

Número de actividad Sección Descripción

P1 2.2.2 Exploración de programas simples, flujos de entrada


y salida y canalizaciones
P2 2.3 Examinar la lista de procesos en la computadora
P3 2.3.1 Examinar el comportamiento de programación con
procesos reales: Introducción

P4 2.3.1 Examinar el comportamiento de la programación


con procesos reales: competencia por la CPU

P5 2.3.1 Uso de Operating Systems Workbench para explorar el


comportamiento de la programación (Introducción): comparación
de la programación por orden de llegada, el trabajo más corto
primero y la programación por turnos

P6 2.3.1 Uso de Operating Systems Workbench para explorar el


comportamiento de la programación (avanzado):
comparación del trabajo más corto primero, round-robin y
algoritmos de programación del siguiente trabajo restante
más corto

P7 2.4.1 Examinar el comportamiento de la programación con


procesos reales: consideraciones en tiempo real

P8 2.4.1 Uso de Operating Systems Workbench para explorar la


programación para sistemas en tiempo real
(Introducción): comparación de algoritmos de
programación monótonos de fechas límite y tasas

P9 2.4.1 Uso de Operating Systems Workbench para


explorar la programación de sistemas en tiempo
real (avanzado): comparación de algoritmos de
programación monótonos de plazos y tasas
P10 2.6.1 Introducción a la comunicación entre procesos
basada en sockets (IPC)
P11 2.7.4 Investigación empírica del comportamiento de
los hilos
P12 2.7.6 Investigación adicional de subprocesos e IPC
utilizando el IPC multiproceso programa
P13 2.7.6 Uso del entorno de trabajo de sistemas operativos para
explorar el comportamiento de los subprocesos

2.12.6 LISTA DE RECURSOS ACOMPAÑANTES


Los siguientes recursos se mencionan directamente en el texto del capítulo, las actividades integradas y / o los
ejercicios de final de capítulo.

• Operating Systems Workbench (edición de “programación de sistemas”)


• Código fuente (incluidas soluciones para tareas de programación)
• Código ejecutable
106 CAPITULO 2 LA VISTA DEL PROCESO

Programa Disponibilidad Secciones relevantes del capítulo

Adder.exe Código fuente, ejecutable Actividad P1 ( Sección 2.2.2 )


CPU_Hog.exe Código fuente Actividad P4 ( Sección 2.3.1 ), Actividad P7 (
Sección 2.4.1 )
CPU_Hog_2Starter.bat Código fuente Actividad P4 ( Sección 2.3.1 )
CPU_Hog_3Starter.bat Código fuente Actividad P4 ( Sección 2.3.1 )
CPU_Hog_4Starter.bat Código fuente Actividad P4 ( Sección 2.3.1 ), Actividad P7 (
Sección 2.4.1 )
Doubler.exe Código fuente, ejecutable Actividad P1 ( Sección 2.2.2 )
IPC_socket_Receiver.exe Código fuente, ejecutable Actividad P10 ( Sección 2.6.1 )
IPC_socket_Receiver_with_Reply. Código fuente, ejecutable Solución de final de capítulo para
exe ejercicio de programación 1.
IPC_socket_Sender.exe Código fuente, ejecutable Actividad P10 ( Sección 2.6.1 )
IPC_socket_Sender_With_reply. Código fuente, ejecutable Solución de final de capítulo para
exe ejercicio de programación 1
Multithreaded_IPC.exe Código fuente, ejecutable Actividad P12 ( Sección 2.7.6 )
Multiproceso_IPC_remoto_ Código fuente, ejecutable Solución de final de capítulo para
shutdown.exe ejercicio de programación 3
Multiproceso_IPC_two_worker_ Código fuente, ejecutable Solución de final de capítulo para
threads.exe ejercicio de programación 2.
PeriodicOutput.exe Código fuente, ejecutable Actividad P3 ( Sección 2.3.1 ), Actividad P7 (
Sección 2.4.1 )
PeriodicOutput_Starter.bat Código fuente, ejecutable Actividad P3 ( Sección 2.3.1 ), Actividad P7 (
Sección 2.4.1 )
Threads_Detach.exe Código fuente, ejecutable Actividad P11 ( Sección 2.7.4 )
Threads_Join.exe Código fuente, ejecutable Actividad P11 ( Sección 2.7.4 )
Timers_Demonstration.exe Código fuente, ejecutable Sección 2.9

También podría gustarte