8. Sistemas operativos por su estructura
Tiempo de lectura: 10 minutos |
Ya hemos discutido anteriormente acerca de los componentes más comunes en un sistema operativo (véase el Capítulo 4). En esta sección comentaremos cómo se clasifican los distintos sistemas operativos según la organización e interconexión de sus componentes.
8.1. Estructura sencilla
Los sistemas con estructura sencilla se caracterizan por:
-
No tener una estructura bien definida. Los componentes no están bien separados y las interfaces entre ellos no están bien definidas.
-
Son sistemas monolíticos, dado que gran parte de la funcionalidad del sistema se implementa en el núcleo.
8.1.1. MS-DOS
Por ejemplo, en el MS-DOS los programas de aplicación podían acceder directamente a toda la memoria y a cualquier dispositivo. Disponiendo de esa libertad un programa erróneo cualquiera podía corromper el sistema completo.
Como el Intel 8086 para el que fue escrito MS-DOS no proporcionaba un modo dual de operación, los diseñadores del sistema no tuvieron más opción que dejar accesible el hardware a los programas de usuario.
8.1.2. UNIX
Otro ejemplo es el de UNIX original, donde sí había una separación clara entre procesos de usuario y código del sistema, pero juntaba un montón de funcionalidad en el núcleo del sistema.
El núcleo proporciona la planificación de CPU, la gestión de la memoria, el soporte de los sistemas de archivos y muchas otras funcionalidades del sistema operativo. En general se trata de una enorme cantidad de funcionalidad que es difícil de implementar y mantener, si no se compartimenta adecuadamente.
Tanto MS-DOS como UNIX eran originalmente sistemas pequeños y simples, limitados por las funcionalidades del hardware de su época, que fueron creciendo más allá de las previsiones originales. Lo cierto es que con mejor soporte del hardware se puede dividir el sistema operativo en piezas más pequeñas y apropiadas que las del MS-DOS y el UNIX original.
8.2. Estructura en capas
Los sistemas con estructura en capas se caracterizan por:
-
La funcionalidad se divide en capas, de tal forma que una capa solo utiliza funciones y servicios de la capa inmediatamente inferior y lo hace a través de una interfaz bien definida.
-
Como en la programación orientada a objetos, cada capa oculta a la capa superior los detalles de su implementación. Por ejemplo, las estructuras de datos internas que usa y las operaciones o el hardware de la capa inferior que utiliza.
-
Escalan mejor que los sistemas con estructura sencilla porque las capas hacen que el código esté mejor compartimentado. Por ejemplo, al corregir un bug o añadir una nueva funcionalidad solo hay que preocuparse de su efecto en la capa a la que afecta y no en todo el código del núcleo —siempre que no se altere la interfaz de la capa con el exterior—.
-
Ser menos eficiente que la de los sistemas de estructura sencilla. En cada capa los argumentos son transformados y los datos necesarios deben de ser transferidos al invocar operaciones en la capa inferior, por lo que cada una añade cierto nivel de sobrecarga al funcionamiento del sistema.
-
También son sistemas monolíticos, dado que gran parte de la funcionalidad del sistema se implementa en el núcleo, aunque ahora el núcleo esté compartimentado en capas.
Un ejemplo de este tipo de sistemas operativos es el OS/2.
8.2.1. Dificultades con el diseño
Es importante tener en cuenta que diseñar un sistema con estructura en capas no es tan sencillo como pudiera parecer. La definición de las capas y sus funcionalidades debe ser planificada cuidadosamente debido a la restricción, comentada anteriormente, de que una capa solo puede utilizar los servicios de las capas inferiores.
Por ejemplo, el planificador de la CPU suele tener información de los procesos que están en la memoria. Parte de esa información puede ser almacenada en el disco para aumentar la memoria principal disponible. Esto nos debería llevar a pensar que la gestión del almacenamiento secundario debe ir en una capa inferior a la del planificador de la CPU, para que así el segundo pueda pedir al primero que guarde los datos en disco.
Sin embargo, el planificador de la CPU debe asignar la CPU a otro proceso cuando el proceso que actualmente la ocupa solicita alguna operación de E/S —lo típico en multiprogramación—. Como es la gestión del almacenamiento secundario el que debe pedir una operación al planificador de la CPU, ahora el primero debe estar sobre el segundo.
La solución a esta dependencia circular es hacer que ambos componentes estén en la misma capa. Este tipo de dependencias no son raras, ocurre en muchos otros casos, ya que los componentes del sistema operativo suelen depender mucho unos de otros.
Al final, la solución de compromiso es tender hacia sistemas con muy pocas capas donde cada una tiene mucha funcionalidad. Esto limita mucho las ventajas de esta técnica porque no permite compartimentar el núcleo tanto como sería deseable.
8.3. Microkernel
Los sistemas con estructura microkernel se caracterizan por:
-
Eliminar todos los componentes no esenciales del núcleo e implementarlos como procesos de usuario.
-
Un núcleo microkernel proporciona funciones mínimas de gestión de procesos y de memoria y algún mecanismo de comunicación entre procesos. Sin embargo, hay que tener en cuenta que hay poco consenso a este respecto, por lo que algunos microkernel reales incluyen en el núcleo algunas funcionalidades adicionales.
-
El mecanismo de comunicación permite a los procesos de los usuarios solicitar servicios a los componentes del sistema. También sirve para que los componentes del sistema se comuniquen entre sí y se pidan servicio.
Dado que los componentes del sistema están aislados unos de otros —ya que se implementan como procesos de usuario— el mecanismo de comunicación entre procesos es la única forma que tienen los procesos de los usuarios y los componentes, de solicitarles un servicio.
Generalmente esta comunicación se implementa mediante paso de mensajes (véase el Apartado 9.8.2).
Entre los beneficios de estos sistemas operativos se incluyen:
-
Facilidad a la hora de añadir nuevas funcionalidades. Los nuevos servicios son añadidos como aplicaciones de nivel de usuario, por lo que no es necesario hacer modificaciones en el núcleo. Desarrollar en el modo privilegiado siempre es más peligrosos que en el modo usuario porque los errores pueden ser catastróficos: bloqueo o caída del sistema, corrupción de datos, etc.
-
Facilidad a la hora de llevar el sistema a otras plataformas. Puesto que el núcleo es muy pequeño, resulta muy sencillo de portar a otras plataformas.
-
Más seguridad y fiabilidad. Puesto que la mayor parte de los servicios se ejecutan como procesos separados de usuario, un servicio que falla no puede afectar a otros ni puede ser utilizado para ganar acceso a otros servicios o al núcleo. Además se pueden implementar estrategias para mejorar la tolerancia a fallos, como reiniciar un servicio que ha fallado, como si fuera un programa cualquiera.
8.3.1. Rendimiento
El mayor inconveniente es el pobre rendimiento que puede tener, causado por la sobrecarga que añade el mecanismo de comunicación.
Por ejemplo, Microsoft Windows NT nació con una estructura de microkernel en capas donde una parte importante de los servicios eran proporcionados por unos procesos de usuario llamados subsistemas.
El sistema operativo podía mostrar diferentes personalidades o entornos operativos —básicamente de OS/2, POSIX y MS-DOS— a través del uso de subsistemas ambientales, que también se ejecutaban como procesos de usuario. Las aplicaciones de Microsoft Windows NT se comunicaban con estos subsistemas utilizando un mecanismo de comunicación denominado LPC (Local Inter-Process Communication).
Con esta estructura, la pérdida de rendimiento respecto a Microsoft Windows 95 era tan importante —especialmente en lo relativo a operaciones gráficas— que los diseñadores se vieron obligados a mover más servicios al espacio del núcleo en la versión 4.0. El resultado es que los Windows sucesores a Windows NT 4.0 tienen una arquitectura más monolítica que microkernel, ya que aunque muchos servicios siguen siendo proporcionados por procesos de usuario, esto solo ocurre con aquellos donde el rendimiento no es un factor crítico.
Microsoft Windows XP tiene 280 llamadas al sistema a las que hay que sumar las más de 650 llamadas del subsistema gráfico, que también se aloja en el núcleo desde Microsoft Windows NT 4.0. Mientras que Microsoft Windows NT 3.51 tenía algo menos de 200 llamadas al sistema. |
Sin embargo, varios sistemas operativos siguen utilizando núcleos microkernel, como QNX o MINIX 3. Ambos son sistemas operativos de tiempo real, que basan en la estructura de microkernel su estabilidad como sistema para tareas críticas.
En la Figura 25, por ejemplo, se puede observar un esquema de MINIX 3. El núcleo es muy pequeño —apenas tiene 5000 líneas de código— por lo que la mayor parte de la funcionalidad reside en los procesos de servicios y de controladores de dispositivo.
MINIX 3 es un sistema compatible POSIX. Así que soporta las llamadas al sistema definidas por este estándar, pero estas se convierten en mensajes enviados al servidor correspondiente con la petición, y no en llamadas directas al núcleo. Para que un servidor pueda atender una petición, quizás tenga que enviar peticiones a otros servidores o controladores de dispositivo. Incluso pueden tener que hacer llamadas al núcleo, para solicitar alguna operación privilegiada que no se puede implementar en el modo usuario. Por ejemplo, operaciones de E/S —fundamentales para los controladores de dispositivo— o el acceso a tablas del núcleo —como la tabla de procesos—.
Es este trasiego de mensajes con peticiones y respuestas —y la correspondiente conmutación de procesos en la CPU para ejecutar el proceso que atiende cada mensaje— para resolver una petición de un proceso de usuario, lo que teóricamente justifica el menor rendimiento de los sistemas microkernel.
8.4. Estructura modular
Los sistemas con estructura modular se caracterizan por:
-
Dividir el núcleo en módulos, cada uno de los cuales implementa funciones y servicios concretos y se comunican entre sí a través de una interfaz bien definida.
-
Como en la programación orientada a objetos, cada módulo oculta al resto los detalles de su implementación.
-
Todos los módulos pueden llamar a funciones de la interfaz de cualquier otro módulo, a diferencia de los sistemas operativos con estructura en capas, donde una capa solo podía usar a la inmediatamente inferior.
-
También son sistemas monolíticos, dado que gran parte de la funcionalidad del sistema se implementa en el núcleo, aunque ahora el núcleo esté compartimentado en módulos.
Estos núcleos suelen disponer de un pequeño conjunto de componentes fundamentales que se cargan durante el arranque. Posteriormente pueden cargar módulos adicionales, tanto durante la inicialización del sistema como en tiempo de ejecución.
En este aspecto se asemejan a los núcleos microkernel, ya que el módulo principal solo tiene funciones básicas. Sin embargo los núcleos modulares:
-
Son más eficientes al no necesitar un mecanismo de comunicación, puesto que los componentes se cargan en la memoria destinada al núcleo, por lo que pueden llamarse directamente.
-
Son menos seguros y fiables, puesto que gran parte de su funcionalidad se ofrece desde el modo privilegiado. Un error en cualquier componente puede comprometer o hacer caer el sistema.
Este tipo de estructura es la utilizada en los UNIX modernos, como Oracle/Sun Microsystems Solaris, FreeBSD, Linux (véase la Figura 26) y macOS.