Sistemas Operativos: Tres Piezas Sencillas. Parte 2: Abstracción: Proceso (traducción)

Introducción a los sistemas operativos

¡Hola, Habr! Quiero presentar a su atención una serie de artículos traducidos sobre una literatura que me parece interesante: OSTEP. Este material examina a fondo el funcionamiento de los sistemas operativos similares a Unix, es decir, el manejo de procesos, varios planificadores, memoria y otros componentes similares que conforman un sistema operativo moderno. Puede ver el origen de todos los materiales aquí aquí. Por favor, tengan en cuenta que la traducción se realizó de manera no profesional (bastante libre), pero espero haber mantenido el sentido general.

Los trabajos de laboratorio sobre este tema se pueden encontrar aquí:

Otras partes:

Y también pueden visitar mi canal en Telegram =)

Consideremos la abstracción más fundamental que el sistema operativo proporciona a los usuarios: el proceso. La definición de proceso es bastante simple: es un programa en ejecución. El programa en sí es una entidad inerte, que reside en el disco: un conjunto de instrucciones y posiblemente algunos datos estáticos, esperando el momento de su ejecución. Es el sistema operativo el que toma esos bytes y los ejecuta, transformando el programa en algo útil.
Con mayor frecuencia, los usuarios desean ejecutar más de un programa a la vez; por ejemplo, puede iniciar un navegador, un juego, un reproductor multimedia, un editor de texto, entre otros, en su computadora portátil. De hecho, un sistema típico puede ejecutar decenas y cientos de procesos simultáneamente. Este hecho hace que el sistema sea más fácil de usar; nunca tiene que preocuparse de si la CPU está libre, simplemente inicia los programas.

De aquí surge el problema: ¿cómo proporcionar la ilusión de múltiples CPUs? ¿Cómo puede un sistema operativo crear la ilusión de prácticamente un número infinito de CPUs, incluso si tiene solo una CPU física?

El sistema operativo crea esta ilusión mediante la virtualización de la CPU. Ejecutando un proceso, luego deteniéndolo, ejecutando otro proceso y así sucesivamente, el sistema operativo puede mantener la ilusión de que existen múltiples CPUs virtuales, aunque en realidad solo hay uno o varios procesadores físicos. Esta técnica se llama división del tiempo de recursos de la CPU. Esta técnica permite a los usuarios ejecutar tantos procesos simultáneamente como deseen. El costo de esta solución es el rendimiento, ya que si varios procesos comparten la CPU, cada proceso se procesará más lentamente.
Para realizar la virtualización de la CPU, y especialmente para hacerlo bien, el sistema operativo necesita tanto soporte de bajo nivel como de alto nivel. El soporte de bajo nivel se denomina mecanismos — son métodos o protocolos de bajo nivel que implementan la parte necesaria de la funcionalidad. Un ejemplo de esta funcionalidad es el cambio de contexto, que permite al sistema operativo detener un programa y ejecutar otro en el procesador. Esta división temporal se realiza en todos los sistemas operativos modernos.
En la parte superior de estos mecanismos se encuentra cierta lógica integrada en el sistema operativo, en forma de "políticas". Política — es un algoritmo de toma de decisiones que el sistema operativo emplea. Tales políticas, por ejemplo, deciden qué programa debe ejecutarse (de la lista de comandos) primero. Así, por ejemplo, esta tarea será resuelta por una política denominada política de planificación (scheduling policy) y al elegir una solución se basará en datos como: la historia de ejecución (qué programa ha estado en ejecución más tiempo en el último minuto), qué carga está generando este proceso (qué tipos de programas se han ejecutado), métricas de rendimiento (si el sistema está optimizado para la interacción interactiva o para capacidad de procesamiento), etc.

Abstracción: proceso

La abstracción de un programa en ejecución que realiza el sistema operativo es lo que llamamos proceso. Como ya se mencionó anteriormente, un proceso es simplemente un programa en ejecución, en un instante dado. Un programa que nos permite obtener información resumida de diversos recursos del sistema, y a los cuales accede o que esta programa afecta durante su ejecución.
Para comprender los componentes de un proceso, es necesario entender los estados del sistema: qué puede leer o modificar un programa durante su funcionamiento. En cada momento, es necesario entender qué elementos del sistema son importantes para la ejecución del programa.
Uno de los elementos más evidentes del estado del sistema que incluye un proceso es la memoria. Las instrucciones se almacenan en la memoria. Los datos que el programa lee o escribe también se encuentran en la memoria. Así, la memoria que el proceso puede direccionar (el llamado espacio de direcciones) es parte del proceso.
También forman parte del estado del sistema los registros. Muchas instrucciones están diseñadas para cambiar el valor de los registros o leer su valor, por lo que los registros también se convierten en una parte importante del funcionamiento del proceso.
Cabe destacar que el estado de la máquina también se compone de algunos registros especiales. Por ejemplo, IP — puntero de instrucción — es el puntero de la instrucción que el programa está ejecutando en este momento. También hay puntero de pila y el relacionado puntero de marco, que se utilizan para gestionar: parámetros de funciones, variables locales y direcciones de retorno.
Finalmente, los programas a menudo acceden a la ROM (memoria de solo lectura). Esta información sobre “I/O” (entrada/salida) debe incluir una lista de archivos abiertos por el proceso en ese momento.

API del proceso

Para mejorar la comprensión del funcionamiento del proceso, estudiaremos ejemplos de llamadas al sistema que deben incluirse en cualquier interfaz del sistema operativo. Estas APIs están disponibles de alguna manera en cualquier SO.

● Crear (creación): El SO debe contener algún método que permita crear nuevos procesos. Cuando ingresas un comando en la terminal o inicias una aplicación haciendo doble clic en el ícono, se envía una solicitud al SO para crear un nuevo proceso y ejecutar el programa especificado.
● Eliminación: Dado que hay una interfaz para crear un proceso, el SO también debe proporcionar la posibilidad de eliminar forzosamente un proceso. La mayoría de los programas se iniciarán y finalizarán naturalmente a medida que se ejecutan. De lo contrario, el usuario querría poder terminarlos, por lo que una interfaz para detener el proceso no estaría de más.
● Esperar (espera): A veces es útil esperar la finalización de un proceso, por lo que se proporcionan algunas interfaces que ofrecen esta posibilidad.
● Control Misceláneo (gestión diversa): Además de terminar y esperar procesos, existen otros métodos de control diversos. Por ejemplo, la mayoría de los sistemas operativos ofrecen la opción de congelar un proceso (detener su ejecución por un período) y reanudarlo posteriormente (continuar su ejecución).
● Estado (estado): Existen diversas interfaces para obtener información sobre el estado de un proceso, como la duración de su ejecución o en qué estado se encuentra actualmente.

Sistemas Operativos: Tres Piezas Sencillas. Parte 2: Abstracción: Proceso (traducción)

Creación de procesos: detalles

Una de las cosas interesantes es cómo las programas se transforman en procesos. Especialmente, cómo el sistema operativo inicia y ejecuta un programa. Cómo se crea un proceso de manera específica.
En primer lugar, el sistema operativo debe cargar el código del programa y los datos estáticos en la memoria (en el espacio de direcciones del proceso). Los programas generalmente se encuentran en disco o en una unidad de estado sólido en algún formato ejecutable. Así, el proceso de carga del programa y de los datos estáticos en la memoria requiere que el sistema operativo tenga la capacidad de leer estos bytes del disco y ubicarlos en algún lugar de la memoria.

En los sistemas operativos más antiguos, el proceso de carga se realizaba de manera ansiosa (eagerly), lo que significa que el código se cargaba completamente en la memoria antes de que se iniciara el programa. Los sistemas operativos modernos lo hacen de manera perezosa (lazily), es decir, cargando trozos de código o datos solo cuando son necesarios durante la ejecución del programa.

Después de que el código y los datos estáticos se han cargado en la memoria, el sistema operativo necesita realizar algunas cosas más antes de iniciar el proceso. Se debe asignar una cierta cantidad de memoria para la pila. Los programas utilizan la pila para variables locales, parámetros de funciones y direcciones de retorno.. El sistema operativo asigna esta memoria y la entrega al proceso. La pila también puede asignarse con ciertos argumentos, específicamente llenando los parámetros de la función main(), como un array con argc y argv.

El sistema operativo también puede asignar cierta cantidad de memoria para el montón (heap) del programa. El montón se utiliza por los programas para datos que se solicitan dinámicamente.. Los programas solicitan este espacio llamando a la función. malloc() y lo liberan explícitamente llamando a la función. free(). La memoria dinámica es necesaria para estructuras de datos como: listas enlazadas, tablas hash, árboles y otros. Al principio se asigna una pequeña cantidad de memoria para la memoria dinámica, pero con el tiempo, durante la ejecución del programa, puede solicitar más memoria a través de la llamada a la API de la biblioteca malloc(). El sistema operativo está involucrado en el proceso de asignación de más memoria para ayudar a satisfacer estas solicitudes.

El sistema operativo también lleva a cabo tareas de inicialización, particularmente aquellas relacionadas con la entrada y salida. Por ejemplo, en sistemas UNIX, cada proceso tiene por defecto 3 descriptores de archivo abiertos, para el flujo estándar de entrada, salida y errores. Estos descriptores permiten a los programas leer entradas desde el terminal, así como mostrar información en la pantalla.

Así, al cargar código y datos estáticos en memoria, crear e inicializar una pila, así como realizar otras tareas relacionadas con la ejecución de operaciones de entrada y salida, el sistema operativo prepara el entorno para la ejecución del proceso. Al final, queda una última tarea: ejecutar el programa a través de su punto de entrada, llamado función main(). Al pasar a la ejecución de la función main(), el sistema operativo transfiere el control de la CPU al nuevo proceso, de esta manera, el programa comienza a ejecutarse.

Estado del proceso

Ahora que tenemos una comprensión básica de lo que es un proceso y cómo se crea, enumeremos los estados del proceso en los que puede encontrarse. En su forma más simple, un proceso puede estar en uno de estos estados:
● En ejecución. En estado en ejecución, el proceso se ejecuta en la CPU. Esto significa que se están ejecutando instrucciones.
● Listo. En estado de listo, el proceso está preparado para ejecutarse, pero por alguna razón, el sistema operativo no lo está ejecutando en ese momento.
● Bloqueado. En estado bloqueado, el proceso está realizando alguna operación que no le permite estar listo para la ejecución hasta que ocurra algún evento. Un ejemplo común es cuando un proceso inicia una operación de E/S, se bloquea y, de esta manera, otro proceso puede utilizar la CPU.

Sistemas Operativos: Tres Piezas Sencillas. Parte 2: Abstracción: Proceso (traducción)

Imaginemos estos estados como un gráfico. Como podemos ver en la imagen, el estado de un proceso puede cambiar entre RUNNING y READY a discreción del sistema operativo. Cuando el estado del proceso cambia de READY a RUNNING, significa que el proceso ha sido programado. En la dirección opuesta, se ha retirado de la planificación. En el momento en que un proceso se convierte en BLOCKED, por ejemplo, al iniciar una operación de E/S, el sistema operativo lo mantendrá en este estado hasta que ocurra algún evento, como la finalización de la E/S. En ese momento, el proceso cambiará al estado READY y posiblemente de inmediato al estado RUNNING, si así lo decide el sistema operativo.
Veamos un ejemplo de cómo dos procesos atraviesan estos estados. Para comenzar, asumamos que ambos procesos están en ejecución y cada uno utiliza solo la CPU. En este caso, sus estados se verán de la siguiente manera.

Sistemas Operativos: Tres Piezas Sencillas. Parte 2: Abstracción: Proceso (traducción)

En el siguiente ejemplo, el primer proceso, tras un tiempo de trabajo, solicita E/S y pasa al estado BLOCKED, permitiendo que el otro proceso se ejecute (VER 1.4). El sistema operativo observa que el proceso 0 no está utilizando la CPU y inicia el proceso 1. Durante la ejecución del proceso 1, la E/S finaliza y el estado del proceso 0 cambia a READY. Finalmente, el proceso 1 finaliza y, al concluir, el proceso 0 se inicia, se ejecuta y completa su tarea.

Sistemas Operativos: Tres Piezas Sencillas. Parte 2: Abstracción: Proceso (traducción)

Estructura de datos

El sistema operativo en sí es un programa y, al igual que cualquier otro, tiene algunas estructuras de datos clave que rastrean diversas piezas de información relevantes. Para seguir el estado de cada proceso, el sistema operativo mantendrá una lista de procesos para todos los procesos en estado READY y cierta información adicional para rastrear los procesos que se están ejecutando en ese momento. Además, el sistema operativo debe rastrear los procesos bloqueados. Tras la finalización de la E/S, el sistema operativo debe despertar el proceso correspondiente y cambiarlo al estado listo para ejecutarse.

Por lo tanto, el sistema operativo debe guardar el estado de los registros de la CPU. En el momento en que el proceso se detiene, el estado de los registros se almacena en el espacio de direcciones del proceso, y al reanudar su ejecución, se deben restaurar los valores de los registros y, de este modo, continuar la ejecución de este proceso.

Además de los estados ready, blocked y running, hay otros estados. A veces, en el momento de la creación, un proceso puede tener el estado INIT. Finalmente, un proceso puede entrar en el estado FINAL, cuando ya ha terminado, pero la información sobre él aún no ha sido limpiada. En los sistemas UNIX, este estado se denomina proceso zombi. Este estado es útil para casos en los que el proceso padre desea conocer el código de retorno del hijo; por ejemplo, normalmente 0 indica una finalización exitosa, y 1 indica un error, sin embargo, los programadores pueden implementar códigos de salida adicionales, señalando diferentes problemas. Al finalizar, el proceso padre realiza la última llamada al sistema, como wait(), para esperar a que el proceso hijo termine y comunicar al SO que se pueden limpiar los datos asociados con el proceso finalizado.

Sistemas Operativos: Tres Piezas Sencillas. Parte 2: Abstracción: Proceso (traducción)

Puntos clave de la lección:

● El proceso — la principal abstracción de un programa en funcionamiento en el SO. En cualquier momento, un proceso puede ser descrito por su estado: el contenido de la memoria en su espacio de direcciones, el contenido de los registros del procesador, incluyendo el puntero de instrucciones y el puntero de pila, así como información sobre IO, como archivos abiertos que se están leyendo o escribiendo.
● API del proceso consiste en llamadas que los programas pueden hacer en relación con los procesos. Normalmente, estas son llamadas para crear, eliminar u otras acciones.
● Un proceso se encuentra en uno de muchos estados, incluyendo running, ready y blocked. Eventos diversos, como la programación, la exclusión de programación o la espera, pueden cambiar el estado de un proceso de uno a otro.
● Lista de procesos contiene información sobre todos los procesos en el sistema. Cada entrada se llama bloque de control de proceso, que en realidad es una estructura que contiene toda la información necesaria sobre un proceso específico. 

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster