Introducción a los procesosHasta el momento hemos visto ejemplos de programas utilizando RTOS y les he hablado de los problemas asociados a la programación con RTOS, pero hay un concepto que debemos aclarar antes de continuar, o de otro modo ustedes pueden llegar a confundirse y escribir programas que no funcionan correctamente.
Todo el tiempo han visto que he utilizado el término tarea o proceso para denotar un fragmento de código que hace “algo”, pero no han visto una definición formal sobre esos términos, así como tampoco una explicación de que es una tarea o proceso.
Las razones de la omisión, son más o menos las mismas que para el caso de los problemas asociados a la programación con RTOS. Normalmente estos temas son bastante tediosos de estudiar, programar utilizando SO no es una tarea fácil al principio, aunque una vez que se domina constituye una poderosa herramienta de trabajo que mejora la productividad y el desempeño de cualquier sistema.
Entonces con el objetivo de motivar en ustedes el interés por la programación con RTOS, decidí no proponerles una línea formal de aprendizaje como suele ocurrir en una escuela o libro sobre el tema, sino ir poniendo ejemplos de cómo utilizar un RTOS y luego introducir la parte pesada del asunto.
Hoy vamos a ver, con bastante detalle qué es un proceso o tarea, porque cuando comencemos a ver los ejemplos de coordinación y sincronización de procesos, necesitaremos tener bien claro que es un proceso, para poder comprender esos problemas-ejemplo de los que tanto les he hablado.
¿Cómo se consigue la multiprogramación?La multitarea o multiprogramación es un proceso que intenta crear la ilusión de que la computadora está haciendo varias cosas al mismo tiempo. Pensemos en una PC, normalmente usted puede escribir un documento mientras escucha música, el programa de descargas baja algún archivo de INTERNET y el antivirus se encuentra activo.
Para lograr hacer cada una de las cosas anteriores, tenemos un conjunto de programas, uno por cada tarea, y cada programa “parece” que se ejecuta como si tuviese la computadora para él solo. En realidad cada programa se ejecuta durante un período corto de tiempo y luego el procesador es cedido a otro programa para que se ejecute durante un poco de tiempo, este mecanismo se conoce como multiprogramación (definición estilo UNIX) o multitarea (definición estilo Microsoft).
Si para una computadora tuviésemos una lista de los programas que está ejecutando y muestreáramos que está haciendo el procesador en un instante de tiempo cualquiera; comprobaríamos que en ese momento está ejecutando una instrucción de alguno de sus programas, pero nunca dos programas a la vez. Más aún, si ese procesador tuviese un pipeline suficientemente grande como para contener varias instrucciones en la cola de ejecución, notaríamos que todas las instrucciones son de un solo programa.
Ahora tomemos un período de tiempo, digamos un segundo, en ese tiempo el procesador debe cambiar de un programa a otro para que se ejecute un pedacito de cada uno y crear la ilusión de que el conjunto se ejecuta de forma concurrente, como si cada programa tuviese un procesador para él solo. Es posible que en ese tiempo el procesador haya cambiado muchas veces de programa, pero si el usuario o sistema conectado a él no nota que sus intereses se han afectado, el objetivo fundamental de la multiprogramación se ha cumplido: crear la ilusión de que el sistema de cómputo tiene varios procesadores, uno por cada programa en ejecución.
Hay sistemas con más de un procesador trabajando de conjunto para ejecutar instrucciones de varios programas, a ese método de ejecución de código en un sistema computacional se le conoce como paralelismo, mientras que a los sistemas con un solo procesador se les conoce como de ejecución seudo paralela.
Como los seres humanos somos muy malos siguiendo la pista a un sistema que cambia constantemente de un lugar a otro, y a veces eso nos trae problemas de cabeza, es que se ha inventado un modelo que permite programar código que pueda ser ejecutado de forma paralela o seudo paralela: el modelo de proceso.
El modelo de procesos:Normalmente se considera que un proceso es un programa en ejecución. Sin embargo eso sería limitar el concepto de proceso al programa que se está ejecutando en el instante en que se mira a la CPU.
Más bien un proceso es un programa al que le ha sido asociado un conjunto de datos que crea una CPU virtual. Eventualmente el proceso irá a parar a la CPU, se ejecutará un poco de código y volverá a ser suspendido hasta que le toque nuevamente la CPU, pero cuando el proceso no se está ejecutando en el procesador, su conjunto de datos asociado mantiene el estado del proceso como si estuviese ejecutándose.
Con este modelo podemos seguir la pista a nuestros programas sin preocuparnos mucho por la forma en que la CPU conmuta o cambia de contexto de un proceso a otro, es como si tuviésemos a cada programa ejecutándose en su propia computadora.
Comprender este concepto es muy importante, porque a partir de ahora si un programa se convierte en proceso, además del código del programa (escrito por el programador), el SO le asignará un
registro de control y estado, de modo que el proceso tenga su CPU virtual. Y usted como programador que utiliza las funcionalidades del SO para escribir su código, deberá conocer este detalle si quiere que sus código sea productivo y seguro.
Estados de procesoYa vimos que a cada programa que se promueve a proceso le es asignado un
registro de control y estado. Este registro puede tener varios campos y cada campo puede tomar varios valores, muchas veces al programador no le interesan cuales son los campos de este registro y cuales sus valores porque son de uso interno del SO, pero hay un conjunto reducido de ellos que sí es necesario conocer.
Dentro de los campos del registro de proceso uno de los más importantes para el programador es el de
estado del proceso. Este campo sirve para conocer en que situación está el proceso y permite determinar hacia que nuevo estado puede dirigirse éste.
Los estados que puede tomar un proceso son básicamente los siguientes:
- En ejecución
- Listo
- Suspendido o bloqueado
En ejecución: corresponde al proceso que en el momento presente está ejecutando el procesador real el sistema.
Listo: el proceso está listo para ser ejecutado. Este estado permite que el SO pueda planificar al proceso para ser ejecutado en algún momento posterior.
Suspendido o bloqueado: los procesos van a parar a este estado porque deben esperar a que alguna condición lógica se cumpla para poder continuar ejecutándose, las condiciones pueden ser muchas: esperar por una E/S, a que transcurra algún tiempo, entre otras.
Además de los estados existen un conjunto de transiciones entre los diferentes estados:
- Ejecución -> Suspendido o bloqueado
- Ejecución -> Listo
- Listo -> Ejecución
- Bloqueado -> Listo
Ejecución -> Suspendido o bloqueado: ocurre cuando un proceso debe esperar por una condición lógica, en este caso como el proceso no puede continuar ejecutándose, lo más lógico es que el procesador se dedique a ejecutar otros procesos que estén listos para ejecutarse.
Ejecución -> Listo: ocurre en los sistemas de tiempo compartido (preentive), en el cual un proceso que estaba ejecutándose es interrumpido porque se terminó su tiempo (slice) de ejecución y debe esperar a que lo vuelvan a planificar para ejecutarse. En este caso el proceso no pasa al estado de bloqueado porque no existe nada que le impida continuar, excepto que el procesador real del sistema debe ejecutar otro proceso para que se cumpla el objetivo de la multiprogramación.
Listo -> Ejecución: a un proceso que estaba listo para ejecutarse le es cedido el procesador
Bloqueado -> Listo: la condición lógica que mantenía al proceso en este estado se ha cumplido y el proceso ya puede ser planificado para ejecutarse.
Ahora he introducido un nuevo elemento en el asunto: la planificación de procesos. Este es un tema importante, aunque a nosotros no nos interesa mucho, porque no diseñamos ningún SO sino que simplemente lo utilizamos.
El planificador de procesos es un componente del SO que básicamente se encarga de revisar la lista de procesos que están listos para ejecutarse y los pone en una cola de la que otro componente del SO, llamado despachador o dispatcher, los va tomando y poniéndolos en ejecución.
En un SO puede haber otros componentes encargados de la E/S, la comunicación entre procesos, etc. pero con lo que hemos visto es suficiente para seguir avanzando en nuestro curso.
Los procesos con el RTOS de CCSPara el RTOS de CCS, los procesos se nombran tareas y es por eso que durante el curso he utilizado indistintamente el término tarea o proceso. Tal y como hemos visto hasta hoy, cuando el RTOS crea una tarea le asigna un registro mediante el cual el RTOS puede controlar la ejecución de la tarea y conocer su estado. Para el programador esta información no está disponible porque realmente no debe acceder directamente a ella, pero como veremos un poco más adelante si se trabaja con esto registros, por supuesto, a través de las funciones que ofrece el propio RTOS.
Para comprender esto mejor veamos cada una de las funciones y directivas del RTOS y el impacto que cada una tiene sobre las tareas:
#TASK:Con esta directiva simplemente le decimos al compilador que la función que viene a continuación será una tarea. En el momento de la compilación le será creado el
registro de control y estado y se marcará como
lista, si la tarea tiene cola se reserva memoria para la cola.
Una tarea no será tenida en cuenta como si fuese una función, por lo tanto no podrá llamar a una “función tipo tarea” como lo hace con cualquier otra función, las tareas son como programas completamente independientes que tienen asociado un
registro de control y estado como si tuviese su propio procesador.
Tampoco debe implementarse una tarea como si fuese una función tipo main(), ya que la tarea ejecutará el código desde que comienza hasta que termina, se marca con el estado
listo y luego el procesador es cedido al RTOS para que el despachador ponga otra tarea en ejecución. Si la tarea se mantiene en el estado
listo, entonces cuando le toque nuevamente su turno en la cola de despachos volverá a ser ejecutada desde el principio, como si hubiese un lazo infinito. Hágase la idea que en vez de un main() con un lazo infinito, tenemos tantos main() como tareas hayamos definido, que se ejecutan de acuerdo a un plan de ejecución.
RTOS_AWAIT( ) y RTOS_WAIT( ):Estas dos funciones permiten pasar a una tarea del estado
en ejecución al estado
bloqueado si las condiciones lógicas que permiten continuar ejecutando código no se cumplen y permanecerán en el estado
bloqueado hasta que el RTOS detecte que puede continuar ejecutándose las pase al estado
listo. Nunca el programador puede pasar una tarea del estado
bloqueado a
listo, esto es asunto del RTOS.
RTOS_DISABLE( )Permite al programador, que desde otra tarea, se cambie el estado de una tarea de listo para
deshabilitada (un estado nuevo de este RTOS), es necesario utilizar la función
RTOS_ENABLE( ) para volver a conmutar del estado
deshabilitada al estado anterior en que se encontraba la tarea. Una tarea no puede auto deshabilitarse, este cambio debe hacerse desde una tarea diferente.
RTOS_YIELD( ):Cede el procesador explícitamente y pasa del estado
en ejecución al estado
listo. La otra forma en que con este RTOS se hace esa transición es cuando se termina el código de la tarea. Cuando le vuelva a corresponder el turno de ejecución a esta tarea, el despachador la volverá poner en ejecución en la línea posterior al yield().
Uff, bueno, hasta aquí por hoy, creo que es más que suficiente. Espero que las condiciones de competencia no vuelvan a afectar a la tarea Foro y pueda escribirles pronto.
El próximo tema será: El problema del productor-consumidor, uno de los más famosos problemas tipo de la programación con SO.
Un saludo: Reinier