Sistemas Operativos

Los sistemas operativos son una de las mejores áreas de la computación. Esta área abarca todo lo que tiene que ver con computadoras, en cualquier plataforma y cualquier formato móvil o personal y de escritorio, e incluso en algunos sistemas anidados (embedded).

Un sistema operativo es básicamente el kernel, pero técnicamente, al adentrarnos a desarrollar un sistema operativo, es un error pensar en términos solo del kernel, porque pensar de esa manera evita que podamos avanzar, y en su lugar nos quedamos con una visión incompleta y simplista de lo que es un sistema operativo.

Para que un programa autónomo que corre directamente sobre la máquina en bruto (bare metal), pueda ser llamado un sistema operativo, por lo menos tiene que ser capaz de manejar los dispositivos más básicos y estandarizados de la máquina (CPU, teclado, ratón, discos duros, CDs/DVDs, disqueteras, puertos serial, paralelo, USB, PCI, dispositivos ISA, video), tiene que ser capaz de administrar dinámicamente la memoria y por lo menos la creación y terminación de procesos, tiene que tener una estructura decente de los componentes del kernel y sus módulos, y necesita poder manejar archivos, guardarlos, leerlos, eliminarlos y mostrarlos. Para que sea utilizable, y por lo tanto un verdadero sistema operativo, necesita por lo menos la interfaz de una consola de texto para que el usuario pueda dirigir la computadora.

Todo esto puede llevarle muchos años a un desarrollador, especialmente a uno que es novato y/o que no tiene fundamentos excelentes en todo tipo de algoritmos genéricos y especializados. También es necesario que tenga un dominio serio, completo, confiable, robusto, de por lo menos los lenguajes C y ensamblador.

Todo esto, quiera aceptarse o no, conlleva rápidamente a la necesidad de conocer convenciones de bajo nivel del compilador, formatos de archivo objeto como ELF, PE/COFF, AOUT, principalmente. Requiere también ser capaz de optimizar secciones críticas de código, que pueden ejecutarse millones de veces por segundo y por lo tanto necesitan ser tan rápidas y transparentes como sea posible.

El ambiente de una máquina cruda es el primer framework maestro que hay que dominar, y por el que estamos condicionados en toda plataforma para la que tengamos información para programar su hardware de manera completa o casi completa.

Es muy fácil confundirse rápidamente y no poder decidirse por dónde comenzar. Es cierto que no importa en qué orden progresemos ya que el progreso tiene una propiedad conmutativa como la suma (no importa en qué orden ganemos experiencia siempre y cuando la ganemos y conservemos), y aquí más que en otras tareas de desarrollo tenemos que valernos de ese hecho, asegurándonos de convertir todos los logros de implementaciones en nanoalgoritmos, para que cuando lleguemos a una parte cíclica del camino de desarrollo y quedemos estancados, podamos por lo menos reiniciar con muy poco esfuerzo el desarrollo de un nuevo intento.

En un kernel y sus componentes de sistema no hay nada que no dependa del resto de partes del programa, y por eso es casi seguro que si no sabemos de antemano todo lo que necesitamos hacer, vamos a entrar en nudos ciegos irremediables sin reimplementar todo. Es por eso que es necesario estar enfocado, regirse por estándares hasta donde estos existan en lugar de tratar de inventar demasiadas cosas nuevas casi seguramente incompatibles con la computación ya existente (especialmente si inventamos precisamente para tratar de escapar del hecho de que no entendemos las cosas que ya existen, que de por sí es un mal precedente).

Tenemos que asegurarnos de definir, plasmado en un documento físico, la estructura exacta, correcta y completa de las dependencias de cada componente. Qué se necesita construír primero, y completar, y dominar cada uno de sus detalles y combinaciones, para tener una base sólida y sin agujeros sobre la que se pueda construír la siguiente escala de bloques.

Es por eso que, por tedioso que parezca, debemos centrarnos solo en una pieza del desarrollo hasta que esté totalmente depurada y hayamos logrado tener una opinión confiable de verdaderos expertos para saber que la finalización de esa pieza es correcta. Podemos por supuesto guardar todo en su forma correspondiente de nanoalgoritmos, que por naturaleza carecen de una secuencia con respecto a los otros de un mismo nivel y un mismo tema y alcance, pero al momento de programar para el kernel y los componentes del sistema, debemos ser rigurosos y hacer que nuestro código escrito sea sólido y sin grietas, o simplemente no programar sobre el proyecto final hasta que obtengamos la experiencia necesaria para continuar, aunque eso tenga que llevarse algunos meses.

_____________________________________________________________________________________
_____________________________________________________________________________________
_____________________________________________________________________________________
_____________________________________________________________________________________
_____________________________________________________________________________________
_____________________________________________________________________________________
_____________________________________________________________________________________
_____________________________________________________________________________________

Para lograr una verdadera y efectiva progresión en un tiempo razonable en nuestros esfuerzos por desarrollar un kernel/sistema operativo, necesitamos seguir estos lineamientos:

- Comparar con Linux usándolo. Esto significa que necesitamos leer de hecho el código fuente de Linux. En mayor medida debemos centrarnos en una versión estática del código, que no actualicemos hasta comprender nuevas porciones y componentes. También significa que vamos a intentar correr los comandos de consola, y sobre todo realizar todo tipo de configuraciones administrativas, para demostrar ante nosotros mismos y de forma práctica, que estamos entendiendo los conceptos de los sistemas operativos (por ejemplo, elegir correctamente y de forma manual el protocolo de mouse correcto para el servidor X, ya sea PS/2, Microsoft (serial), tomando en cuenta 3 botones, USB, etc...)

- Comenzar por la funcionalidad en nuestro kernel. No tiene sentido programar algo que un usuario no pueda dirigir según su voluntad sin tener que recompilar toda la distribución de código fuente. Eso significa que lo primero que necesitamos programar es una consola con comandos estandarizados, para desde allí probar lo que implementemos, y podamos gradualmente completar el kernel, sus módulos, y la consola, hasta convertirla en un programa externo de shell POSIX y/o de otros tipos. Todo esto lográndolo manteniendo una consola útil, y luego una GUI completa, para probar nuestro sistema operativo. La siguiente prioridad a lograr de manera urgente es la capacidad de leer y guardar archivos permanentes en unidades de disco duro y diskette, para comenzar. Esto nos va a permitir comenzar a crear una base sólida para probar algoritmos más complejos. Lo siguiente es la administración de memoria, y luego la administración de tareas. Todo esto puede llevarse perfectamente un año, o más, trabajando en tiempo completo, si se es un novato y/o si no se tienen bases sólidas de programación avanzada.



El objetivo de todo esto es migrar todo el conocimiento de más alta calidad, propio del desarrollo de sistemas operativos, al ámbito de la programación normal. Eso significa que no debemos olvidar el nivel de programación de aplicaciones, y a su vez significa que necesitamos leer el código fuente de otros programas, como eMule, GIMP, VLC Media Player, entre muchos otros, usando nuestras técnicas de lectura extremadamente rápida.

Al fin de cuentas y sobre todo, los sistemas operativos no son más que un paquete enorme de utilidades, algoritmos, librerías, interfaces y software de administración del hardware para que constituya un elemento del mundo real.



Prioridades de Desarrollo


Ahora necesitamos definir exactamente en qué orden es más natural, y genuinamente progresivo, desarrollar un sistema operativo, idealmente solo y solo cuando cada prioridad ha completado su implementación al 100%: Un orden en el que existen interdependencias, y que constituye todos los requisitos obligatorios para el siguiente componente que agregar, que no sea posible agregar si falta alguno de los elementos anteriores.

La primer prioridad es:

Interfaces de los Sistemas Operativos Existentes.
   Necesitamos saber cómo llevar a cabo las diferentes tareas a nivel de sistema y a nivel de aplicación. El propósito de cualquier sistema operativo es poner a disposición de usuarios y desarrolladores esos recursos e interfaces de control, administración, programación y utilización, principalmente mediante APIs y algún tipo de objetos privilegiados para periféricos, memoria y procesos (por ejemplo, con archivos especiales en Linux), además del shell, sea de tipo consola o de interfaz gráfica de usuario (con todos sus widgets y funcionalidades).

En otras palabras, necesitamos tener una experticia seria en crear aplicaciones avanzadas de usuario, en por lo menos uno de los sistemas operativos ya conocidos, y eso implica también tener una experticia igualmente seria en por lo menos un lenguaje de programación, preferiblemente C, y herramientas asociadas para construír y compilar el proyecto como uno ejecutable. Si carecemos de esa experticia, es vital adquirirla antes de esperar ser realmente competentes a nivel de desarrollo de un kernel.


La segunda prioridad es:

Práctica General en el Manejo Directo del Hardware.
   Necesitamos pasar un tiempo considerable intentando entender cómo programar correctamente los diferentes dispositivos estándar de hardware. Cualquier cosa que nos parezca divertida y que nos presente retos que nos interesen. Por ejemplo, leer/escribir discos duros, diskettes, manejar los LEDs de un teclado PS/2 con un patrón, activar el modo gráfico, apagar la computadora con software, activar el mouse serial y PS/2, leer los datos de configuración y de los periféricos en el bus PCI, abrir la bandeja del CD-ROM y volver a cerrarla, hacer girar el motor y detenerlo, detectar discos duros y unidades de CDs/DVDs en puertos IDE y SATA, detectar la cantidad de memoria instalada, detectar, activar y desactivar los diferentes núcleos del CPU, entrar al Modo Protegido (32 bits) y al Modo Largo (64 bits), etc...


Esta clase de tareas se llevan a cabo de mejor forma la primera vez que se investigan, y es vital guardarlas y registrarlas en forma de nanoalgoritmos a medida que se van aprendiendo. De lo contrario esfuerzos duplicados se vuelven extremadamente tediosos. Pero por lo menos, mientras se tengan “ediciones” anteriores del código producido en nuestros esfuerzos, tenemos la posibilidad absoluta de separar las partes importantes y reutilizables del código en forma de nanoalgoritmos.


La tercera prioridad es de hecho nuestra primera tarea formal en el ámbito de los sistemas operativos:

Bootear la Computadora.
   Desde aquí, necesitamos tomar nota especial de todos los algoritmos y técnicas necesarias para maniobrar libremente en esta primer tarea. Bootear la computadora requiere tener un medio, y esos medios incluyen diskettes, CDs/DVDs, memorias USB, discos duros, la red, y cualquier otro medio que podamos establecer mediante el BIOS o manualmente para recuperar código ejecutable autónomo capaz de tomar el control de la PC. A su vez, el boot puede tener una dos o más etapas, en donde la etapa final es cargar el kernel a la memoria y pasarle el control. Normalmente se usan boots de dos etapas en sistemas profesionales porque esto permite mucha mayor facilidad de cargar un administrador de boot en la primer etapa, y obtener información de configuración vital del BIOS, que no se puede obtener de manera estable de ninguna otra forma, para finalmente cargar el kernel. La primera etapa del kernel normalmente solo contiene menos de 512 bytes de código de máquina, y según los estándares más comunes (como los CDs/DVDs booteables) hasta 2KB, que es demasiado poco para llevar a cabo todas las tareas significativas necesarias. Pero es suficiente para cargar un administrador de segunda etapa (second stage boot manager) que puede tener por lo menos 64KB, y puede tener fácilmente unos 128KB o más, si tiene una parte de modo protegido capaz de usar por lo menos 4GB de memoria. También, tenemos la opción de usar la funcionalidad del BIOS en la mayoría de computadoras actuales y más “antiguas”, y EFI/UEFI en algunas máquinas recientes, pero aparentemente no dominante como el clásico BIOS.



Hay otra tarea alternativa, que es tan útil como aprender del booteo, y tiene una aplicación más extendida:

Tecnología de Compiladores.
   Esta es una opción alterna en este punto del desarrollo de un sistema operativo, y es una de mis principales opciones favoritas. Podemos optar por crear un compilador para un lenguaje propio nuestro, y de allí, con la experiencia ganada, extendernos a compiladores o ensambladores y herramientas relacionadas para lenguajes de programación estándar. Esto tiene la ventaja de que podemos aplicar una parte importante de los conceptos de sistemas operativos, al crear aplicaciones con un compilador propio, que de hecho interactúan con los sistemas operativos para los que están diseñados a correr.


Sobre todo, necesitamos encontrar una utilidad absolutamente inmediata en todo lo que hagamos. Cuando logremos bootear, tenemos que encontrar una razón, un motivo, y una aplicación útil para eso y para lo que le sigue inmediatamente después, a nivel práctico y a nivel de desarrollo. De lo contrario, el proyecto no puede sobrevivir ante la abundancia mucho mayor de material y tareas cotidianas. No importa si son solo componentes los que puedan aplicarse inmediatamente en algo más, pero debe ser contundentemente aplicable para poder comparar los resultados supuestos y teóricos en el mundo real y tener una base física sobre la que seguir avanzando, más allá de las ideas y código “experimental” plasmado.



Tutoriales Disponibles


Existe una variedad impresionante de tutoriales, documentos técnicos, código fuente, y foros, tanto en este sitio como en muchos otros, sobre el desarrollo de sistemas operativos. El problema es que todos estos solamente se centran en el “boiler plate”, o “esqueleto”, para la programación de un kernel, y/o para programar los dispositivos de hardware más comunes, así como usar las herramientas de desarrollo, principalmente herramientas GNU, como GCC, NASM, Make, etc.

El problema con esto es que hay muchísimas otras áreas que necesitan también de un tratamiento a profundidad, siendo los más importantes la administración de memoria (de la cual hay diferentes niveles, como son físico, segmentado, paginado, y de alto nivel, del lado de las aplicaciones de usuario), el manejo de lectura/escritura y formateo, entre muchas otras tareas de soporte de bajo nivel, para los sistemas de archivos, la administración de los procesos y tareas, la programación de las herramientas de desarrollo y APIs nativas, las interfaces de consola, gráficas y de red, para el usuario (lo que implica subsistemas de red) y uno o más formatos nativos para correr las aplicaciones, sin mencionar la capacidad de correr software y librerías de terceros, como Java.

Un kernel no es un sistema operativo; este es solo el objeto que amalgama y dirige de forma maestra el resto de subsistemas. Un sistema operativo es software complejo con todos esos subsistemas, que además son capaces de serle de utilidad a un usuario final, sea este un usuario de escritorio, o un usuario de máquinas automatizadas.

A pesar de lo que se dice comúnmente, se necesitaría la cantidad actual de tutoriales para cada faceta de los sistemas operativos (que no es una poca cantidad), para que estos temas se vuelvan tan fáciles como los que se han tratado en la actualidad, gracias a lo cual se conoce de manera muy accesible lo que se requiere y el orden en que hay que trabajar, para escribir un kernel a nivel de esqueleto, y la programación de hardware también a nivel fundamental, que es algo requerido, pero para lo que no existen tutoriales de las subsiguientes etapas del desarrollo, y por lo tanto, por lo menos en 2012, no existe información fácilmente accesible para todos, para darle forma de manera profesional, y encapsular las diferentes partes y algoritmos operativos, en forma de verdaderos subsistemas, sin lo cual, el único resultado posible es una incapacidad absoluta de poder seguir avanzando en el desarrollo. Quienes han logrado crear, no solamente el kernel, sino todos sus subsistemas para la máquina, y sus respectivas interfaces para el usuario, lo han logrado porque cuentan de manera privada con todos esos requisitos, y más, y trabajan de manera pública y privada en grupos, no individualmente.

La siguiente es una lista de funcionalidades y/o subsistemas que deberíamos comprender intrincadamente en nuestro sistema operativo cotidiano actual (Window, Linux, Mac), antes de siquiera pensar en poder programarlos por nosotros mismos. Esta es una lista resumida a nivel MACROSCÓPICO (a grandes rasgos). Es precisamente esta lista de temas para la que hasta ahora NO EXISTE un gran, abundante y claro, de alta calidad, repositorio de tutoriales, documentos y código de estudio, didáctico y de grado industrial, como sí lo existe, pero solamente para los temas de la creación del esqueleto del kernel y el manejo genérico, sin efectuarse a través de subsistemas aislados y bien definidos, de todos los diferentes componentes de hardware y CPU. Y hay que mencionar que hay muchos componentes e interfaces de hardware, como la administración óptima de memoria, ACPI, e incluso la programación de los buses y periféricos USB, y la mayoría de funciones interesantes del IDE/ATA/ATAPI (en su forma PATA o SATA) para los que la documentación útil y completa en forma de tutoriales y código didáctico también es virtualmente inexistente, en 2012.

• Memoria.

• Archivos y Tareas de Sistemas de Archivos.

• Entrada/Salida de Consolas de Texto.

• Administradores Gráficos de Ventanas.

• Video Directo.

• Audio.

• Red.

• Otros tipos de control de Entrada/Salida y de Hardware.

• Acciones Especiales y Privilegiadas (Creación, Terminación, Detener —Halting—, Reinicio, Duplicación y Priorización de Tareas).

• Carga/Descarga de Drivers y Ubicación/Reubicación/Reserva/Liberación de Memoria.

• Privilegios de Hardware, de Sistema y de Usuario.

• Reinicio/Apagado/Suspensión/Hibernación de la Computadora (lo que requiere generalmente ACPI).

• Administración de Componentes Específicos del Sistema Operativo (Registro de Windows, Formatos Ejecutables Nativos, Rutas de Sistema, POSIX, Interfaz de Drivers, Plataforma NT, Plataforma Windows 9x, Errores —Bugs— Específicos de una Versión de Sistema...)




Lista de Sistemas Operativos con Código Fuente


El propósito de esta lista de sistemas operativos no tradicionales (diferentes de Microsoft Windows o GNU/Linux) es no solo hacer más conocido este software de terceros, sino analizar el código, explicarlo y aprender de este:

ReactOS
El mejor clon, o probablemente el único, de Windows NT 5 (mejor conocido como Windows XP).

Sitio: http://reactos.org

Copia Local del Código, Versión 0.3.14: [Aquí]


MenuetOS
Un sistema operativo pequeño pero muy completo, y también relativamente fácil de entender, escrito totalmente en Ensamblador de FASM, relativamente fácil de traducir a NASM/YASM. Diseñado para correr desde una diskettera.

Sitio: http://menuetos.net

Copia Local del Código, Versión 0.85B: [Aquí]


Etapas de Desarrollo para Alcanzar el Nivel de Sistema Operativo


Esta es una lista de las etapas necesarias, en el orden natural en el que yo personalmente he avanzado, para acumular el conocimiento, el código y la funcionalidad necesarios para poder alcanzar el nivel de desarrollo necesario para comprender los conceptos de sistemas operativos a un nivel que sea útil para los usuarios finales, y para los desarrolladores que lleguen a comprender estos conceptos.

Etapa 1
En esta etapa hablamos del hardware, software y algoritmos más básicos y simples, y la manera de acceder la funcionalidad básica más simple del hardware de una PC. Esta etapa contiene los fundamentos más simples y comunes. Temas más avanzados deben estar en otra etapa, acorde al nivel secuencial de complejidad y dependencias.