Conceptos Principales
Existen cuatro conceptos principales en sistemas operativos:
Hilos
-
Proporcionan el conexto de ejecución (tienen el contexto completo del programa).
-
Tiene Program Counter, Registros, Flags de Ejecución, Stack.
Espacio de Direcciones
-
Son direcciones de memoria disponibles para un programa (escribir y leer).
-
Pueden ser virtuales al separar la memoria asignada a los programas de las direcciones físicas en el hardware (usando traductor).
Procesos
-
Una instancia de ejecución de un programa.
-
Tiene un espacio de direcciones protegido y uno o más hilos.
Modo de Operación Dual
-
Solamente el kernel tiene acceso privilegiado a ciertos recursos y operaciones.
-
Este modo combinado con el traductor del espacio de direcciones permite proteger al sistema operativo, aislando los programas del usuario de los programas y memoria del sistema operativo.
Hilos (Threads)
Un Thread es un contexto de ejecución unico que tiene sus datos asociados como contador de programa, registros, banderas de ejecución (execution flags), stack, heap, y estados de la memoria.
Un sistema operativo puede tener múltiples Threads pero solo uno a la vez es residente en los registros del procesador y es ejecutado.
Que un Thread sea residente quiere decir que el procesador almacena los datos raíz del Thread. Esto quiere decir que se almacenan el contador del programa del Thread (PC) que es un puntero a la siguiente instrucción en memoria a ejecutar, la lista de instrucciones almacenadas en la memoria. El puntero del Stack para saber la dirección del inicio del Stack (que está en memoria). También esto incluye valores intermedios necesarios para realizar las operaciones (pueden ser valores numéricos o que apunten a una dirección de memoria). Todos los demás datos necesarios para el Thread estarán almacenados en la memoria.
Cuando un Thread está suspendido (no se ejecuta) y su estado no es residente (cargado en el procesador). Quiere decir que el procesador está ocupado por otro Thread. El PC del procesador apunta a las instrucciones del Thread actual. Una copia de todos los registros y su último valor del Thread suspendido es almacenada en memoria, lo que es conocido como Thread Control Block (TCB) (Bloque de Control del Hilo), la información normalmente es el stack, heap, datos e instrucciones de código a ejecutar en el thread. Cuando el procesador cambia de un Thread a otro, se conoce como cambio de contexto (Context Switch).
Un Thread es como un núcleo (core) virtual del procesador. Es decir que cada núcleo del procesador puede tener un Thread a la vez. Cada núcleo tiene un único hilo (Thread) de ejecución, lo que se conoce como hilo principal (main thread).
Entonces los Threads proporcionan la ilusión de tener múltiples núcleos de procesador (multiplexar el procesador) ejecutandose al mismo tiempo, asumiendo que un procesador sea de un solo núcleo, Los threads proporcionan la ilusión de tener múltiples procesadores. Estos Threads comparten la misma cantidad de memoria, comparten el mismo caché, mismos IO y todo lo demás del hardware , por lo que si ocurre un cambio de contexto muy rápido, quizás no se pueda obtener el beneficio del caché.
¿Qué pasa si el procesador cambia de un Thread a otro?.
La respuesta rápida sería que ocurre un cambio de contexto (Context Switch). La respuesta detallada es que ocurre lo siguiente:
-
Ocurre un evento (temporizador, interrupciones de IO, pausa voluntaria del thread, entre otros) que provoca que el sistema operativo necesite cambiar de un Thread A a un Thread B.
-
Se almacena toda la información del Thread A dentro de la memoria en el Thread Control Block (TCB).
-
Se carga toda la información del Thead B (TCB del Thread B) en los registros y PC al procesador.
Normalmente esta es una tarea que tarda microsegundos y un sistema operativo debe velar por que sea en tiempo reducido, si no se puede encontrar problemas de latencia o congelamiento de las operaciones.
¿Por qué es mejor usar cambio de contexto a esperar que se complete la tarea?.
Si bien esperar a que una operación o tarea termine antes de ejecutar otra puede ser eficiente. Se tendría que esperar mucho tiempo antes de iniciar la siguiente tarea, lo que rompería la ilusión de tener tareas en paralelo. Este tipo de problemas se resuelve con estrategias de cronograma y priorización (Scheduling).
¿Puede un Thread acceder a la memoria de otro?
En los primeros sistemas operativos modernos (Windows 3.1, 95 - ME, MacOS 1 - 9, Aplicaciones de microcontroladores) si bien se podía dar la ilusión de múltiples procesadores. Existía el peligro de que un Thread accediera y pudiera sobre escribir o acaparar la memoria de otro Thread. Por lo que era un contexto muy inseguro. Por ejemplo una programa podía ejecutar un ciclo infinito o calcular un número muy elevado (como dígitos de PI) y congelar todo el sistema operativo.Esta modalidad de multiplexión no tiene seguridad ni protecciones.
Labor de un Sistema Operativo con los Threads
La labor de un sistema operativo es protegerse a si mismo de los programas ejecutados por sus usuarios. Debe cumplir con las características:
-
Confiabilidad: Si se compromete el sistema operativo puede llevar a congelamientos o fallas graves.
-
Seguridad: Debe limitar el alcance de lo que pueden hacer los Threads.
-
Privacidad: Debe limitar cada Thread para que solamente pueda acceder a los datos que cuenta con permisos de leer y escribir.
-
Equilibrio: Cada Thread debe tener los recursos justos y necesarios para su operación, sin agotarlos (CPU, memoria, IO, etc).
Protección
El sistema operativo también debe proteger los Threads entre sí. Debe prevenir que un Thread ejecutado por un usuario pueda perjudicar el Thread ejecutado por otro (Por ejemplo prevenir acceder a datos secretos de un usuario)
Una estrategia de protección son los límites de Base (inicio) y Bound (fin); Es almacenar dos direcciones que marquen los límites de la memoria utilizados por el programa. Uno que marque el inicio (base) y uno que marque el límite máximo o fin (bound).
Acá el hardware del CPU puede comparar entre estos dos límites y entregar un resultado booleano si está dentro o fuera de los límites (>=
inicio, <
fin).
Si está fuera de los límites el sistema operativo puede terminar el Thread. Esto es una protección de hardware simple y es usada para proteger que los programas puedan sobre escribir al sistema operativo.
Es común que se utilice un componente de hardware (Adder) el cual suma al PC la dirección Base y con eso se puede asegurar que siempre estará dentro del espacio de memoria adecuado, protegiendo que el programa de usuario no pueda acceder a espacios de memoria del sistema operativo o de otros programas.
El sistema operativo es quien decide los límites según la estrategia best fit
o la que sea factible.
Espacio de Direcciones (Address Space)
El espacio de direcciones es un conjunto de direcciones disponibles y un estado asociado a ellas. Son las direcciones potenciales que un procesador puede utilizar.
En un procesador de 32 bit tiene
Mientras que en uno de 64 bit tiene
¿Qué ocurre cuando se lee o se escribe una dirección?
-
Puede que se lea como una memoria normal.
-
Puede que solo sea de lectura (ignore las escrituras).
-
Puede que gatille un evento (memoria asociada al IO).
-
Puede causar una excepción fatal.
-
Puede comunicar con otro programa.
¿Qué puede almacenar?

-
PC: Program Counter. La siguiente instrucción a ejecutar.
-
SP: Stack Pointer. La dirección del elemento superior en el Stack.
-
Instrucciones de Código: Las instrucciones que el procesador debe ejecutar.
-
Static Data: Datos estáticos como variables estáticas o globales e strings constantes.
-
Stack: El contenido del stack, cuánto espacio necesita y cómo se debe dimensionar (normalmente variables locales). Puede estar separado por páginas.
-
Heap: El contenido del heap (espacio de memoria dinámico, estructuras, punteros, listas enlazadas). Puede ser aumentado dinámicamente según necesidad.
Traducción del Espacio de Direcciones (Address Space Translation)
Lo que ocurre normalmente es que el sistema operativo tiene un espacio virtual de direcciones, las cuales deben pasar por un proceso de traducción el cual entrega la dirección real de la memoria en hardware a utilizar.
