Introducción a los sistemas operativos
Hola Habr! Quiero llamar su atención sobre una serie de artículos-traducciones de una literatura interesante en mi opinión: OSTEP. Este artículo analiza en profundidad el trabajo de los sistemas operativos tipo Unix, a saber, el trabajo con procesos, varios programadores, memoria y otros componentes similares que componen el sistema operativo moderno. El original de todos los materiales que puedes ver
aquí . Tenga en cuenta que la traducción se realizó de manera no profesional (con bastante libertad), pero espero haber conservado el significado general.
El trabajo de laboratorio sobre este tema se puede encontrar aquí:
Otras partes:
Y puedes mirar mi canal en
telegram =)
Considere la abstracción más fundamental que el sistema operativo proporciona a los usuarios: un proceso. Definir un proceso es bastante simple: es un
programa que funciona . El programa en sí es una cosa sin vida ubicada en el disco: es un conjunto de instrucciones y posiblemente algún tipo de datos estáticos que esperan ser lanzados. Es el sistema operativo el que toma estos bytes y los inicia, convirtiendo el programa en algo útil.
Con mayor frecuencia, los usuarios desean ejecutar más de un programa a la vez, por ejemplo, puede ejecutar un navegador, un juego, un reproductor multimedia, un editor de texto y similares 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 tendrá que preocuparse por si la CPU es gratuita, simplemente ejecute los programas.
De ahí el problema: ¿cómo proporcionar la ilusión de múltiples CPU? ¿Cómo crea el sistema operativo la ilusión de una cantidad casi infinita de CPU, incluso si solo tiene una CPU física?El sistema operativo crea esta ilusión a través de la virtualización de la CPU. Al iniciar un proceso, luego detenerlo, iniciar otro proceso, etc., el sistema operativo puede soportar la ilusión de que hay muchas CPU virtuales, aunque en realidad será uno o más procesadores físicos. Esta técnica se llama
tiempo compartido de CPU . Esta técnica permite a los usuarios ejecutar tantos procesos simultáneos como deseen. El costo de dicha solución es el rendimiento, porque si la CPU es compartida por varios procesos, cada proceso se procesará más lentamente.
Para implementar la virtualización de la CPU, y especialmente para hacerlo bien, el sistema operativo necesita soporte de bajo y alto nivel. El soporte de bajo nivel se denomina
mecanismos : estos son métodos o protocolos de bajo nivel que implementan la parte necesaria de la funcionalidad. Un ejemplo de dicha funcionalidad es el cambio de contexto, que le da al sistema operativo la capacidad de detener un programa y ejecutar otro programa en el procesador. Esta división de tiempo se implementa en todos los sistemas operativos modernos.
En la parte superior de estos mecanismos hay una lógica incrustada en el sistema operativo, en forma de "políticas".
Una política es un cierto algoritmo de toma de decisiones de un sistema operativo. Tales políticos, por ejemplo, deciden qué programa ejecutar (de la lista de comandos) en primer lugar. Entonces, por ejemplo, una política llamada política de
programación resolverá este problema y al elegir una solución se guiará por datos tales como: historial de inicio (qué programa ha estado funcionando más tiempo en los últimos minutos), qué tipo de carga lleva a cabo este proceso (qué tipos de programas se lanzaron ), métricas de rendimiento (si el sistema está optimizado para la interacción interactiva o para el ancho de banda), etc.
Abstracción: proceso
La abstracción de un programa en ejecución realizado por el sistema operativo es lo que llamamos un
proceso . Como se mencionó anteriormente, un proceso es simplemente un programa de trabajo, en cualquier período de tiempo instantáneo. Un programa con la ayuda del cual podemos obtener información resumida de varios recursos del sistema, y al que este programa se dirige o que afecta durante su ejecución.
Para comprender los componentes del proceso, debe comprender el estado del sistema: que el programa puede leer o cambiar durante su funcionamiento. En cualquier momento, debe comprender qué elementos del sistema son importantes para la ejecución del programa.
Uno de los elementos obvios del estado del sistema que incluye el proceso es la
memoria . Las instrucciones están en la memoria. Los datos que el programa lee o escribe también se encuentran en la memoria. Por lo tanto, la memoria que el proceso puede abordar (el llamado espacio de direcciones) es parte del proceso.
Los registros también son parte del estado del sistema. Muchas instrucciones están destinadas a cambiar el significado de los registros o leer su significado, por lo que los registros también se convierten en una parte importante del proceso.
Cabe señalar que el estado de la máquina también se forma a partir de algunos registros especiales. Por ejemplo,
IP - puntero de instrucción - un puntero a la instrucción que el programa está ejecutando actualmente. También hay un
puntero de pila y el
puntero de cuadro asociado, que se utilizan para controlar: parámetros de función, variables locales y direcciones de retorno.
Finalmente, los programas a menudo acceden a la ROM (memoria de solo lectura). Dicha información sobre "E / S" (entrada-salida) debe incluir una lista de archivos abiertos actualmente por el proceso.
API de proceso
Para mejorar la comprensión del proceso, examinemos ejemplos de llamadas al sistema que deberían incluirse en cualquier interfaz del sistema operativo. Estas API de una forma u otra están disponibles en cualquier sistema operativo.
●
Crear : el sistema operativo debe incluir algún método que le permita crear nuevos procesos. Cuando ingresa un comando en la terminal o inicia la aplicación haciendo doble clic en el icono, se envía una apelación al sistema operativo para crear un nuevo proceso y luego iniciar el programa especificado.
●
Eliminación : una vez que hay una interfaz para crear un proceso, el sistema operativo también debe proporcionar la capacidad de forzar la eliminación del proceso. La mayoría de los programas se lanzarán y completarán de forma natural a medida que se ejecuten. De lo contrario, al usuario le gustaría poder matarlos y, por lo tanto, la interfaz para detener el proceso no será superflua.
●
Esperar : a veces es útil esperar a que se complete el proceso, por lo que se proporcionan algunas interfaces que brindan la capacidad de esperar.
●
Control misceláneo (control misceláneo): además de matar y esperar el proceso, existen varios otros métodos de control. Por ejemplo, la mayoría de los sistemas operativos brindan la capacidad de congelar un proceso (detenerlo durante un cierto período) y luego reanudarlo (continuar la ejecución)
●
Estado : existen varias interfaces para recibir información sobre el estado del proceso, como la duración de su funcionamiento o en qué estado se encuentra ahora.

Creación de procesos: detalles
Una de las cosas interesantes es cómo exactamente los programas se transforman en procesos. Especialmente cómo el sistema operativo recoge y ejecuta el programa. ¿Cómo se crea exactamente el proceso?
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 el disco o en una unidad de estado sólido en algún formato ejecutable. Por lo tanto, el proceso de cargar un programa y datos estáticos en la memoria requiere que el sistema operativo lea estos bytes del disco y los coloque en algún lugar de la memoria.
En los primeros sistemas operativos, el proceso de descarga se ejecutaba con entusiasmo, lo que significa que el código se cargó en la memoria por completo antes de que comenzara el programa. Los sistemas operativos modernos hacen esto de manera perezosa, es decir, cargando piezas de código o datos solo cuando el programa los necesita durante su ejecución.
Después de cargar el código y los datos estáticos en la memoria del sistema operativo, debe hacer algunas cosas más antes de comenzar el proceso. Se debe asignar una cierta cantidad de memoria para la pila.
Los programas usan la pila para variables locales, parámetros de función y direcciones de retorno . El sistema operativo asigna esta memoria y la entrega al proceso. La pila también se puede asignar con algunos argumentos, específicamente llena los parámetros de la función main (), por ejemplo, con la matriz argc y argv.
El sistema operativo también puede asignar una cierta cantidad de memoria para el montón del programa.
Los programas usan el montón para los datos asignados dinámicamente solicitados explícitamente . Los programas solicitan este espacio llamando a la función
malloc () y borran explícitamente llamando a la función
free () . Se necesita un montón para estructuras de datos como hojas vinculadas, tablas hash, árboles y otros. Al principio, se asigna una pequeña cantidad de memoria para el montón, pero con el tiempo, durante la operación del programa, el montón puede solicitar más memoria a través de la API de la biblioteca llamada malloc (). El sistema operativo está involucrado en el proceso de asignación de más memoria para ayudar a enfrentar estos desafíos.
El sistema operativo también realizará tareas de inicialización, en particular las relacionadas con E / S. Por ejemplo, en los sistemas UNIX, cada proceso tiene 3 descriptores de archivos abiertos de forma predeterminada, para flujos de entrada, salida y error estándar. Estos descriptores permiten que los programas lean la entrada desde el terminal, así como también muestran información en la pantalla.
Por lo tanto, al cargar el código y los datos estáticos en la memoria, crear e inicializar la pila, así como realizar otros trabajos relacionados con la realización de tareas de E / S, el sistema operativo prepara el sitio para el proceso. Al final, queda la última tarea: ejecutar el programa a través de su punto de entrada, llamado la función main (). Continuando con la función main (), el sistema operativo transfiere el control de la CPU al proceso recién creado, por lo que el programa comienza a ejecutarse.
Estado del proceso
Ahora que entendemos qué es un proceso y cómo se crea, enumeramos los estados del proceso en los que puede estar. En su forma más simple, un proceso puede estar en uno de estos estados:
●
Corriendo . En el estado de ejecución, el proceso se ejecuta en el procesador. Esto significa que las instrucciones se están ejecutando.
●
Listo . En un estado listo, el proceso está listo para comenzar, pero por alguna razón, el sistema operativo no lo ejecuta en un momento dado.
●
Bloqueado . En un estado bloqueado, el proceso realiza algunas operaciones que impiden que esté listo para su ejecución hasta que se produzca un evento. Un ejemplo común es cuando un proceso inicia una operación de E / S, se bloquea y, por lo tanto, algún otro proceso puede usar el procesador.

Puedes imaginar estos estados en forma de gráfico. Como podemos ver en la imagen, el estado del proceso puede cambiar entre EN EJECUCIÓN y LISTO a discreción del sistema operativo. Cuando el estado de un proceso cambia de READY a RUNNING, esto significa que el proceso ha sido programado. En la dirección opuesta, eliminada del diseño. En el momento en que el proceso se BLOQUEA, por ejemplo, inicializo la operación IO, el sistema operativo lo mantendrá en este estado hasta que ocurra algún evento, por ejemplo, la finalización del IO. en este momento, haga la transición al estado LISTO y posiblemente instantáneamente al estado EN EJECUCIÓN, si el sistema operativo así lo decide.
Echemos un vistazo a un ejemplo de cómo dos procesos pasan por estos estados. Primero, imagine que ambos procesos se están ejecutando y que cada uno usa solo la CPU. En este caso, sus estados se verán de la siguiente manera.

En el siguiente ejemplo, el primer proceso después de un tiempo solicita IO y entra en el estado BLOQUEADO, dando al otro proceso la capacidad de comenzar (FIG. 1.4). El sistema operativo ve que el proceso 0 no usa la CPU e inicia el proceso 1. Durante la ejecución del proceso 1: IO finaliza y el estado del proceso 0 cambia a LISTO. Finalmente, el proceso 1 se completa y, al final, el proceso 0 comienza, se ejecuta y finaliza su trabajo.

Estructura de datos
El sistema operativo en sí es un programa y, como cualquier otro programa, tiene algunas estructuras de datos clave que rastrean una variedad de datos relevantes. Para rastrear el estado de cada proceso en el sistema operativo, se admitirá una
lista de procesos determinada para todos los procesos en el estado LISTO y cierta información adicional para el seguimiento de los procesos que se están ejecutando actualmente. Además, el sistema operativo debe rastrear y bloquear procesos. Después de completar el IO, el sistema operativo debe despertar el proceso deseado y ponerlo en un estado de preparación para el lanzamiento.
Entonces, por ejemplo, el sistema operativo debe guardar el estado de los registros del procesador. En el momento en que se detiene el proceso, el estado de los registros se guarda en el espacio de direcciones del proceso y, en el momento de su continuación, es posible restaurar los valores de los registros y, por lo tanto, continuar el proceso.
Además de los estados listos, bloqueados y en ejecución, hay algunos otros estados. A veces, en el momento de la creación, un proceso puede tener un estado INIT. Finalmente, un proceso se puede colocar en el estado FINAL cuando ya se ha completado, pero la información al respecto aún no se ha borrado. En los sistemas UNIX, este estado se denomina
proceso zombie . Este estado es útil para los casos en que el proceso padre quiere saber el código de retorno del descendiente, por ejemplo, generalmente 0 indica la finalización exitosa y 1 indica un error, pero los programadores pueden crear códigos de salida adicionales, lo que indica varios problemas. Al finalizar, el proceso padre realiza la última llamada al sistema, por ejemplo, wait (), para esperar a que se complete el proceso descendiente y señalar al sistema operativo que cualquier dato asociado con el proceso completado se puede borrar.

Puntos clave de la conferencia:
●
Proceso : la abstracción principal de un programa en ejecución en el sistema operativo. En cualquier momento, un proceso puede describirse por su estado: el contenido de la memoria en su espacio de direcciones, el contenido de los registros del procesador, incluido el puntero de instrucción y el puntero de pila, también con información sobre IO, por ejemplo, archivos abiertos que se leen o escriben.
● La
API de proceso consta de llamadas que los programas pueden hacer con respecto a los procesos. Por lo general, estas son llamadas para crear, eliminar u otras.
● Un proceso se encuentra en uno de los muchos estados, incluidos en ejecución, listo, bloqueado. Varios eventos, como la planificación, la exclusión de la planificación o las expectativas, pueden traducir el estado de un proceso de uno a otro.
● La
lista de procesos contiene información sobre todos los procesos en el sistema. Cada entrada en él se llama un bloque de control de proceso, que en realidad es una estructura que contiene toda la información necesaria sobre un proceso en particular.