HISTORIA
DE LOS SISTEMAS OPERATIVOS
Los
sistemas operativos han estado evolucionando durante muchos años. En las siguientes
secciones examinaremos brevemente este desarrollo. Dado que, históricamente,
los sistemas operativos han estado de manera muy estrecha vinculados con la
arquitectura de las computadoras en las que se ejecutan, estudiaremos las
sucesivas generaciones de computadoras para ver qué clase de sistemas
operativos usaban. Esta correspondencia entre las generaciones de sistemas
operativos y de computadoras es algo burda, pero establece un poco de
estructura que de otra forma sería inexistente. La primera computadora digital
verdadera fue diseñada por el matemático inglés Charles Babbage (1792- 1871).
Aunque Babbage invirtió la mayor parte de su vida y su fortuna tratando de
construir su “máquina analítica”, nunca logró que funcionara correctamente
porque era totalmente mecánica, y la tecnología de su época no podía producir
las ruedas, engranes y levas con la elevada precisión que él requería. Huelga
decir que la máquina analítica no contaba con un sistema operativo. Como
acotación histórica interesante, diremos que Babbage se dio cuenta de que
necesitaría software para su máquina analítica, así que contrató a una joven
mujer, Ada Lovelace, hija del famoso poeta británico, Lord Byron, como la
primera programadora de la historia. El lenguaje de programación Ada® recibió
sunombre en honor a ella.
La
primera generación (1945-55): Tubos de vacío y tableros de conmutación
Después
del fracaso de los trabajos de Babbage, fueron pocos los avances que se
lograron en la construcción de computadoras digitales hasta la Segunda Guerra
Mundial. A mediados de la década de 1940, Howard Aiken en Harvard, John von
Neumann en el Institute for Advanced Study en Princeton, J. Presper Eckert y
William Mauchley en la University of Pennsylvania y Konrad Zuse en Alemania,
entre otros, lograron construir máquinas calculadoras usando tubos de vacío.
Estas máquinas eran enormes, y ocupaban cuartos enteros con decenas de miles de
tubos de vacío, pero eran mucho más lentas que incluso las computadoras
personales más baratas de la actualidad. En esos primeros días, un solo grupo
de personas diseñaba, construía, programaba, operaba y mantenía a cada máquina.
Toda la programación se realizaba en lenguaje de máquina absoluto, a menudo
alambrando tableros de conmutación para controlar las funciones básicas de la
máquina. No existían los lenguajes de programación (ni siquiera los de
ensamblador). Nadie había oído hablar de los sistemas operativos. La forma de
operación usual consistía en que el programador se anotaba para recibir un
bloque de tiempo en la hoja de reservaciones colgada en la pared, luego bajaba
al cuarto de la máquina, insertaba su tablero de conmutación en la computadora,
y pasaba las siguientes horas con la esperanza de que ninguno de los cerca de
20000 tubos de vacío se quemara durante la sesión. Prácticamente todos los
problemas eran cálculos numéricos directos, como la producción de tablas de
senos y cosenos.
A
principios de la década de 1950, la rutina había mejorado un poco con la
introducción de las tarjetas perforadas. Ahora era posible escribir programas
en tarjetas e introducirlas para ser leídas, en lugar de usar tableros de
conmutación; por lo demás, el procedimiento era el mismo.
La segunda generación (1955-65): Transistores
y sistemas por lote
La
introducción del transistor a mediados de la década de 1950 alteró el panorama
radicalmente. Las computadoras se hicieron lo bastante confiables como para
poderse fabricar y vender a clientes comerciales con la expectativa de que
seguirían funcionando el tiempo suficiente para realizar algo de trabajo útil.
Por primera vez, había una separación clara entre diseñadores, constructores,
operadores, programadores y personal de mantenimiento.
Estas
máquinas se encerraban en cuartos de computadora con acondicionamiento de aire
especial, con equipos de operadores profesionales para operarlas. Sólo las
grandes empresas, o las principales dependencias del gobierno o universidades,
podían solventar el costo de muchos millones de dólares. Para ejecutar un trabajo(es
decir, un programa o serie de programas), un programador escribía primero el programa
en papel (en FORTRAN o ensamblador) y luego lo perforaba en tarjetas. Después,
llevaba el grupo de tarjetas al cuarto de entrada y lo entregaba a uno de los
operadores. Cuando la computadora terminaba el trabajo que estaba ejecutando en
ese momento, un operador acudía a la impresora, separaba la salida impresa y la
llevaba al cuarto de salida donde el programador podía recogerla después.
Luego, el operador tomaba uno de los grupos de tarjetas traídos del cuarto de
entrada y lo introducía en el lector. Si se requería el compilador de FORTRAN,
el operador tenía que traerlo de un archivero e introducirlo en el lector. Gran
parte del tiempo de computadora se desperdiciaba mientras los operadores iban
de un lugar a otro, en el cuarto de la máquina. Dado el alto costo del equipo,
no es sorprendente que la gente pronto buscara formas de reducir el desperdicio
de tiempo. La solución que se adoptó generalmente fue el sistema por lotes. El principio de este modo
de operación consistía en juntar una serie de trabajos en el cuarto de entrada,
leerlos y grabarlos en una cinta magnética usando una computadora pequeña y
(relativamente) económica, como una IBM 1401, que era muy buena para leer
tarjetas, copiar cintas e imprimir salidas, pero no para realizar cálculos numéricos.
Otras máquinas, mucho más costosas, como la IBM 7094, se usaban para la
computación
propiamente
dicha. Esta situación se muestra en la Fig. 1-2.
Después
de cerca de una hora de reunir un lote de trabajos, la cinta se rebobinaba y se
llevaba al cuarto de la máquina, donde se montaba en una unidad de cinta. El
operador cargaba entonces un programa especial (el antepasado del sistema
operativo actual), que leía el primer trabajo de la cinta y lo ejecutaba. La
salida se escribía en una segunda cinta, en lugar de imprimirse. Cada vez que
terminaba un trabajo, el sistema operativo leía automáticamente el siguiente
trabajo de la cinta y comenzaba a ejecutarlo. Una vez que estaba listo todo el
lote, el operador desmontaba las cintas de entrada y salida, montaba la cinta
de entrada del siguiente lote, y llevaba la cinta de salida a una 1401 para la
impresión fuera de línea (o sea, no conectada
a la computadora principal). La estructura de un trabajo de entrada típico se
muestra en la Fig. 1-3. El trabajo comenzaba con una tarjeta $JOB, que
especificaba el tiempo de ejecución máximo en minutos, el número de cuenta al
que se debía cobrar el trabajo, y el nombre del programador. Luego venía una
tarjeta $FORTRAN, que ordenaba al sistema operativo leer el compilador de
FORTRAN de la cinta de sistema. Esta tarjeta iba seguida del programa por
compilar y por una tarjeta $LOAD, que ordenaba al sistema operativo cargar el
programa objeto recién compilado. (Los programas compilados a menudo se
escribían en cintas temporales y tenían que cargarse explícitamente.) Luego
venía la tarjeta $RUN, que ordenaba al sistema operativo ejecutar el programa
con los datos que le seguían. Por último, la tarjeta $END marcaba el final del
trabajo. Estas tarjetas de control primitivas eran los precursores de los
lenguajes de control de trabajos e intérpretes de comandos modernos.

Las
computadoras grandes de la segunda generación se usaban primordialmente para
cálculos científicos y de ingeniería, como la resolución de ecuaciones
diferenciales parciales. Estas máquinas generalmente se programaban en FORTRAN
y lenguaje ensamblador. Los sistemas operativos típicos eran FMS (el Fortran Monitor
System) e IBSYS, el sistema operativo de IBM para la 7094.
La
tercera generación (1965-1980): Circuitos integrados y multiprogramación
A
principios de la década de 1960, la mayoría de los fabricantes de computadoras
tenían dos líneas de producto distintas y totalmente incompatibles. Por un lado
estaban las computadoras científicas a gran escala, orientadas hacia las palabras,
como la 7094, que se usaban para cálculos numéricos en ciencias e ingeniería.
Por el otro, estaban las computadoras comerciales orientadas hacia los
caracteres, como la 1401, que los bancos y las compañías de seguros utilizaban
ampliamente para ordenar e imprimir desde cinta.
La
creación y mantenimiento de dos líneas de producto totalmente distintas era una
situación costosa para los fabricantes. Además, muchos clientes de computadoras
nuevas necesitaban inicialmente una máquina pequeña que más adelante les
resultaba insuficiente, de modo que querían una máquina más grande que ejecutara
todos sus viejos programas, pero más rápidamente.
IBM trató
de resolver simultáneamente ambos problemas introduciendo la System/360. La 360
era una serie de máquinas de software compatible que iban desde tamaños
comparables a la 1401 hasta computadoras mucho más potentes que la 7094. Las
máquinas diferían sólo en el precio y el rendimiento (memoria máxima, velocidad
del procesador, número de dispositivos de E/S permitidos, etc.). Puesto que
todas las máquinas tenían la misma arquitectura y conjunto de instrucciones,
los programas escritos para una máquina podían ejecutarse en todas las demás,
al menos en teoría. Además, la 360 estaba diseñada para manejar computación
tanto científica como comercial. Así, una sola familia de máquinas podía
satisfacer las necesidades de todos los clientes. En años subsecuentes IBM
produjo sucesoras comparables a la línea 360, usando tecnología más moderna,
conocidas como series 370, 4300, 3080 y 3090. La 360 fue la primera línea
importante de computadoras en usar (a pequeña escala) circuitos integrados(IC),
ofreciendo así una ventaja de precio/rendimiento considerable respecto a las
máquinas de la segunda generación, que se armaban con transistores
individuales. Esta línea fue un éxito inmediato, y la idea de una familia de
computadoras compatibles pronto fue adoptada por todos los demás fabricantes
importantes.
Los
descendientes de estas máquinas todavía se emplean en uno que otro centro de
cómputo en la actualidad, pero su uso está en rápido declive. La gran ventaja
de la idea de “una familia” fue también su gran debilidad. La intención era que
todo el software, incluido el sistema operativo, funcionara en todos los
modelos. El software tenía que funcionar en sistemas pequeños, que en muchos
casos simplemente sustituían a la 1401 para copiar tarjetas en cinta, y en
sistemas muy grandes, que con frecuencia sustituían a las 7094 para realizar
pronósticos del tiempo y otros trabajos de computación pesada. El software
tenía que ser bueno en sistemas con pocos y con muchos periféricos; tenía que
funcionar en entornos comerciales y. científicos y, sobre todo, tenía que ser eficiente
para todos estos usos distintos.
Era
imposible que IBM (o alguien más) pudiera escribir un programa que satisficiera
todos esos requisitos opuestos. El resultado fue un sistema operativo enorme,
extraordinariamente complejo, tal vez dos o tres órdenes de magnitud mayor que
FMS. Este sistema consistía en millones de líneas de lenguaje ensamblador
escrito por miles de programadores, y contenía miles y miles de errores,
requiriéndose un flujo continuo de nuevas versiones en un intento por
corregirlos. Cada versión nueva corregía algunos errores e introducía otros
nuevos, de modo que es probable que el número de errores se mantuviera
constante con el tiempo.
Uno de
los diseñadores de OS/360, Fred Brooks, escribió después un ingenioso e
incisivo libro (Brooks, 1975) describiendo sus experiencias con el OS/360.
Aunque sería imposible resumir aquí ese libro, baste con decir que la portada
muestra una manada de bestias prehistóricas atascadas en un foso de brea. La portada
del libro de Silberschatz y Galvin (1994) es una alusión similar.
A pesar
de su enorme tamaño y de sus problemas, OS/360 y los sistemas operativos de
tercera generación parecidos a él producidos por otros fabricantes de
computadoras lograron satisfacer a sus clientes en un grado razonable, y también
popularizaron varias técnicas clave que no existían en los sistemas operativos de
la segunda generación. Tal vez la más importante de ellas haya sido la multiprogramación. En la 7094, cuando el
trabajo actual hacía una pausa para esperar que se completara una operación de
cinta u otra operación de E/S, la CPU simplemente permanecía ociosa hasta que
la E/S terminaba. En los cálculos científicos, con gran uso de CPU, la E/S es
poco frecuente, así que el tiempo desperdiciado no es significativo. En el procesamiento
de datos comerciales, el tiempo de espera por E/S puede ser el 80 o 90% del
tiempo total, de modo que algo debía hacerse para evitar que la CPU estuviera
ociosa tanto tiempo.
La
solución a la que se llegó fue dividir la memoria en varias secciones, con un
trabajo distinto en cada partición, como se muestra en la Fig. 1-4. Mientras un
trabajo estaba esperando que terminara su E/S, otro podía estar usando la CPU.
Si se podían tener en la memoria principal suficientes trabajos a la vez, la
CPU podía mantenerse ocupada casi todo el tiempo. Tener múltiples trabajos en
la memoria a la vez requiere hardware especial para proteger cada trabajo
contra espionaje por parte de los demás, pero la 360 y otros sistemas de
tercera generación estaban equipados con este hardware.
Otra
característica importante presente en los sistemas operativos de la tercera
generación era la capacidad de leer trabajos de las tarjetas al disco tan
pronto como se llevaban al cuarto de computadoras. Luego, cada vez que un
trabajo terminaba su ejecución, el sistema operativo podía cargar uno nuevo del
disco en la partición que había quedado vacía y ejecutarlo. Esta técnica se
llama spooling(de “operación simultánea de periféricos en línea”) y también se
usaba para la salida. Con spooling, las 1401 ya no eran necesarias, y desapareció
una buena parte del transporte de cintas. Aunque los sistemas operativos de la
tercera generación se adaptaban bien a cálculos científicos extensos y sesiones
masivas de procesamiento de datos comerciales, seguían siendo básicamente
sistemas por lotes.
Muchos
programadores añoraban los días de la primera generación cuando tenían toda la
máquina para ellos solos durante unas cuantas horas, lo que les permitía depurar
sus programas rápidamente. Con los sistemas de la tercera generación, el tiempo
entre la presentación de un trabajo y la obtención de las salidas a menudo era
de varias horas, y una sola coma mal colocada podía causar el fracaso de una
compilación y que el programador desperdiciara medio día.
Este
deseo de respuesta rápida preparó el camino para el tiempo compartido, una variante de la multiprogramación,
en la que cada usuario tiene una terminal en línea. En un sistema de tiempo
compartido, si 20 usuarios ingresan en el sistema y 17 de ellos están pensando,
hablando o tomando café, la CPU puede asignarse por turno a los tres trabajos
que requieren servicio. Puesto que las personas que están depurando programas
usualmente emiten comandos cortos (p. ej., compilar un procedimiento de cinco páginas)
en vez de largos (p. ej., ordenar un archivo de un millón de registros), la
computadora puede proporcionar servicio rápido interactivo a varios usuarios y
tal vez también trabajar con trabajos de lote grandes en segundo plano cuando
la CPU está ociosa. Aunque el primer sistema serio de tiempo compartido (CTSS)
fue creado en el M.I.T. en una 7094 especialmente modificada (Corbato et al.,
1962), el tiempo compartido no se popularizó realmente hasta que se generalizó
el uso del hardware de protección necesario durante la tercera generación.
Después
del éxito del sistema CTSS, MIT, Bell Labs y General Electric (por ese entonces
un fabricante importante de computadoras) decidieron emprender el desarrollo de
un “servicio de computadora” una máquina que diera apoyo a cientos de usuarios
de tiempo compartido simultáneos. Su modelo fue el sistema de distribución de
electricidad: cuando usted necesita potencia eléctrica, simplemente enchufa una
clavija en la pared y, dentro de límites razonables, obtendrá tanta
electricidad como necesite. Los diseñadores de este sistema, llamado
MULTICS(servicio
de información y computación multiplexado), contemplaban una enorme máquina que
proporcionara potencia de cómputo a todos los usuarios de Boston. La idea de
que máquinas mucho más potentes que su GE-645 se vendieran como computadoras personales
por unos cuantos miles de dólares sólo 30 años después no era sino ciencia
ficción en ese entonces.
Para
resumir un poco la historia, MULTICS introdujo muchas ideas seminales en la
literatura de computación, pero su construcción fue mucho más difícil de lo que
nadie había imaginado. Bell Labs abandonó el proyecto, y General Electric dejó
el negocio de las computadoras por completo. Finalmente, MULTICS funcionó lo
bastante bien como para usarse en un entorno de producción de MIT y en docenas
de otros sitios, pero el concepto de un servicio de computadora se hizo
obsoleto al desplomarse los precios de las computadoras. No obstante, MULTICS
tuvo una influencia enorme sobre los sistemas subsecuentes; se le describe en
(Corbato et al., 1972; Corbato y Vyssotsky, 1965; Daley y Dennis, 1968;
Organick, 1972; Saltzer, 1974).
Otro
avance importante durante la tercera generación fue el crecimiento fenomenal de
las minicomputadoras, comenzando con la DEC PDP- 1 en 1961. La PDP-1 sólo tenía
4K de palabras de 18 bits, pero a $120 000 por máquina (menos del 5% del precio
de una 7094), se vendieron como pan caliente. Para ciertos tipos de trabajos no
numéricos, la PDP-1 era casi tan rápida como la 7094, e hizo nacer una industria
totalmente nueva. A esta máquina pronto siguió una serie de otras PDP (todas
incompatibles, a diferencia de la familia IBM), culminando en la PDP-11.
Uno de
los computólogos de BelI Labs que había trabajado en el proyecto MULTICS, Ken
Thompson, encontró subsecuentemente una pequeña minicomputadora PDP-7 que nadie
estaba usando y se propuso escribir una versión de MULTICS reducida al mínimo,
para un solo usuario. Este trabajo posteriormente evolucionó para convertirse
en el sistema operativo
UNIX®,
que se popularizó en el mundo académico, las dependencias del gobierno y muchas
compañías.
La
historia de UNIX se cuenta en otras obras (p. ej., Salus, 1994). Baste con
decir que, dado que casi todo mundo podía obtener el código fuente, diversas
organizaciones desarrollaron sus propias versiones (incompatibles), lo que
condujo al caos. Con objeto de que fuera posible escribir programas
susceptibles de ejecución en cualquier sistema UNIX, el IEEE creó un estándar
para UNIX, llamado Posix, que casi todas
las versiones actuales de UNIX reconocen. POSIX define una interfaz mínima de
llamadas al sistema que los sistemas UNIX deben reconocer. De hecho, algunos
otros sistemas de programación ya reconocen la interfaz POSIX.
La cuarta
generación (1980-presente): Computadoras personales
Con la
invención de los circuitos integrados a gran escala (LSI), chips que contienen
miles de transistores en un cm2 de silicio, nació la era de la computadora
personal. En términos de arquitectura, las computadoras personales no eran muy
diferentes de las minicomputadoras de la clase PDP-11, pero en términos de
precio sí que eran diferentes. Si bien la minicomputadora hacía posible que un
departamento de una compañía o universidad tuviera su propia computadora, el
chip microprocesador permitía que un solo individuo tuviera su propia
computadora personal. Las computadoras personales más potentes empleadas por
empresas, universidades e instalaciones del gobierno suelen llamarse estaciones de trabajo, pero en realidad sólo
son computadoras personales grandes. Por lo regular estas máquinas están
interconectadas mediante una red.
La amplia
disponibilidad de la potencia de cómputo, sobre todo la potencia de cómputo
altamente interactiva casi siempre acompañada por excelentes gráficos, dio pie
al crecimiento de una importante industria productora de software para
computadoras personales. Una buena parte de este software era amistoso con el usuario, lo que significa que
estaba dirigido a usuarios que no sólo no sabían nada de computación, sino que
además no tenían la mínima intención de aprender. Sin duda, esto representaba
un cambio drástico respecto al OS/360, cuyo lenguaje de control de trabajos,
JCL, era tan arcano que llegaron a escribirse libros enteros sobre él (p. ej.,
Cadow, 1970).
Dos
sistemas operativos dominaron inicialmente el campo de las computadoras
personales y las estaciones de trabajo: MS-DOS de Microsoft y
UNIX.
MS-DOS se usaba ampliamente en la IBM PC y otras máquinas basadas en la CPU
Intel 8088 y sus sucesoras, la 80286, 80386 y 80486 (que en adelante llamaremos
la 286, 386 y 486, respectivamente) y más tarde la Pentium y Pentium Pro.
Aunque la versión inicial de MS-
DOS era
relativamente primitiva, versiones subsecuentes han incluido características
más avanzadas, muchas de ellas tomadas de UNIX. El sucesor de Microsoft para
MS-DOS, WINDOWS, originalmente se ejecutaba encima de MS-DOS (es decir, era más
un shell que un verdadero sistema operativo), pero a partir de 1995 se produjo
una versión autosuficiente de WINDOWS,
WINDOWS
95 ®, de modo que ya no se necesita MS-DOS para apoyarlo. Otro sistema
operativo de Microsoft es
WINDOWS
NT, que es compatible con WINDOWS 95 en cierto nivel, pero internamente se
reescribió desde cero.
El otro
competidor importante es UNIX, que domina en las estaciones de trabajo y otras
computadoras del extremo alto, como los servidores de red. UNIX es popular
sobre todo en máquinas basadas en chips RISC de alto rendimiento. Estas
máquinas por lo regular tienen la potencia de cómputo de una minicomputadora, a
pesar de estar dedicadas a un solo usuario, por lo que resulta lógico que estén
equipadas con un sistema operativo diseñado originalmente para
minicomputadoras, a saber, UNIX. Una tendencia interesante que apareció a
mediados de la década de 1980 fue el crecimiento de redes de computadoras
personales en las que se ejecutan sistemas
operativos de red o sistemas operativos distribuidos (Tanenbaum, 1995). En un
sistema operativo de red los usuarios están conscientes de la existencia de
múltiples computadoras y pueden ingresar en máquinas remotas y copiar archivos
de una máquina a otra. Cada máquina ejecuta su propio sistema operativo local y
tiene su propio usuario o usuarios locales.
Los sistemas operativos de red no son fundamentalmente
distintos de aquellos para un solo procesador.
Obviamente,
estos sistemas necesitan un controlador de la interfaz con la red y software de
bajo nivel para operarlo, así como programas para realizar inicios de sesión
remotos y acceso a archivos remotos, pero estas adiciones no alteran la
estructura esencial del sistema operativo.
Un sistema
operativo distribuido, en cambio, presenta el mismo aspecto a los usuarios que
un sistema tradicional de un solo procesador, aunque en realidad se compone de
múltiples procesadores. Los usuarios no deben enterarse de en dónde se están
ejecutando sus programas o almacenando sus archivos; de todo eso debe
encargarse el sistema operativo automática y eficientemente.
Los
verdaderos sistemas operativos distribuidos requieren más que la adición de un
poco más de código a un sistema operativo uniprocesador, porque los sistemas
distribuidos y centralizados difieren en aspectos cruciales. Los sistemas
distribuidos, por ejemplo, a menudo permiten a las aplicaciones ejecutarse en
varios procesadores al mismo tiempo, por lo que requieren algoritmos de
planificación de más complejos a fin de optimizar el grado de paralelismo.
En muchos
casos, los retardos de comunicación dentro de la red implican que éstos (y
otros) algoritmos deban ejecutarse con información incompleta, caduca o incluso
incorrecta. Esta situación difiere radicalmente un sistema de un solo
procesador en el que el sistema operativo tiene toda la información sobré el
estado del sistema.