UP | HOME

Introducción

Índice

1 ¿Qué es un sistema operativo?

El sistema operativo es el principal programa que se ejecuta en toda computadora de propósito general.

Hay sistemas operativos de todo tipo, desde muy simples hasta terriblemente complejos, y entre más casos de uso hay para el cómputo en la vida diaria, más variedad habrá en ellos.

No nos referiremos al sistema operativo como lo ve el usuario final, o como lo vende la mercadotecnia — El ambiente gráfico, los programas que se ejecutan en éste, los lenguajes de programación en que están desarrollados y en que más fácilmente se puede desarrollar para ellos, e incluso el conjunto básico de funciones que las bibliotecas base ofrecen son principalmente clientes del sistema operativo — Se ejecutan sobre él, y ofrecen su implementación a sus usuarios (incluídos, claro, los desarrolladores). La diferencia en el uso son sólo –y si mucho– consecuencias del diseño de un sistema operativo. Más aún, con el mismo sistema operativo –como pueden constatarlo comparando dos distribuciones de Linux, o incluso la forma de trabajo de dos usuarios en la misma computadora– es posible tener entornos operativos completamente disímiles.

1.1 ¿Por qué estudiar los sistemas operativos?

La importancia de estudiar este tema radica no sólo en comprender los mecanismos que emplean los sistemas operativos para cumplir sus tareas sino en entender estos mecanismos para evitar los errores más comunes al programar, que pueden resultar desde un rendimiento deficiente hasta pérdida de información.

Como desarrolladores, comprender el funcionamiento básico de los sistemas operativos y las principales alternativas que nos ofrecen en muchos de sus puntos, o saber diseñar algoritmos y procesos que se ajusten mejor al sistema operativo en que vayamos a ejecutarlo, puede resultar en una diferencia cualitativa decisiva en nuestros productos.

Como administradores de sistemas, muchas veces podemos enfrentarnos a situaciones de bajo rendimiento, de conflictos entre aplicaciones, demoras en la ejecución, y comprender lo que ocurre tras bambalinas resulta fundamental para realizar nuestro trabajo. Los sistemas de archivos resultan un área de especial interés para administradores de sistemas: ¿Cómo comparar las virtudes y desventajas de tantos sistemas existentes? ¿Por qué puede resultarnos conveniente mezclarlos en el mismo servidor? ¿Cómo evitar la corrupción o pérdida de información? Lo que es más, ¿cómo recuperar información de un disco dañado?

En el área de la seguridad en cómputo, la relación resulta obvia: si nos interesa localizar vulnerabilidades que nos permitan elevar nuestro nivel de privilegios, ¿cómo podríamos hacerlo sin comprender cómo se engranan los diversos componentes de un sistema? La cantidad de tareas que debe cubrir un sistema operativo es tremenda, y veremos ejemplos de sitios donde un atacante puede enfocar sus energías. Del mismo modo, para quien busca defender un sistema (o una red), resulta fundamental comprender cuáles son los vectores de ataque más comunes y –nuevamente– la relación entre los componentes involucrados para poder remediar o, mejor, prevenir dichos ataques.

Y claro está, podemos ver al mundo en general, fuera del entorno del cómputo, como una serie de modelos interactuantes. Muchos de los métodos y algoritmos que aquí veremos pueden emplearse fuera del entorno del cómputo, y una vez que comprendamos los problemas de concurrencia, de competencia por recursos, o de protección y separación que han sido resueltos en el campo de los sistemas operativos, podemos extrapolar estas soluciones a otros campos.

El camino por delante es largo, y puede resultar interesante y divertido.

2 Funciones y objetivos de los sistemas operativos

El sistema operativo es el único programa que interactúa directamente con el hardware de la computadora. Sus funciones primarias son:

Abstracción
Los programas no deben tener que preocuparse de los detalles de acceso a hardware, o de la configuración particular de una computadora. El sistema operativo se encarga de proporcionar una serie de abstracciones para que los programadores puedan enfocarse en resolver las necesidades particulares de sus usuarios. Un ejemplo de tales abstracciones es que la información está organizada en archivos y directorios (en uno o muchos dispositivos de almacenamiento).
Administración de recursos
Una sistema de cómputo puede tener a su disposición una gran cantidad de recursos (memoria, espacio de almacenamiento, tiempo de procesamiento, etc.), y los diferentes procesos que se ejecuten en él compiten por ellos. Al gestionar toda la asignación de recursos, el sistema operativo puede implementar políticas que los asignen de forma efectiva y acorde a las necesidades establecidas para dicho sistema.
Aislamiento
En un sistema multiusuario y multitarea cada proceso y cada usuario no tendrá que preocuparse por otros que estén usando el mismo sistema — Idealmente, su experiencia será la misma que si el sistema estuviera exclusivamente dedicado a su atención (aunque fuera un sistema menos poderoso).

Para implementar correctamente las funciones de aislamiento hace falta que el sistema operativo utilice hardware específico para dicha protección.

3 Evolución de los sistemas operativos

No se puede comenzar a abordar el tema de los sistemas operativos sin revisar brevemente su desarrollo histórico. Esto no sólo permitirá comprender por qué fueron apareciendo determinadas características y patrones de diseño que se siguen empleando décadas más tarde, sino (como resulta particularmente bien ejemplificado en el discurso de recepción del premio Turing de Fernando Corbató en 1990, /On building systems that will fail/), adecuar un sistema existente a un entorno cambiante, por mejor diseñado que éste estuviera, lleva casi inevitablemente a abrir espacios de comportamiento no previsto — El espacio más propicio para que florezcan los fallos. Conocer los factores que motivaron a los distintos desarrollos puede ayudar a prever y prevenir problemas.

3.1 Proceso por lotes (batch processing)

Los antecedentes a lo que hoy se conoce como sistema operativo se pueden encontrarlos en la automatización inicial del procesamiento de diferentes programas, surgida en los primeros centros de cómputo: cuando en los '50 aparecieron los dispositivos perforadores/lectores de tarjetas de papel, el tiempo que una computadora estaba improductiva esperando a que estuviera lista una tarea (como se designaba a una ejecución de cada determinado programa) para poder ejecutarla disminuyó fuertemente ya que los programadores entregaban su lote de tarjetas perforadas (en inglés, batches) a los operadores, quienes las alimentaban a los dispositivos lectores, que lo cargaban en memoria en un tiempo razonable, iniciaban y monitoreaban la ejecución, y producían los resultados.

En esta primer época en que las computadoras se especializaban en tareas de cálculo intensivo y los dispositivos que interactuaban con medios externos eran prácticamente desconocidos, el rol del sistema monitor o de control era básicamente asistir al operador en la carga de los programas y las bibliotecas requeridas, la notificación de resultados y la contabilidad de recursos empleados para su cobro.

Los sistemas monitores se fueron sofisticando al implementar protecciones que evitaran la corrupción de otros trabajos (por ejemplo, lanzar erróneamente la instrucción leer siguiente tarjeta causaría que el siguiente trabajo encolado perdiera sus primeros caracteres, corrompiéndolo e impidiendo su ejecución), o que entraran en un ciclo infinito, estableciendo alarmas (timers) que interrumpirían la ejecución de un proceso si éste duraba más allá del tiempo estipulado. Estos monitores implicaban la modificación del hardware para contemplar dichas características de seguridad — Y ahí se puede hablar ya de la característica básica de gestión de recursos que identifica a los sistemas operativos.

Cabe añadir que el tiempo de carga y puesta a punto de una tarea seguía representando una parte importante del tiempo que la computadora dedicaba al procesamiento: un lector de cintas rápido procesaba del orden de cientos de caracteres por minuto, y a pesar de la lentitud relativa de las computadoras de los '50 ante los estándares de hoy (se medirían por miles de instrucciones por segundo, KHz, en vez de miles de millones como se hace hoy, GHz), esperar cinco o diez minutos con el sistema completamente detenido por la carga de un programa moderadadamente extenso resulta a todas luces un desperdicio.

3.2 Sistemas en lotes con dispositivos de carga (spool)

Una mejora natural a este último punto fue la invención del spool: Un mecanismo de entrada/salida que permitía que una computadora de propósito específico, mucho más económica y limitada, leyera las tarjetas y las fuera convirtiendo a cinta magnética, un medio mucho más rápido, teniéndola lista para que la computadora central la cargara cuando terminara con el trabajo anterior. Del mismo modo, la computadora central guardarba sus resultados en cinta para que equipos especializados la leyeran e imprimieran para el usuario solicitante.

La palabra spool (bobina) se tomó como acrónimo inverso hacia Simultaneous Peripherial Operations On-Line, operación simultánea de periféricos en línea.

3.3 Sistemas multiprogramados

A lo largo de su ejecución, un programa normalmente pasa por etapas con muy distintas características: durante un ciclo fuertemente dedicado al cálculo numérico, el sistema opera limitado por el CPU (CPU-bound), mientras que al leer o escribir resultados a medios externos (incluso a través de spools) el límite es impuesto por los dispositivos, esto es, opera limitado por entrada-salida (I-O bound). La programación multitareas o los sistemas multiprogramados buscaban maximizar el tiempo de uso efectivo del procesador ejecutando varios procesos al mismo tiempo.

El hardware requerido cambió fuertemente. Si bien se esperaba que cada usuario fuera responsable con el uso de recursos, se hizo necesario que apareciera la infraestructura de protección de recursos: un proceso no debe sobreescribir el espacio de memoria de otro (ni el código ni los datos), mucho menos el espacio del monitor. Esta protección se encuentra en la Unidad de Manejo de Memoria (MMU), presente en todas las computadoras de uso genérico desde los '90.

Ciertos dispositivos requieren bloqueo para ofrecer acceso exclusivo/único — Cintas e impresoras, por ejemplo, son de acceso estrictamente secuencial, y si dos usuarios intentaran usarlas al mismo tiempo, el resultado para ambos se corrompería. Para estos dispositivos, el sistema debe implementar otros spools y mecanismos de bloqueo.

3.4 Sistemas de tiempo compartido

El modo de interactuar con las computadoras se modificó drásticamente durante los '60, al extenderse la multitarea para convertirse en sistemas interactivos y multiusuarios, en buena medida diferenciados de los anteriores por la aparición de las terminales (primero teletipos seriales, posteriormente equipos con una pantalla completa como se conocen hasta hoy).

En primer término, la tarea de programación y depuración del código se simplificó fuertemente al poder el programador hacer directamente cambios y someter el programa a la ejecución inmediata. En segundo término, la computadora nunca más estaría simplemente esperando a que esté listo un progama: Mientras un programador editaba o compilaba su programa, la computadora seguía calculando lo que otros procesos requirieran.

Un cambio fundamental entre el modelo de multiprogramación y de tiempo compartido es el tipo de control sobre la multitarea: (se verá en detalle en el capítulo PROC (Administración de procesos)

Multitarea cooperativa o no apropiativa
(Cooperative multitasking) La implementaron los sistemas multiprogramados: Cada proceso tenía control del CPU hasta que éste hacía una llamada al sistema (o indicara su disposición a cooperar por medio de la llamada yield: ceder el paso).

Un cálculo largo no era interrumpido por el sistema operativo, en consecuencia un error de programador podía congelar la computadora completa.

Multitarea preventiva o apropiativa
(Preemptive multitasking) En los sistemas de tiempo compartido, el reloj del sistema interrumpe periódicamente a los diversos procesos, transfiriendo forzosamente el control nuevamente al sistema operativo. El sistema operativo puede entonces elegir otro proceso para continuar la ejecución.

Además, fueron naciendo de forma natural y paulatina las abstracciones que se conocen hoy en día, como los conceptos de archivos y directorios, y el código necesario para emplearlos iba siendo enviado a las bibliotecas de sistema y, cada vez más (por su centralidad) hacia el núcleo mismo del –ahora sí– sistema operativo.

Un cambio importante entre los sistemas multiprogramados y de tiempo compartido es que la velocidad del cambio entre una tarea y otra es mucho más rápido: si bien en un sistema multiprogramado un cambio de contexto podía producirse sólo cuando la tarea cambiaba de un modo de ejecución a otro, en un sistema interactivo, para dar la ilusión de uso exclusivo de la computadora, el hardware emitía periódicamente al sistema operativo interrupciones (señales) que le indicaban que cambie el proceso activo (como ahora se le denomina a una instancia de un programa en ejecución).

Diferentes tipos de proceso pueden tener distinto nivel de importancia — Ya sea porque son más relevantes para el funcionamiento de la computadora misma (procesos de sistema), porque tienen mayor carga de interactividad (por la experiencia del usuario) o por diversas categorías de usuarios (sistemas con contabilidad por tipo de atención). Esto requiere la implementación de diversas prioridades para cada uno de estos.

4 Y del lado de las computadoras personales

Si bien la discusión hasta este momento asume una computadora central con operadores dedicados y múltiples usuarios, en la década de los '70 comenzaron a aparecer las computadoras personales, sistemas en un inicio verdaderamente reducidos en prestaciones y a un nivel de precios que los ponían al alcance, primero, de los aficionados entusiastas y, posteriormente, de cualquiera.

4.1 Primeros sistemas para entusiastas

./img/altair.jpg

La microcomputadora Altair 8800, primer computadora personal con distribución masiva, a la venta a partir de 1975. (Imagen de la Wikipedia: Altair 8800)

Las primeras computadoras personales eran distribuídas sin sistemas operativos o lenguajes de programación; la interfaz primaria para programarlas era a través de llaves (switches), y para recibir sus resultados, se utilizaban bancos de LEDs. Claro está, esto requería conocimientos especializados, y las computadoras personales eran aún vistas sólo como juguetes caros.

4.2 La revolución de los 8 bits

La verdadera revolución apareció cuando‚ poco tiempo más tarde, comenzaron a venderse computadoras personales con salida de video (típicamente a través de una televisión) y entrada a través de un teclado. Estas computadoras popularizaron el lenguaje de programación BASIC, diseñado para usuarios novatos en los '60, y para permitir a los usuarios gestionar sus recursos (unidades de cinta, pantalla posicionable, unidades de disco, impresoras, modem, etc.) llevaban un software mínimo de sistema — Nuevamente, un proto-sistema operativo.

./img/commodore_pet.jpg

La Commodore Pet 2001, en el mercado desde 1977, una de las primeras con intérprete de BASIC. (Imagen de la Wikipedia: Commodore PET)

4.3 La computadora para fines "serios": La familia PC

Al aparecer las computadoras personales "serias", orientadas a la oficina más que al hobby, a principios de los '80 (particularmente representadas por la IBM PC, 1981), sus sistemas operativos se comenzaron a diferenciar de los equipos previos al separar el entorno de desarrollo en algún lenguaje de programación del entorno de ejecución. El rol principal del sistema operativo ante el usuario era administrar los archivos de las diversas aplicaciones a través de una sencilla interfaz de línea de comando, y lanzar las aplicaciones que el usuario seleccionaba.

La PC de IBM fue la primer arquitectura de computadoras personales en desarrollar una amplia familia de clones, computadoras compatibles diseñadas para trabajar con el mismo sistema operativo, y que eventualmente capturaron casi el 100% del mercado. Prácticamente todas las computadoras de escritorio y portátiles en el mercado hoy derivan de la arquitectura de la IBM PC.

./img/ibmpc.jpg

La computadora IBM PC modelo 5150 (1981), iniciadora de la arquitectura predominantemente en uso hasta el día de hoy. (Imagen de la Wikipedia: IBM Personal Computer)

Ante las aplicaciones, el sistema operativo (PC-DOS, en las versiones distribuídas directamente por IBM, o el que se popularizó más, MS-DOS, en los clones) ofrecía la ya conocida serie de interfaces y abstracciones para administrar los archivos y la entrada/salida a través de sus puertos. Cabe destacar que, particularmente en sus primeros años, muchos programas se ejecutaban directamente sobre el hardware, arrancando desde el BIOS y sin emplear el sistema operativo.

4.4 El impacto del entorno gráfico (WIMP)

Hacia mediados de los '80 comenzaron a aparecer computadoras con interfaces gráficas basadas en el paradigma WIMP (Windows, Icons, Menus, Pointer; Ventanas, Iconos, Menúes, Apuntador), que permitían la interacción con varios programas al mismo tiempo. Esto no necesariamente significa que sean sistemas multitarea — Por ejemplo, la primer interfaz de MacOS permitía visualizar varias ventanas abiertas simultáneamente, pero sólo el proceso activo se ejecutaba.

./img/mac128.png

Apple Macintosh (1984), popularizó la interfaz usuario gráfica (GUI). (Imagen de la Wikipedia: Macintosh)

Esto comenzó, sin embargo, a plantear inevitablemente las necesidades de concurrencia a los programadores. Los programas ya no tenían acceso directo a la pantalla para manipular a su antojo, sino que a una abstracción (la ventana) que podía variar sus medidas, y que requería que toda la salida fuera estrictamente a través de llamadas a bibliotecas de primitivas gráficas que comenzaron a verse como parte integral del sistema operativo.

Además, los problemas de protección y separación entre procesos concurrentes comenzaron a hacerse evidentes: los programadores tenían ahora que programar con la conciencia de que compartirían recursos — con el limitante (que no tenían en las máquinas profesionales) de no contar con hardware especializado para esta protección. Los procesadores en uso comercial en los '80 no manejaban anillos o niveles de ejecución ni unidad de administración de memoria (MMU), por lo que un programa fallado o dañino podía corromper la operación completa del equipo. Y si bien los entornos que más éxito tuvieron (Apple MacOS y Microsoft Windows) no implementaban multitarea real, sí hubo desde el principio sistemas como la Commodore Amiga o la Atari ST que hacían un multitasking preventivo verdadero.

./img/A500.jpg

Commodore Amiga 500 (1987), la computadora más popular de la familia Amiga, con amplias capacidades multimedia y multitarea preventiva; una verdadera maravilla para su momento. (Imagen de la Wikipedia: Amiga)

Naturalmente, ante el uso común de un entorno de ventanas, los programas que se ejecutaban sin requerir de la carga del sistema operativo cayeron lentamente en el olvido.

4.5 Convergencia de los dos grandes mercados

Conforme fueron apareciendo los CPU con características suficientes en el mercado para ofrecer la protección y aislamiento necesario (particularmente, Intel 80386 y Motorola 68030), la brecha de funcionalidad entre las computadoras personales y las estaciones de trabajo y mainframes se fue cerrando.

Hacia principios de los 1990, la mayor parte de las computadoras de arquitecturas alternativas fueron cediendo a las presiones del mercado, y hacia mediados de la década sólo quedaban dos arquitecturas principales: la derivada de IBM y la derivada de la Apple Macintosh.

Los sistemas operativos primarios para ambas plataformas fueron respondiendo a las nuevas características del hardware: en las IBM, la presencia de Microsoft Windows (originalmente un entorno operativo desde su primer edición en 1985, evolucionando hacia un sistema operativo completo ejecutando sobre una base de MS-DOS en 1995) se fue haciendo prevalente hasta ser la norma. Windows pasó de ser un sistema meramente de aplicaciones propias y que operaba únicamente por reemplazo de aplicación activa a ser un sistema de multitarea cooperativa, y finalmente un sistema que requería protección en hardware (80386) e implementaba multitarea preventiva.

A partir del 2003, el núcleo de Windows en más amplio uso fue reemplazado por un desarrollo hecho de inicio como un sistema operativo completo y ya no como una aplicación dependiente de MS-DOS: el núcleo de Nueva Tecnología (Windows NT), que, sin romper compatibilidad con los APIs históricos de Windows, ofreció mucho mayor estabilidad.

Por el lado de Apple, la evolución fue muy en paralelo: ante un sistema ya agotado y obsoleto, el MacOS 9, en 2001 anunció una nueva versión de su sistema operativo que fue en realidad un relanzamiento completo: MacOS X es un sistema basado en un núcleo Unix BSD, sobre el microkernel Mach.

Y otro importante jugador que entró en escena durante los '90 fue el software libre, por medio de varias implementaciones distintas de sistemas tipo Unix — principalmente, Linux y los *BSD (FreeBSD, NetBSD, OpenBSD). Estos sistemas implementaron, colaborativamente y a escala mundial, software compatibles con las PC y con el que se ejecutaba en las estaciones de trabajo a gran escala, con alta confiabilidad, y cerrando por fin la divergencia del árbol del desarrollo de la computación en fierros grandes y fierros chicos.

Al día de hoy, la arquitectura derivada de Intel (y la PC) es el claro ganador de este proceso de 35 años, habiendo conquistado casi la totalidad de los casos de uso, incluso las máquinas Apple. Hoy en día, la arquitectura Intel ejecuta desde subportátiles hasta supercomputadoras y centros de datos; el sistema operativo específico varía según el uso, yendo mayoritariamente hacia Windows, con los diferentes Unixes concentrados en los equipos servidores.

En el frente de los dispositivos embebidos (las computadoras más pequeñas, desde microcontroladores hasta teléfonos y tabletas), la norma es la arquitectura ARM, también bajo versiones específicas de sistemas operativos Unix y Windows (en ese orden).

5 Organización de los sistemas operativos

Para comenzar el estudio de los sistemas operativos, la complejidad del tema requiere que se haga de una forma modular. En este texto no se busca enseñar cómo se usa un determinado sistema operativo, ni siquiera comparar el uso de uno con otro (fuera de hacerlo con fines de explicar diferentes implementaciones).

Al nivel que se estudiará, un sistema operativo es más bien un gran programa, que ejecuta otros programas y les provee un conjunto de interfaces para que puedan aprovechar los recursos de cómputo. Hay dos formas primarias de organización interna del sistema operativo: los sistemas monolíticos y los sistemas microkernel. Y si bien no se puede marcar una línea clara a rajatabla que indique en qué clasificiación cae cada sistema, no es dificil encontrar líneas bases.

Monolíticos
La mayor parte de los sistemas operativos históricamente han sido monolíticos — Esto significa que hay un sólo proceso privilegiado (justamente el sistema operativo) que opera en modo supervisor, y dentro del cual se encuentran todas las rutinas para las diversas tareas que realiza el sistema operativo.

./img/dot/diseno_monolitico.png

Esquematización de los componentes en un sistema monolítico

Microkernel
El núcleo del sistema operativo se mantiene en el mínimo posible de funcionalidad, descargando en procesos especiales sin privilegios las tareas que implementan el acceso a dispositivos y las diversas políticas de uso del sistema.

./img/dot/diseno_microkernel.png

Esquematización de los componentes en un sistema microkernel

La principal ventaja de diseñar un sistema siguiendo un esquema monolítico es la simplificación de una gran cantidad de mecanismos de comunicación, que lleva a una mayor velocidad de ejecución (al requerir menos cambios de contexto para cualquier operación realizada). Además, al manejarse la comunicación directa como paso de estructuras en memoria, el mayor acoplamiento permite más flexibilidad al adecuarse para nuevos requisitos (al no tener que modificar no sólo al núcleo y a los procesos especiales, sino también la interfaz pública entre ellos).

Por otro lado, los sistemas microkernel siguen esquemas lógicos más limpios, permiten implementaciones más elegantes y facilitan la comprensión por separado de cada una de sus piezas. Pueden auto-repararse con mayor facilidad, dado que en caso de fallar uno de los componentes (por más que parezca ser de muy bajo nivel), el núcleo puede reiniciarlo o incluso reemplazarlo.

Sistemas con concepciones híbridas
No se puede hablar de concepciones únicas ni de verdades absolutas. A lo largo del libro se verán ejemplos de concepciones híbridas en este sentido — Sistemas que son mayormente monolíticos pero manejan algunos procesos que parecerían centrales a través de procesos de nivel usuario como los microkernel (por ejemplo, los sistemas de archivos en espacio de usuario, FUSE, en Linux).

./img/dot/diseno_hibrido.png

Esquematización de los componentes en un sistema híbrido

6 Otros recursos