CONCEPTO DE PROCESO

Un proceso no es más que un conjunto de threads que ejecutan el mismo código, junto con las zonas de memoria asociadas a ellos y los ficheros que tienen abiertos.

Un programa consta, al menos, de un proceso, y un proceso, al menos, de un thread. Cuando un programa tiene varios procesos, lo normal es que cada uno ejecute un código distinto, los cuales se encuentran en ficheros ejecutables separados. Dos procesos solo pueden compartir una zona de memoria si esta es definida expresamente como tal. Así mismo, es en este caso cuando los sistemas de sincronización a la hora de compartir memoria (de los que hablaremos más adelante) se vuelven especialmente necesarios e importantes.

DosExecPgm
DosKillProcess

Los threads que componen un proceso tienen un cierto control sobre los demás, pudiendo bloquearse entre sí de forma temporal si alguno lo necesita, entre otras posibilidades.

DosEnterCritSec
DosExitCritSec

Cada vez que un thread se adueña de un recurso del sistema (un fichero o un bloque de memoria), este pasa a formar parte del proceso al que pertenece dicho thread, y accesible a todos los threads de éste, pero los threads del resto de los procesos no tienen por qué poder acceder a estos. Por supuesto, si un proceso acaba o bien si es abortado (bien voluntariamente por el usuario, bien por un error o trap), OS/2 libera automáticamente todos los recursos ocupados por éste. Sin embargo, esto no significa que no se deban liberar estos antes de finalizar los threads que componen el proceso. Es buena práctica hacerlo, aparte de que el hecho de cerrar los ficheros permite que se actualicen los datos que se han grabado en ellos.

Si se crean dos procesos a partir del mismo código, OS/2 también procede a realizar compartición de código, con el consiguiente ahorro de memoria. Sin embargo, este hecho no implica que los recursos de cada proceso sea compartido con el resto.

En el caso de los procesos, sí existe una relación padre-hijo: si un proceso crea otro, el primero se denomina padre, y el segundo, hijo. Los hijos de un proceso, así como los hijos de los hijos, etc, se llaman descendientes.

El proceso padre mantiene un control sobre toda su descendencia, de forma que puede matar a cualquier proceso perteneciente a ésta.

Cuando se crea un proceso hijo, este hereda los recursos del padre, a menos que estos fuesen adquiridos por el padre sin derecho a herencia. Esto es: si el padre ha abierto con derecho a herencia un fichero, sus hijos podrán acceder a él; en caso contrario, no podrán (a menos que lo vuelvan a abrir ellos mismos, y los privilegios de acceso lo permitan).

Por último, se puede escoger si un proceso hijo determinado se ejecuta de forma síncrona o asíncrona. En el primer caso, el proceso padre se detiene hasta que el proceso hijo ha terminado; en el segundo, ambos procesos siguen ejecutándose de forma independiente.

Prioridades de ejecución

En un S.O. multitarea, todos los procesos compiten por tener el máximo tiempo de CPU. Sin embargo, es obvio que no todos tienen la misma importancia. Por ejemplo, un programa que controle un MODEM tiene que tener prioridad sobre otro que controle una impresora, por ejemplo. Por esto existen las prioridades.

En OS/2 existen cuatro niveles de prioridad, en orden de mayor a menor:

En el nivel crítico se ejecutan aplicaciónes que necesiten una atención inmediata por parte del procesador, como sistemas de comunicaciones o en tiempo real (sonido, video,...). Dentro de este nivel existen 32 subniveles distintos de prioridad. El subnivel en el que se situa el programa es escogido por él mismo. Un ejemplo es el selector de programas del OS/2, el cual corre en el subnivel más bajo (0) del nivel crítico.

Para evitar una sobrecarga del sistema, es conveniente que los programas que se situen en nivel crítico consuman poco tiempo de CPU.

En el nivel de primer plano se ejecuta la aplicación que se encuentra en primer plano en ese momento. Solo tiene un subnivel de prioridad.

En el nivel medio se ejecutan los programas normales, como las aplicaciones, juegos, etc. Tiene 32 subniveles distintos de prioridad. El subnivel en que se situa es escogido por OS/2, y varia de forma dinámica en función de los accesos de E/S y del tiempo de CPU que consuma el programa, de forma que los más voraces tendrán menor prioridad que los menos.

Por último, en el nivel desocupado (o Idle Time) se situan aquellos programas que no necesitan una atención excesiva de la CPU. Es lo opuesto al nivel crítico. Tiene 32 subniveles de prioridad. El subnivel en el que se situa el programa es escogido por él mismo. Un ejemplo es el Spooler de Impresora, o bien un salvapantallas.

DosSetPriority

Vemos que un detalle importante a tener en cuenta en el diseño de un programa es el consumo de CPU que hace. Pero ¿como podemos hacer que nuestro programa consuma poca CPU? La respuesta es: no realizar nunca una espera activa.

Una espera activa es cuando entramos en un bucle que se limita a comprobar el estado de una variable, y solo salimos de él cuando cambia a un valor determinado. Un ejemplo es cuando queremos esperar a que el usuario pulse una tecla. Normalmente hacemos un bucle que se queda leyendo el teclado constantemente hasta que el usuario realice la pulsación. El problema es que estamos desperdiciando un tiempo que se podría aprovechar para ejecutar otras cosas. Para evitarlo, OS/2 nos suele dar la posibilidad de esperar por nosotros. Por ejemplo, cuando vimos el subsistema de teclado, podíamos pedirle a OS/2 que retornase de la llamada tanto si había una tecla pulsada como si no, o bien que no devolviese el control hasta que se pulsase. Pues bien, si le pedimos que haga la segunda opción, estaremos ahorrando ciclos de CPU. Esto es así porque OS/2 sabe que ese proceso no está listo para correr, por lo que repartirá el tiempo de CPU unicamente entre el resto de los procesos, y no volverá a dedicarle atención hasta que el usuario pulse una tecla, momento en que volverá a darle ciclos de CPU. Vemos que, de esta forma, OS/2 ahorra el tiempo que consumiríamos si nos dedicásemos a hacer una espera activa, con lo que si ejecutamos varios programas a la vez tardarán menos que la suma del tiempo que tardarían en ejecutarse uno a uno, precisamente porque aprovechamos estos tiempos muertos. Por supuesto, no solo se ahorran éstos, sino también los necesarios para mover los cabezales de los discos, escribir caracteres en la pantalla, etc.

Si hay dos procesos listos para correr, siempre obtendrá el procesador aquel que tenga mayor prioridad. Puede parecer que si un proceso siempre tiene cosas que hacer y está situado en un nivel alto, siempre se quedará con la CPU, dejando al resto de las tareas bloqueadas. Esta es la razón de que la prioridad de los procesos en el nivel medio sea dinámica: de esta forma, los procesos que consumen mucha CPU ven rebajada su prioridad, de modo que todos se ejecutan por igual. Por otro lado, existe un tiempo máximo durante el cual un proceso puede estar sin recibir ciclos de CPU. Transcurrido este, OS/2 le cambiará momentaneamente la prioridad, de modo que recibirá un intervalo de tiempo, volviendo a su prioridad original después. Esto permite que los programas de prioridad desocupada obtengan siempre ciclos de CPU, aunque haya un proceso de nivel medio que consuma mucha (por ejemplo, un RayTracer).

Pagina anterior  Indice  Pagina siguiente