Procesos

per Victor Carceler darrera modificació 2020-03-25T16:30:30+02:00

De forma intuitiva podemos considerar que un proceso es un programa en ejecución. Si en un sistema informático se están ejecutando 3 programas diferentes: un navegador, un compilador y un editor, mientras dure su ejecución por cada programa existirá un proceso. Un sistema operativo multitarea intenta dedicar atención a todos los procesos para que su ejecución avance en paralelo, aunque sea de un modo aparente.

Normalmente existirán más procesos listos para ejecutar que CPUs reales. Como una CPU, en un instante dado, sólo ejecuta un proceso, el sistema operativo ha de repartir la atención de las CPUs entre los procesos listos para ejecutar. A la técnica de alternar la ejecución de diferentes procesos en una misma CPU se la llama tiempo compartido. A la decisión de qué proceso pasa a ejecutar en una CPU, y durante cuanto tiempo, se la llama planificación.

Realmente sólo un programa sencillo genera un proceso. Cualquier aplicación de usuario que quiera realizar varias acciones de manera simultánea (por ejemplo: atender la interfaz de usuario y manejar las conexiones de red) puede crear varios procesos que se ejecutan en paralelo. Además un mismo programa puede ser ejecutado varias veces de manera simultánea, por un mismo usuario o por diferentes usuarios del sistema. En cada caso el sistema operativo creará los procesos que correspondan a la múltiple ejecución del programa.

En definitiva, un proceso es una tarea que el sistema operativo puede planificar para ejecutar en una CPU.

Características de los procesos

Aunque los detalles de implementación son diferentes en cada sistema operativo. Desde un punto de vista teórico, asociado con los procesos podemos encontrar:

Segmentos o memoria asignada
Memoria asignada para la ejecución de un proceso. Normalmente segmento de código, segmento de datos y pila.
Estados
Un proceso va cambiando de estado durante su existencia. Algunos estados posibles son: nuevo, listo para ejecutar, en ejecución, bloqueado y terminado.
Estructuras de control del SO
El sistema operativo debe seguir la pista a los diferentes procesos: qué recursos tiene asignado cada proceso, a qué usuario pertenece, caracteristicas que le permitan tomar una decisión de planificación óptima...

Segmentos o memoria asignada

Segmentos de un proceso
Segmento de código, datos y pila

Si en un sistema informático tres usuarios cargan, una vez cada uno, un mismo editor de textos. Suponiendo que el editor sólo necesita de un proceso para ejecutarse, el sistema operativo contará con tres procesos para la triple ejecución del editor.

 

Parece claro que en el sistema se reservará, para cada proceso, memoria para almacenar las instrucciones del  programa editor y para los datos que está editando el usuario. También será necesario en cada proceso tener un espacio de memoria llamado pila, en el que se registrará el contexto actual antes de hacer un salto a una función, para retomarlo cuando esta termine.

Así cada proceso contará con su propio segmento de código, segmento de datos y pila.

Es responsabilidad de la gestión de memoria del sistema operativo ubicar estos segmentos en memoria para cada proceso. En los sistemas operativos modernos se utiliza memoria virtual, y estos segmentos no tienen porqué residir (completamente) en memoria principal. Tampoco se trata de bloques contíguos de direcciones de memoria físicas, pero desde el punto de vista lógico los segmentos están disponibles y probablemente estén formados por áreas de memoria contígua.

Volviendo al ejemplo de los tres editores, parece evidente que mientras cada usuario edita un texto diferente, por tanto el segmento de datos tiene un contenido diferente en cada caso, los tres están utilizando el mismo editor, y por lo tanto el segmento de código tiene el mismo contenido, las instrucciones del editor, en los tres casos.

Aunque cada proceso ve su propio segmento de código, puede que físicamente sólo haya un segmento de código en memoria para los tres procesos, así se evita consumir memoria para guardar información redundante. En un SO moderno puede que sólo exista un segmento de código común, y compartido, para los tres procesos. Sin embargo, en este ejemplo no tiene sentido compartir los segmentos de datos y pila pues son diferentes en los tres procesos.

Compartir áreas de memoria es un método de comunicación entre procesos.

Imagen de un proceso en Unix

Según William Stallings en el libro Sistemas Operativos, la imagen de un proceso en Unix está formada por:

 

Contexto del usuario
Incluye: Texto del proceso (las instrucciones ejecutables), Datos del proceso (segmento de datos), Pila del usuario (argumentos, variables locales y punteros de las funciones), Memoria compartida (utilizada para la comunicación interproceso).
Contexto de los registros
Incluye: Contador de programa, Registro de estado del procesador, Puntero de pila, Registros de propósito general
Contexto del sistema
Incluye: Entrada en la tabla de procesos, Area U (de usuario), Tabla de regiones de preproceso y Pila del núcleo

 

 

 

Estados

Mientras existe, es decir desde que se crea hasta que se destruye, un proceso pasa por diferentes estados. El planificador del SO en función de las necesidades del proceso y de la disponibilidad de recursos va actualizando el estado de cada proceso. Un modelo de estados extremadamente reducido es el de dos estados:

modelo2estados.png

 

En este caso un proceso sólo puede encontrarse en dos estados diferentes:

  1. No ejecución: Está espearando la disponibilidad de una CPU o bien no necesita la atención de ninguna CPU porque está esperando algún suceso (por ejemplo una operación de entrada/salida).
  2. Ejecución: El proceso se está ejecutando en alguna CPU.

Mientras que en el estado de no ejecución puede haber varios procesos, en el estado de ejecución sólo habrá un proceso por cada CPU instalada (normalmente una).

En este modelo pueden ocurrir las siguientes transiciones de estado:

Entrar
Un proceso nuevo, recién creado, pasa al estado de no ejecución. Y muy probablemente esté preparado para ejecutar tan pronto como haya una CPU disponible.
Expedir/Planificar
Cuando hay una CPU disponible el SO toma uno de los procesos que están esperando en no ejecución y lo pasa al estado de ejecución.
Interrumpir
Un proceso que está en ejecución pasa a no ejecución. Las razones para esta transición son múltiples: el proceso ha estado en ejecución durante un tiempo máximo, algún proceso de mayor prioridad demanda la CPU, el proceso decide pasar voluntariamente a no ejecución, el proceso inicia una operación de entrada/salida.
Salir
El proceso termina y deja de existir

Modelo con estado Listo y Bloqueado

Un modelo más detallado puede incluir 5 estados:

 

modelo_5_estados.png

 

Las novedades frente al modelo anterior son:

 

  1. Cuando se crea un nuevo proceso, este debe ser admitido por el SO. El SO puede rehusar pasar el nuevo proceso al estado de listo al proceso si no cuenta con suficientes recursos.
  2. El anterior estado de no ejecución se divide en listo y bloqueado. De esta manera se distingue entre aquellos procesos que no se están ejecutando porque no hay una CPU disponible (estado listo) y los que están esperando que ocurra algún suceso (estado bloqueado).
  3. Cuando un proceso completa su ejecución el SO libera sus recursos y posiblemente realiza algunas operaciones contables como tomar nota de los recursos que utilizó. En algunos sistemas se paga por el tiempo de CPU consumido.

Modelo de estados de Unix

Si nos fijamos en el modelo de estados de Unix, vemos que aparecen muchos estados nuevos y sus correspondientes transiciones:

Modelo de estados de los procesos en Unix

El significado de los estados es:

 

1 - Ejecución modo usuario
El proceso está siendo ejecutado por una CPU. En un momento dado sólo puede haber tantos procesos en ejecución como CPUs físicas tenga la máquina. Además la CPU está funcionando en modo usuario, es decir que está ejecutando código del espacio de usuario. Si el código está mal escrito y se produce un error, la aplicación terminará.
2 - Ejecución modo núcleo
El proceso está siendo ejecutado por una CPU, y la CPU está funcionando en modo núcleo. Sólo el kernel del SO se ejecuta en modo núcleo. Cuando una CPU funciona en modo núcleo o modo supervisor, permite realizar algunas operaciones que están prohibidas en modo usuario. De esta manera el núcleo del sistema operativo puede controlar todo el sistema informático. Si el código está mal escrito y se produce un error probablemente todo el sistema se detenga.
3 - Listo para ejecutar y en memoria
Un proceso está listo para ejecutar cuando está disponible para ejecutar en cuanto sea asignado a alguna CPU. Además este proceso está cargado en memoria principal, de manera que puede ser ejecutado en cuanto quede una CPU disponible.
4 - Dormido y en memoria
Un proceso dormido está esperando que ocurra determinado suceso: le llegue una señal de algún otro proceso, se complete una operación de entrada/salida, transcurra determinado tiempo... Es decir, de momento no precisa la atención de ninguna CPU. Además, el proceso está cargado en memoria principal.
5 - Listo para ejecutar y descargado
Un proceso que estaba ocurriendo determinado suceso ya está preparado para ejecutarse, pero aún necesita ser cargado en memoria principal antes de que pueda ser planificado en alguna CPU.
6 - Dormido y descargado
El proceso no necesita la atención de ninguna CPU porque está esperando algún suceso externo, y el planificador del SO ha decidido descargarlo de la memoria principal (a disco) para que la memoria principal liberada pueda agilizar el funcionamiento de otros procesos.
7 - Expulsado
Básicamente es un proceso que abandona la ejecución en modo núcleo antes de ser planificado para la ejecución en modo usuario. A efectos de planificación aquellos procesos que están en expulsado y los que están en listo para ejecutar y en memoria forman una sola cola.
8 - Creado
Un nuevo proceso puede pasar al estado de listo para ejecutar y en memoria o bien al de listo para ejecutar y descargado en función de la memoria disponible.
9 - Zombie
Cuando un proceso termina, el SO libera todos los recursos que tenía asociados excepto la estructura de control que guarda los datos del proceso (código de retorno, tiempo de ejecución...) esperando a que el proceso padre la recoja. Si un proceso termina y el proceso padre no recoge estos datos, el proceso que ha terminado se queda en el estado zombie. En un sistema que funciona correctamente no debería haber procesos en este estado durante un tiempo prolongado.

Estructuras de control del SO

Según William Stallings en el libro Sistemas Operativos, las entradas en la tabla de procesos de Unix tienen:

Estado del proceso
Estado actual del proceso
Punteros
Al área de usuario y a los segmentos de código, datos y pila
Tamaño del proceso
Permite que el SO sepa cuánto espacio asignar al proceso
Identificadores de usuario
El ID real de usuario dueño del proceso. Y el ID efectivo que sirve para que un proceso disponga temporalmente de privilegios asociados
Identificadores de proceso
ID del proceso (PID) y del proceso padre (PPID)
Descriptor del suceso
Válido cuando el proceso está dormido cuando se produce el suceso, el proceso pasa a listo para ejecutar
Prioridad
Empleada en la planificación del proceso
Señal
Enumera las señales enviadas al proceso y que aún no ha tratado
Temporizadores
Incluye el tiempo de ejecución del proceso, el uso de los recursos del núcleo y un temporizador de usuario empleado para enviar señales de alarma al proceso
Enlace-P
Puntero al siguiente proceso en la cola de listos
Estado de la memoria
Indica si la imagen del proceso está en memoria principal o se ha descargado a disco. También indica si el proceso se puede descargar o está temporalmente bloqueado en memoria principal

Y las entradas en el  Área U (usuario) de Unix:

 

 

Puntero a la tabla de procesos
Indica la entrada que corresponde al área U
Identificadores de usuario
Los IDs de usuario reales y efectivos
Temporizadores
Registro del tiempo que el proceso (y sus descendientes) dedicaron ejecutando en modo usuario y en modo núcleo
Vector de tratamiento de señales
Para cada tipo de señal definida en el sistema, indica cómo reacionará el proceso al recibirla (terminar, ignorar, ejecutar una función especificada por el usuario)
Terminal de control
Indica el terminal de conexión para el proceso (si procede)
Campo de error
Registra los errores producidos durante una llamada al sistema
Valor de retorno
Contiene el resultado de las llamadas al sistema
Parámetros de E/S
Describen la cantidad de datos a transferir, la dirección origen (o destino) del vector de datos del espacio de usuario y los desplazamientos en los archivos para la E/S
Parámetros de archivos
El directorio actual y el directorio raíz actual describen el entorno del sistema de archivos para el proceso
Tabla de descriptores de los archivos del usuario
Registra los archivos que el proceso tiene abiertos
Campos de límites
Restringe el tamaño del proceso y el tamaño de un archivo en que éste puede escribir
Campos de modos de protección
Máscara con los modos de protección de los archivos creados por el proceso

 

Planificación de tareas

El planificador (en inglés scheduler) es el elemento del SO que organiza la ejecución de los procesos. Es capaz de expulsar a un proceso en ejecución y decidir qué proceso se debe ejecutar a continuación y durante cuanto tiempo. La función del planificador se apoya en el hardware, ya que se utilizan las interrupciones para lanzar su ejecución.

Las dos razones más comunes para que el planificador entre en acción son:

  • El proceso en ejecución ha consumido su lapso de tiempo asignado (cuanto de tiempo). El temporizador lanza una interrupción.
  • El proceso queda bloqueado por una operación de I/O.

Cada vez que se interrumpe la ejecución de un proceso se guarda su contexto. El contexto es toda la información que se necesita para reanudar posteriormente su ejecución (contador de programa, registros de la cpu...). Después se carga el contexto del nuevo proceso y se continúa su ejecución.

Las decisiones de planificación se toman a diferentes niveles:

Planificación a corto plazo
Resuelve las decisiones más inmediatas sobre el proceso a ejecutar. Es el planificador que se ejecuta con más frecuencia, de manera que es importante que sea ligero, en cuanto a la sobrecarga que impone al sistema, y no se lo piense demasiado antes de decidir.
Planificación a medio plazo
Intenta ajustar el número de procesos cargados a la cantidad de recursos disponibles. Típicamente decide qué procesos se cargan y qué procesos se descargan. La frecuencia de ejecución de este planificador es menor que la del de corto plazo.
Planificación a largo plazo
Típicamente decide si es posible, o no, aceptar los nuevos procesos.

 

 

Políticas de planificación

Existen diferentes políticas de planificación que se pueden utilizar, normalmente combinadas, para conseguir los objetivos perseguidos.

Entre  los objetivos a alcanzar podemos encontrar:

Objetivos:Significado:
Tiempo de respuesta
Tiempo transcurrido desde que se emite una solicitud hasta que se comienza a recibir la respuesta
Tiempo de retorno
Tiempo transcurrido desde el lanzamiento de un proceso y su fin
Plazos Si el sistema operativo permite especificar plazos de terminación, el planificador deberá maximizar el porcentaje de plazos cumplidos
Previsibilidad Un mismo trabajo debe ejecutarse siempre en el mismo tiempo aproximado (independientemente de la carga del sistema)
Productividad Número de procesos terminados por unidad de tiempo
Utilización del procesador
Porcentaje de tiempo en el que el procesador está trabajando
Equidad Igual trato para los procesos, no inanición
Prioridades Los procesos de mayor prioridad deben obtener un mejor trato
Equilibrio de recursos
Se debe repartir la carga entre los diferentes recursos

Algunos criterios son incompatibles con otros, por ejemplo, no es posible minimizar el tiempo de respuesta y el tiempo de retorno. En función del uso al que se vaya a destinar el sistema informático, cada uno de los objetivos cobrará un valor diferente.

Políticas de planificación

Algoritmo:Descripción:Comentarios:
FCFS o FIFO
Los procesos listos para ejecutar están en una cola,  cuando hay una CPU disponible
el más antiguo de la cola pasa a ejecución hasta que termina o se bloquea.
Penaliza procesos cortos y con E/S
Round Robin Turno rotatorio. Cuando un proceso se ha ejecutado durante un tiempo determinado
se expropia y pasa a ejecución el siguiente.
Equidad, a menor cuanto mejor tiempo
de respuesta y peor productividad
Realimentación Se ajusta la prioridad dinámicamente. Intenta favorecer a los procesos cortos

 

Procesos y hebras

En muchos sistemas operativos, y algunos lenguajes de programación como Java, aparece el concepto de hebra (Thread) como sinónimo de proceso. Una hebra (o hilo) de ejecución es similar a un proceso en cuanto a que puede ser planificada para ejecución. Un programa multihebra puede realizar varias tareas simultáneamente, las hebras, como los procesos, dan soporte a la programación concurrente.

Pero a diferencia de los procesos, las hebras no tienen todas las estructuras de control que las permiten un funcionamiento independiente. Normalmente la asignación de recursos no se realiza por hebra, sinó por proceso, y en un mismo proceso puede haber varias hebras de ejecución.

Mientras los procesos proporcionan un mayor aislamiento e independencia, las hebras ofrecen un mayor rendimiento pues los cambios de contexto son menos costosos. Eso sí, si en un proceso hay varias hebras, y por alguna razón el proceso termina, todas las hebras se ven afectadas.

El soporte para hebras puede implementarse de diferentes maneras, por ejemplo:

Hilos:ProcesosDescripciónSistemas de ejemplo
1:1 Por cada hebra de ejecución hay un proceso, con sus propios recursos
y espacio de direcciones. Una hebra es un proceso.
Unix System V
M:1 Cada proceso cuenta con su espacio de direcciones y sus recursos, en
un proceso pueden existir varias hebras. Todas las hebras comparten
los recursos asignados al proceso.
OS/2, MVS, MACH,
GNU/Linux
1:M Una hebra puede emigrar de un proceso a otro. Por lo tanto puede migrar
entre diferentes sistemas (cluster).
Ra
M:M Combina los casos M:1 y 1:M TRIX

 

Procesos en Unix

El modelo de procesos de Unix es complejo, pero sus características más evidentes son:

CaracterísticaDescripción:
Identificación PID: Identificador de proceso
PPID: Identificador de proceso padre
Usuario UID: Identificador de usuario
EUID: Identificador de usuario efectivo
Prioridad Prioridad combinada: realimentación automática + valor nice ajustado por el usuario
El valor nice [-20,20] normal es 0. El usuario puede empeorar el valor nice
pero sólo root puede mejorarlo.
Memoria
Gestión de memoria virtual:
VIRT: Tamaño virtual, RES: Espacio residente, SHR: Espacio compartido, SWAP: Descargado
Recursos La asignación de recursos (ficheros, dispositivos, conexiones de red) se realiza por proceso
Creación
La llamada al sistema fork() permite que un proceso padre se bifurque en dos procesos
identicos, salvo por el PID y PPID.

Control de procesos y tareas desde el shell

Para el shell cada sentencia a ejecutar constituye una tarea, aunque una tarea pueda estar formada por varios procesos.

Por ejemplo, si ejecutamos:

ls
Mientras dura la ejecución de 'ls' hay un proceso en ejecución
ls | grep A*Z
Mientras dura la ejecución de la tarea hay dos procesos en ejecución
mozilla-firefox
Una tarea, el navegador, puede crear varios procesos para gestionar las descargas o para mantener varias ventanas abiertas

Algunos comandos de utilidad relacionados con los procesos son:

Comando:Función:
ps Muestra información sobre los procesos:
ps
ps fax
ps faxl
ps -U <usuario>
top Monitor del sistema. Otros monitores: atop, htop, iftop. Otras herramientas de monitorización: sar, munin.
uptime Muestra la carga media del sistema
kill
killall
Envian una señal a un proceso , o a todos los procesos de determinado comando.
Por defecto envian la señal de terminar (SIGTERM), pero también se pueden utilizar
para enviar la señal de muerte (SIGKILL) o cualquier otra.
nice
renice
Lanzan un proceso con un valor de nice (de -20 a 19) o bien ajustan el valor nice de
un proceso creado.
nohup
Lanza un proceso con su salida desconectada del terminal. Útil si se quiere lanzar
un proceso que siga vivo después de cerrar el terminal que lo lanzó.

Primer y segundo plano en el shell

El shell soporta dos planos de ejecución, en primer plano (foreground) está el proceso que se ejecuta de manera interactiva conectado a nuestro terminal (salida y entrada). En segundo plano (background) se encuentran todos los procesos que se ejecutan sin acaparar nuestra entrada.

Cuando se esribe una tarea en el shell, el proceso correspondiente se lanza en primer plano y el shell queda ocupado (no procesa nuestra entrada) hasta que termina. Si se quiere lanzar una tarea que tardará en completarse, se puede lanzar en segundo plano utilizando el símbolo & al final de la sentencia.

Ejemplo:

vcarceler@localhost ~]$ kwrite fichero 2>/dev/null &
[1] 4983
[vcarceler@localhost ~]$
[1]+ Done kwrite fichero 2>/dev/null
[vcarceler@localhost ~]$

La ejecución de kwrite hubiese ocupado el terminal desde el que se lanzó si no se hubiese especificado el &. Al lanzar el proceso en segundo plano, el shell nos indica: El número de trabajo o tarea en segundo plano (1), y el PID del nuevo proceso (4983). Cuando concluye la ejecución de kwrite, el shell nos indica que la tarea (1) se ha completado.

Los comandos internos del shell fg y bg permiten controlar la ejecución en primer y segundo plano. El comando interno jobs muestra la lista de tareas lanzadas desde el shell Bash. Las combinaciones de teclas CTRL-C y CTRL-Z permiten terminar y abortar y terminar el trabajo en primer plano.

Comando:Función:
fg
fg %<id trabajo>
Pasa a primer plano el trabajo por defecto o el trabajo especificado
bg
bg %<id trabajo>
Reanuda en segundo plano la ejecución del trabajo
jobs
Muestra la lista de trabajos lanzados desde el shell. El símbolo +
acompaña al último trabajo parado y el - al penúltimo.
CTRL C
Termina el trabajo en primer plano
CTRL Z
Detiene, sin terminar, el proceso en primer plano

Más información: