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 =)
Programa de trabajo
¿Qué sucede cuando un programa funciona? Ejecutar programas hace una cosa simple: ejecuta instrucciones. Cada segundo, el procesador extrae millones e incluso posiblemente miles de millones de instrucciones de la RAM, a su vez, las decodifica (por ejemplo, reconoce a qué tipo pertenecen estas instrucciones) y las ejecuta. Esto puede ser la suma de dos números, acceso a la memoria, verificación de condiciones, cambio a funciones, etc. Después de completar una instrucción, el procesador procede a ejecutar otra. Y así, instrucción por instrucción, se ejecutan hasta que se completa el programa.
Este ejemplo se considera naturalmente simplificado: de hecho, para acelerar el procesador, el hardware moderno le permite ejecutar instrucciones fuera de turno, calcular posibles resultados, seguir instrucciones al mismo tiempo y trucos similares.
Modelo de computación de von Neumann
La forma simplificada de trabajo descrita por nosotros es similar al modelo de cálculos de Von Neumann.
Von Neumann es uno de los pioneros de los sistemas informáticos; también es uno de los autores de la teoría de juegos . Mientras se ejecuta el programa, se producen muchos otros eventos, muchos otros procesos y el trabajo de lógica de terceros, cuyo objetivo principal es simplificar el lanzamiento, la operación y el mantenimiento del sistema.
Existe un conjunto de software que es responsable de la simplicidad de ejecutar programas (o incluso de permitirle ejecutar varios programas al mismo tiempo), permite que los programas compartan la misma memoria e interactúen con diferentes dispositivos. Tal conjunto de software (software) se denomina esencialmente sistema operativo y sus tareas incluyen monitorear que el sistema funciona de manera correcta y eficiente, así como garantizar la facilidad de administración de este sistema.
Sistema operativo
Un sistema operativo, abreviatura de SO, es un complejo de programas interconectados diseñados para administrar los recursos de la computadora y organizar la interacción de un usuario con una computadora .
El sistema operativo logra su eficacia principalmente a través de la técnica más importante: la técnica de
virtualización . El sistema operativo interactúa con un recurso físico (procesador, memoria, disco y similares) y lo transforma en una forma de sí mismo más general, más potente y más fácil de usar. Por lo tanto, para una comprensión general, puede comparar el sistema operativo con una máquina virtual.
Para permitir a los usuarios dar instrucciones al sistema operativo y, por lo tanto, utilizar las capacidades de una máquina virtual (como: iniciar un programa, asignar memoria, acceder a un archivo, etc.), el sistema operativo proporciona una interfaz llamada
API (interfaz de programación de aplicaciones) y a la que Puedes hacer llamadas. Un sistema operativo típico hace posible realizar cientos de llamadas al sistema.
Y finalmente, dado que la virtualización permite que muchos programas funcionen (compartiendo así la CPU) y al mismo tiempo obteniendo acceso a sus instrucciones y datos (compartiendo así la memoria), así como accediendo a discos (compartiendo así dispositivos de E / S ), el sistema operativo también se denomina administrador de recursos. Cada procesador, disco y memoria es un recurso del sistema y, por lo tanto, una de las funciones del sistema operativo se convierte en la tarea de administrar estos recursos, hacerlo de manera eficiente, honesta o, por el contrario, dependiendo de la tarea para la cual está diseñado este sistema operativo.
Virtualización de la CPU
Considere el siguiente programa:
(https://www.youtube.com/watch?v=zDwT5fUcki4)

No realiza ninguna acción especial, de hecho, todo lo que hace es llamar a la función
spin (), cuya tarea es cambiar el tiempo y regresar después de que haya pasado un segundo. Por lo tanto, repite sin cesar la cadena que el usuario pasó como argumento.
Ejecute este programa y pase el símbolo "A" como argumento. El resultado no es muy interesante: el sistema simplemente ejecuta un programa que muestra periódicamente el símbolo "A".
Ahora intentemos la opción cuando se estén ejecutando muchas instancias del mismo programa, pero mostrando letras diferentes, para que sea más comprensible. En este caso, el resultado será ligeramente diferente. A pesar de que tenemos un procesador, el programa se ejecuta simultáneamente. ¿Cómo sucede esto? Pero resulta que el sistema operativo, no sin la ayuda de las capacidades de hardware, crea una ilusión. La ilusión de que hay varios procesadores virtuales en el sistema, convirtiendo un procesador físico en un número teóricamente infinito y permitiendo así que los programas parezcan ejecutarse simultáneamente. Tal ilusión se llama
CPU Virtualization .
Tal imagen plantea muchas preguntas, por ejemplo, si varios programas quieren comenzar simultáneamente, ¿cuál se lanzará? Las "políticas" del sistema operativo son responsables de esta pregunta. Las políticas se utilizan en muchos lugares del sistema operativo y responden preguntas similares, y también son los mecanismos básicos que implementa el sistema operativo. De ahí el papel del sistema operativo como administrador de recursos.
Virtualización de la memoria
Ahora echemos un vistazo a la memoria.
El modelo de memoria física en los sistemas modernos se representa como una matriz de bytes . Para leer desde la memoria, debe especificar la
dirección de la celda para acceder a ella. Para registrar o actualizar datos, también debe especificar los datos y la dirección de la celda donde escribirlos.
Los accesos a la memoria ocurren constantemente durante la operación del programa. Un programa almacena en la memoria toda su estructura de datos y accede a ella siguiendo varias instrucciones. Mientras tanto, las instrucciones también se almacenan en la memoria, por lo que también se accede para cada solicitud a la siguiente instrucción.
Llamar a malloc ()
Considere el siguiente programa que asigna una región de memoria usando la llamada
malloc () (https://youtu.be/jnlKRnoT1m0):

El programa hace algunas cosas. En primer lugar, asigna una cierta cantidad de memoria (línea 7), luego muestra la dirección de la celda seleccionada (línea 9), escribe cero en la primera ranura de la memoria asignada. Luego, el programa ingresa a un ciclo en el cual incrementa el valor registrado en la memoria en la dirección en la variable “p”. También muestra el identificador de proceso de sí mismo.
El ID del proceso es único para cada proceso en ejecución . Después de haber lanzado varias copias, encontraremos un resultado interesante: en el primer caso, si no hace nada y solo inicia varias copias, las direcciones serán diferentes. ¡Pero esto no cae bajo nuestra teoría! Es cierto, ya que en las distribuciones modernas la función de aleatorización de la memoria está activada por defecto. Si lo desactiva, obtenemos el resultado esperado: las direcciones de memoria de dos programas que se ejecutan simultáneamente coincidirán.
Como resultado, resulta que dos programas independientes funcionan con sus propios espacios de direcciones privadas, que a su vez son mostrados por el sistema operativo en la memoria física . Por lo tanto, el uso de direcciones de memoria dentro de un programa no afectará a otros de ninguna manera, y a cada programa le parece que tiene su propia pieza de memoria física, que está completamente a su disposición. La realidad, sin embargo, es que la memoria física es un recurso compartido administrado por el sistema operativo.
Coherencia
Otro tema importante dentro de los sistemas operativos es la
consistencia . Este término se usa cuando se trata de problemas en el sistema que pueden ocurrir al trabajar con muchas cosas al mismo tiempo dentro del mismo programa. Los problemas de consistencia surgen incluso en el propio sistema operativo. En ejemplos anteriores con memoria y virtualización del procesador, nos dimos cuenta de que el sistema operativo gestiona muchas cosas al mismo tiempo: inicia el primer proceso, luego el segundo, y así sucesivamente. Al final resultó que, este comportamiento puede conducir a algunos problemas. Entonces, por ejemplo, los programas multiproceso modernos experimentan tales dificultades.
Considere el siguiente programa:

El programa en la función principal crea dos hilos usando la llamada
Pthread_create () . En este ejemplo, un subproceso puede considerarse como una función que se ejecuta en el mismo espacio de memoria junto a otras funciones, y la cantidad de funciones iniciadas simultáneamente es claramente más de una. En este ejemplo, cada subproceso inicia y ejecuta la función
trabajador () ,
que a su vez simplemente incrementa la variable.Ejecute este programa con el argumento 1000. Como habrá adivinado, el resultado debería ser 2000, ya que cada subproceso incrementó la variable 1000 veces. Sin embargo, no todo es tan simple. Intentemos ejecutar el programa con el número de repeticiones un orden de magnitud más.

Al ingresar un número, por ejemplo, 100000, esperamos ver 200000 en la salida. Sin embargo, al ejecutar el número 100000 varias veces, no solo no veremos la respuesta correcta, sino que también obtendremos diferentes respuestas incorrectas. La respuesta radica en el hecho de que para aumentar el número, se requieren tres operaciones: extraer el número de la memoria, incrementar y luego volver a escribir el número. Dado que todas estas instrucciones no se implementan atómicamente (todas al mismo tiempo), pueden suceder cosas tan extrañas. Este problema se llama programación de
condición de carrera -
condición de carrera . Cuando fuerzas desconocidas en un momento desconocido pueden afectar el rendimiento de cualquiera de sus operaciones.