El núcleo es un programa informático que se encuentra en el núcleo del sistema operativo de una computadora y generalmente tiene control completo sobre todo lo que hay en el sistema. El núcleo también es responsable de prevenir y mitigar conflictos entre diferentes procesos. [1] Es la parte del código del sistema operativo que siempre reside en la memoria [2] y facilita las interacciones entre los componentes de hardware y software. Un núcleo completo controla todos los recursos de hardware (por ejemplo, E/S, memoria, criptografía) a través de controladores de dispositivos , arbitra conflictos entre procesos relacionados con dichos recursos y optimiza la utilización de recursos comunes, por ejemplo , uso de CPU y caché , sistemas de archivos y conectores de red. En la mayoría de los sistemas, el núcleo es uno de los primeros programas que se cargan al iniciar (después del cargador de arranque ). Maneja el resto del inicio, así como la memoria, los periféricos y las solicitudes de entrada/salida (E/S) del software , traduciéndolas en instrucciones de procesamiento de datos para la unidad central de procesamiento .
El código crítico del núcleo se carga normalmente en un área separada de la memoria, que está protegida del acceso por parte del software de aplicación u otras partes menos críticas del sistema operativo. El núcleo realiza sus tareas, como ejecutar procesos, administrar dispositivos de hardware como el disco duro y manejar interrupciones, en este espacio protegido del núcleo . Por el contrario, los programas de aplicación como los navegadores, procesadores de texto o reproductores de audio o vídeo utilizan un área separada de la memoria, el espacio de usuario . Esta separación evita que los datos del usuario y los datos del núcleo interfieran entre sí y provoquen inestabilidad y lentitud, [1] así como evita que las aplicaciones que funcionan mal afecten a otras aplicaciones o bloqueen todo el sistema operativo. Incluso en sistemas en los que el núcleo está incluido en los espacios de direcciones de aplicación , se utiliza la protección de la memoria para evitar que aplicaciones no autorizadas modifiquen el núcleo.
La interfaz del núcleo es una capa de abstracción de bajo nivel . Cuando un proceso solicita un servicio del núcleo, debe invocar una llamada al sistema , normalmente a través de una función contenedora .
Existen diferentes diseños de arquitectura de kernel. Los kernels monolíticos se ejecutan completamente en un único espacio de direcciones con la CPU ejecutándose en modo supervisor , principalmente por velocidad. Los microkernels ejecutan la mayoría, pero no todos, de sus servicios en el espacio de usuario, [3] como lo hacen los procesos de usuario, principalmente por resiliencia y modularidad . [4] MINIX 3 es un ejemplo notable de diseño de microkernel. El kernel de Linux es a la vez monolítico y modular, ya que puede insertar y eliminar módulos de kernel cargables en tiempo de ejecución.
Este componente central de un sistema informático es el encargado de ejecutar los programas. El núcleo es el encargado de decidir en cada momento cuál de los muchos programas en ejecución debe asignarse al procesador o procesadores.
La memoria de acceso aleatorio (RAM) se utiliza para almacenar tanto las instrucciones del programa como los datos. [a] Normalmente, ambos deben estar presentes en la memoria para que un programa se ejecute. A menudo, varios programas querrán acceder a la memoria, lo que con frecuencia demandará más memoria de la que la computadora tiene disponible. El núcleo es responsable de decidir qué memoria puede usar cada proceso y determinar qué hacer cuando no hay suficiente memoria disponible.
Los dispositivos de E/S incluyen, entre otros, periféricos como teclados, ratones, unidades de disco, impresoras, dispositivos USB , adaptadores de red y dispositivos de visualización . El núcleo proporciona métodos convenientes para que las aplicaciones utilicen estos dispositivos, que normalmente están abstraídos por el núcleo, de modo que las aplicaciones no necesitan conocer sus detalles de implementación.
Los aspectos clave necesarios en la gestión de recursos son definir el dominio de ejecución ( espacio de direcciones ) y el mecanismo de protección utilizado para mediar el acceso a los recursos dentro de un dominio. [5] Los núcleos también proporcionan métodos para la sincronización y la comunicación entre procesos (IPC). Estas implementaciones pueden estar ubicadas dentro del propio núcleo o el núcleo también puede depender de otros procesos que esté ejecutando. Aunque el núcleo debe proporcionar IPC para proporcionar acceso a las facilidades proporcionadas por los demás, los núcleos también deben proporcionar a los programas en ejecución un método para realizar solicitudes de acceso a estas facilidades. El núcleo también es responsable del cambio de contexto entre procesos o subprocesos.
El núcleo tiene acceso total a la memoria del sistema y debe permitir que los procesos accedan a esta memoria de forma segura cuando la necesiten. A menudo, el primer paso para hacerlo es el direccionamiento virtual , que normalmente se logra mediante paginación y/o segmentación . El direccionamiento virtual permite al núcleo hacer que una dirección física dada parezca otra dirección, la dirección virtual. Los espacios de direcciones virtuales pueden ser diferentes para diferentes procesos; la memoria a la que accede un proceso en una dirección (virtual) particular puede ser una memoria diferente a la que accede otro proceso en la misma dirección. Esto permite que cada programa se comporte como si fuera el único (aparte del núcleo) en ejecución y, por lo tanto, evita que las aplicaciones se bloqueen entre sí. [6]
En muchos sistemas, la dirección virtual de un programa puede hacer referencia a datos que no están actualmente en la memoria. La capa de indirección proporcionada por el direccionamiento virtual permite al sistema operativo utilizar otros almacenes de datos, como un disco duro , para almacenar lo que de otro modo tendría que permanecer en la memoria principal ( RAM ). Como resultado, los sistemas operativos pueden permitir que los programas utilicen más memoria de la que el sistema tiene físicamente disponible. Cuando un programa necesita datos que no están actualmente en la RAM, la CPU envía una señal al núcleo de que esto ha sucedido y el núcleo responde escribiendo el contenido de un bloque de memoria inactivo en el disco (si es necesario) y reemplazándolo con los datos solicitados por el programa. El programa puede entonces reanudarse desde el punto en el que se detuvo. Este esquema se conoce generalmente como paginación por demanda .
El direccionamiento virtual también permite la creación de particiones virtuales de memoria en dos áreas separadas, una reservada para el núcleo ( espacio del núcleo ) y la otra para las aplicaciones ( espacio del usuario ). El procesador no permite que las aplicaciones accedan a la memoria del núcleo, lo que impide que una aplicación dañe el núcleo en ejecución. Esta partición fundamental del espacio de memoria ha contribuido mucho a los diseños actuales de núcleos de propósito general reales y es casi universal en dichos sistemas, aunque algunos núcleos de investigación (por ejemplo, Singularity ) adoptan otros enfoques.
Para realizar funciones útiles, los procesos necesitan acceder a los periféricos conectados a la computadora, que son controlados por el núcleo a través de controladores de dispositivos . Un controlador de dispositivo es un programa de computadora que encapsula, monitorea y controla un dispositivo de hardware (a través de su Interfaz de hardware/software (HSI)) en nombre del sistema operativo. Proporciona al sistema operativo una API, procedimientos e información sobre cómo controlar y comunicarse con una determinada pieza de hardware. Los controladores de dispositivos son una dependencia importante y vital para todos los sistemas operativos y sus aplicaciones. El objetivo de diseño de un controlador es la abstracción; la función del controlador es traducir las llamadas de función abstractas exigidas por el sistema operativo (llamadas de programación) en llamadas específicas del dispositivo. En teoría, un dispositivo debería funcionar correctamente con un controlador adecuado. Los controladores de dispositivos se utilizan, por ejemplo, para tarjetas de video, tarjetas de sonido, impresoras, escáneres, módems y tarjetas de red.
A nivel de hardware, las abstracciones comunes de los controladores de dispositivos incluyen:
Y a nivel de software, las abstracciones del controlador de dispositivo incluyen:
Por ejemplo, para mostrarle algo al usuario en la pantalla, una aplicación haría una solicitud al núcleo, que la reenviaría a su controlador de pantalla, que luego es responsable de trazar el carácter/píxel. [6]
Un núcleo debe mantener una lista de dispositivos disponibles. Esta lista puede ser conocida de antemano (por ejemplo, en un sistema integrado donde el núcleo se reescribirá si cambia el hardware disponible), configurada por el usuario (típico en PCs más antiguas y en sistemas que no están diseñados para uso personal) o detectada por el sistema operativo en tiempo de ejecución (normalmente llamado plug and play ). En sistemas plug-and-play, un administrador de dispositivos primero realiza un escaneo en diferentes buses periféricos , como Peripheral Component Interconnect (PCI) o Universal Serial Bus (USB), para detectar dispositivos instalados, luego busca los controladores apropiados.
Como la gestión de dispositivos es un tema muy específico del sistema operativo , estos controladores se manejan de forma diferente en cada tipo de diseño de kernel, pero en todos los casos, el kernel tiene que proporcionar la E/S para permitir que los controladores accedan físicamente a sus dispositivos a través de algún puerto o ubicación de memoria. Se deben tomar decisiones importantes al diseñar el sistema de gestión de dispositivos, ya que en algunos diseños los accesos pueden implicar cambios de contexto , lo que hace que la operación requiera un uso intensivo de la CPU y fácilmente cause una sobrecarga de rendimiento significativa. [ cita requerida ]
En informática, una llamada al sistema es la forma en que un proceso solicita un servicio del núcleo de un sistema operativo que normalmente no tiene permiso para ejecutar. Las llamadas al sistema proporcionan la interfaz entre un proceso y el sistema operativo. La mayoría de las operaciones que interactúan con el sistema requieren permisos que no están disponibles para un proceso de nivel de usuario, por ejemplo, la E/S realizada con un dispositivo presente en el sistema o cualquier forma de comunicación con otros procesos requiere el uso de llamadas al sistema.
Una llamada al sistema es un mecanismo que utiliza el programa de aplicación para solicitar un servicio del sistema operativo. Utilizan una instrucción de código de máquina que hace que el procesador cambie de modo. Un ejemplo sería del modo supervisor al modo protegido. Aquí es donde el sistema operativo realiza acciones como acceder a dispositivos de hardware o a la unidad de administración de memoria . Generalmente, el sistema operativo proporciona una biblioteca que se encuentra entre el sistema operativo y los programas de usuario normales. Por lo general, es una biblioteca C como Glibc o Windows API. La biblioteca maneja los detalles de bajo nivel de pasar información al núcleo y cambiar al modo supervisor. Las llamadas al sistema incluyen cerrar, abrir, leer, esperar y escribir.
Para que un proceso pueda realizar un trabajo realmente útil, debe poder acceder a los servicios que ofrece el núcleo. Esto se implementa de forma diferente en cada núcleo, pero la mayoría proporciona una biblioteca C o una API que, a su vez, invoca las funciones relacionadas del núcleo. [7]
El método para invocar la función del núcleo varía de un núcleo a otro. Si se utiliza el aislamiento de memoria, es imposible que un proceso de usuario invoque el núcleo directamente, porque eso violaría las reglas de control de acceso del procesador. Algunas posibilidades son:
Una consideración importante en el diseño de un núcleo es el soporte que proporciona para la protección contra fallos ( tolerancia a fallos ) y contra comportamientos maliciosos ( seguridad ). Estos dos aspectos no suelen distinguirse claramente, y la adopción de esta distinción en el diseño del núcleo conduce al rechazo de una estructura jerárquica para la protección . [5]
Los mecanismos o políticas proporcionados por el núcleo se pueden clasificar según varios criterios, entre ellos: estáticos (aplicados en tiempo de compilación ) o dinámicos (aplicados en tiempo de ejecución ); preventivos o post-detección; según los principios de protección que satisfacen (por ejemplo, Denning [8] [9] ); si son compatibles con hardware o basados en lenguaje; si son más un mecanismo abierto o una política vinculante; y muchos más.
El soporte para dominios de protección jerárquica [10] normalmente se implementa utilizando modos de CPU .
Muchos núcleos proporcionan la implementación de "capacidades", es decir, objetos que se proporcionan al código de usuario y que permiten un acceso limitado a un objeto subyacente administrado por el núcleo. Un ejemplo común es el manejo de archivos: un archivo es una representación de información almacenada en un dispositivo de almacenamiento permanente. El núcleo puede ser capaz de realizar muchas operaciones diferentes, incluidas leer, escribir, eliminar o ejecutar, pero es posible que a una aplicación de nivel de usuario solo se le permita realizar algunas de estas operaciones (por ejemplo, es posible que solo se le permita leer el archivo). Una implementación común de esto es que el núcleo proporcione un objeto a la aplicación (normalmente llamado "identificador de archivo") sobre el cual la aplicación puede invocar operaciones, cuya validez el núcleo verifica en el momento en que se solicita la operación. Un sistema de este tipo se puede ampliar para cubrir todos los objetos que administra el núcleo y, de hecho, los objetos proporcionados por otras aplicaciones de usuario.
Una forma eficiente y sencilla de proporcionar soporte de hardware para las capacidades es delegar a la unidad de gestión de memoria (MMU) la responsabilidad de verificar los derechos de acceso para cada acceso a la memoria, un mecanismo llamado direccionamiento basado en capacidades . [11] La mayoría de las arquitecturas informáticas comerciales carecen de dicho soporte de la MMU para las capacidades.
Un enfoque alternativo consiste en simular capacidades utilizando dominios jerárquicos comúnmente admitidos. En este enfoque, cada objeto protegido debe residir en un espacio de direcciones al que la aplicación no tiene acceso; el núcleo también mantiene una lista de capacidades en dicha memoria. Cuando una aplicación necesita acceder a un objeto protegido por una capacidad, realiza una llamada al sistema y el núcleo comprueba si la capacidad de la aplicación le otorga permiso para realizar la acción solicitada y, si está permitido, realiza el acceso para ella (ya sea directamente o delegando la solicitud a otro proceso de nivel de usuario). El costo de rendimiento de la conmutación del espacio de direcciones limita la viabilidad de este enfoque en sistemas con interacciones complejas entre objetos, pero se utiliza en los sistemas operativos actuales para objetos a los que no se accede con frecuencia o que no se espera que funcionen rápidamente. [12] [13]
Si el firmware no admite mecanismos de protección, es posible simular la protección a un nivel superior, por ejemplo simulando capacidades mediante la manipulación de tablas de páginas , pero existen implicaciones en el rendimiento. [14] Sin embargo, la falta de soporte de hardware puede no ser un problema para los sistemas que eligen usar protección basada en lenguaje. [15]
Una decisión importante en el diseño del núcleo es la elección de los niveles de abstracción en los que se deben implementar los mecanismos y políticas de seguridad. Los mecanismos de seguridad del núcleo desempeñan un papel fundamental en el respaldo de la seguridad en niveles superiores. [11] [16] [17] [18] [19]
Un enfoque consiste en utilizar el soporte del firmware y del núcleo para la tolerancia a fallos (véase más arriba) y, sobre esa base, crear la política de seguridad para comportamientos maliciosos (añadiendo funciones como mecanismos de criptografía cuando sea necesario), delegando parte de la responsabilidad al compilador . Los enfoques que delegan la aplicación de la política de seguridad al compilador o al nivel de aplicación suelen denominarse seguridad basada en el lenguaje .
La falta de muchos mecanismos de seguridad críticos en los sistemas operativos actuales impide la implementación de políticas de seguridad adecuadas a nivel de abstracción de aplicaciones . [16] De hecho, un concepto erróneo común en seguridad informática es que cualquier política de seguridad se puede implementar en una aplicación independientemente del soporte del núcleo. [16]
Según los desarrolladores de Mars Research Group, la falta de aislamiento es uno de los principales factores que socavan la seguridad del núcleo. [20] Proponen su marco de aislamiento de controladores para la protección, principalmente en el núcleo de Linux. [21] [22]
Los sistemas informáticos típicos actuales utilizan reglas impuestas por hardware sobre qué programas pueden acceder a qué datos. El procesador supervisa la ejecución y detiene un programa que viola una regla, como un proceso de usuario que intenta escribir en la memoria del núcleo. En los sistemas que carecen de soporte para capacidades, los procesos se aíslan entre sí mediante el uso de espacios de direcciones separados. [23] Las llamadas de los procesos de usuario al núcleo se regulan exigiéndoles que utilicen uno de los métodos de llamada al sistema descritos anteriormente.
Un enfoque alternativo es utilizar protección basada en lenguaje. En un sistema de protección basado en lenguaje , el núcleo solo permitirá que se ejecute código que haya sido producido por un compilador de lenguaje confiable . El lenguaje puede entonces diseñarse de tal manera que sea imposible para el programador ordenarle que haga algo que viole un requisito de seguridad. [15]
Las ventajas de este enfoque incluyen:
Las desventajas incluyen:
Algunos ejemplos de sistemas con protección basada en el lenguaje son JX y Singularity de Microsoft .
Edsger Dijkstra demostró que, desde un punto de vista lógico, las operaciones de bloqueo y desbloqueo atómico que operan en semáforos binarios son primitivas suficientes para expresar cualquier funcionalidad de cooperación de procesos. [24] Sin embargo, este enfoque generalmente se considera deficiente en términos de seguridad y eficiencia, mientras que un enfoque de paso de mensajes es más flexible. [25] También hay disponibles otros enfoques (ya sea de nivel inferior o superior), y muchos núcleos modernos brindan soporte para sistemas como memoria compartida y llamadas a procedimientos remotos .
La idea de un núcleo en el que los dispositivos de E/S se manejan de manera uniforme con otros procesos, como procesos cooperativos paralelos, fue propuesta e implementada por primera vez por Brinch Hansen (aunque se sugirieron ideas similares en 1967 [26] [27] ). En la descripción de Hansen de esto, los procesos "comunes" se denominan procesos internos , mientras que los dispositivos de E/S se denominan procesos externos . [25]
De manera similar a la memoria física, permitir que las aplicaciones accedan directamente a los puertos y registros del controlador puede provocar que el controlador funcione mal o que el sistema se bloquee. Con esto, dependiendo de la complejidad del dispositivo, algunos dispositivos pueden volverse sorprendentemente complejos de programar y usar varios controladores diferentes. Debido a esto, es importante proporcionar una interfaz más abstracta para administrar el dispositivo. Esta interfaz normalmente la realiza un controlador de dispositivo o una capa de abstracción de hardware. Con frecuencia, las aplicaciones requerirán acceso a estos dispositivos. El núcleo debe mantener la lista de estos dispositivos consultando al sistema por ellos de alguna manera. Esto se puede hacer a través del BIOS o a través de uno de los diversos buses del sistema (como PCI/PCIE o USB). Usando un ejemplo de un controlador de video, cuando una aplicación solicita una operación en un dispositivo, como mostrar un carácter, el núcleo necesita enviar esta solicitud al controlador de video activo actual. El controlador de video, a su vez, necesita llevar a cabo esta solicitud. Este es un ejemplo de comunicación entre procesos (IPC).
Las tareas y características enumeradas anteriormente se pueden proporcionar de muchas maneras que difieren entre sí en diseño e implementación.
El principio de separación de mecanismo y política es la diferencia sustancial entre la filosofía de los núcleos micro y monolíticos. [28] [29] Aquí un mecanismo es el soporte que permite la implementación de muchas políticas diferentes, mientras que una política es un “modo de operación” particular. Ejemplo:
Debido a que el mecanismo y la política están separados, la política se puede cambiar fácilmente para, por ejemplo, requerir el uso de un token de seguridad .
En un microkernel mínimo sólo se incluyen algunas políticas muy básicas, [29] y sus mecanismos permiten que lo que se ejecuta sobre el kernel (la parte restante del sistema operativo y las otras aplicaciones) decida qué políticas adoptar (como gestión de memoria, programación de procesos de alto nivel, gestión del sistema de archivos, etc.). [5] [25] Un kernel monolítico, en cambio, tiende a incluir muchas políticas, restringiendo así al resto del sistema a depender de ellas.
Per Brinch Hansen presentó argumentos a favor de la separación de mecanismo y política. [5] [25] El fracaso en cumplir apropiadamente con esta separación es una de las principales causas de la falta de innovación sustancial en los sistemas operativos existentes, [5] un problema común en la arquitectura de computadoras. [30] [31] [32] El diseño monolítico es inducido por el enfoque arquitectónico de "modo kernel"/"modo usuario" para la protección (técnicamente llamado dominios de protección jerárquica ), que es común en los sistemas comerciales convencionales; [33] de hecho, cada módulo que necesita protección es por lo tanto preferiblemente incluido en el kernel. [33] Este vínculo entre diseño monolítico y "modo privilegiado" puede ser reconducido a la cuestión clave de la separación de mecanismo-política; [5] de hecho el enfoque arquitectónico de "modo privilegiado" fusiona el mecanismo de protección con las políticas de seguridad, mientras que el principal enfoque arquitectónico alternativo, el direccionamiento basado en capacidad , distingue claramente entre los dos, conduciendo naturalmente a un diseño de microkernel [5] (ver Separación de protección y seguridad ).
Mientras que los núcleos monolíticos ejecutan todo su código en el mismo espacio de direcciones ( espacio del núcleo ), los micronúcleos intentan ejecutar la mayoría de sus servicios en el espacio del usuario, con el objetivo de mejorar la capacidad de mantenimiento y la modularidad de la base de código. [4] La mayoría de los núcleos no encajan exactamente en una de estas categorías, sino que se encuentran entre estos dos diseños. Estos se denominan núcleos híbridos . Hay diseños más exóticos, como nanonúcleos y exonúcleos , pero rara vez se utilizan para sistemas de producción. El hipervisor Xen , por ejemplo, es un exonúcleo.
En un núcleo monolítico, todos los servicios del sistema operativo se ejecutan junto con el hilo principal del núcleo, por lo que también residen en la misma área de memoria. Este enfoque proporciona un acceso al hardware rico y potente. El desarrollador de UNIX Ken Thompson afirmó que "en [su] opinión, es más fácil implementar un núcleo monolítico". [34] Las principales desventajas de los núcleos monolíticos son las dependencias entre los componentes del sistema (un error en un controlador de dispositivo puede hacer que todo el sistema se estropee) y el hecho de que los núcleos grandes pueden volverse muy difíciles de mantener; Thompson también afirmó que "también es más fácil que [un núcleo monolítico] se convierta en un desastre rápidamente a medida que se modifica". [34]
Los núcleos monolíticos, que tradicionalmente han sido utilizados por los sistemas operativos tipo Unix, contienen todas las funciones básicas del sistema operativo y los controladores de dispositivos. Un núcleo monolítico es un único programa que contiene todo el código necesario para realizar todas las tareas relacionadas con el núcleo. Todas las partes a las que acceden la mayoría de los programas y que no se pueden colocar en una biblioteca se encuentran en el espacio del núcleo: controladores de dispositivos, planificador, manejo de memoria, sistemas de archivos y pilas de red. Se proporcionan muchas llamadas al sistema a las aplicaciones para permitirles acceder a todos esos servicios. Un núcleo monolítico, aunque inicialmente esté cargado con subsistemas que pueden no ser necesarios, se puede ajustar hasta un punto en el que sea tan rápido o más que el que fue diseñado específicamente para el hardware, aunque sea más relevante en un sentido general.
Los núcleos monolíticos modernos, como el núcleo Linux , el núcleo FreeBSD , el núcleo AIX , el núcleo HP-UX y el núcleo Solaris , todos los cuales caen en la categoría de sistemas operativos tipo Unix, admiten módulos de núcleo cargables , lo que permite que los módulos se carguen en el núcleo en tiempo de ejecución, lo que permite una fácil extensión de las capacidades del núcleo según sea necesario, al tiempo que ayuda a minimizar la cantidad de código que se ejecuta en el espacio del núcleo.
La mayor parte del trabajo en el núcleo monolítico se realiza mediante llamadas al sistema. Se trata de interfaces, normalmente guardadas en una estructura tabular, que acceden a algún subsistema dentro del núcleo, como las operaciones de disco. En esencia, las llamadas se realizan dentro de los programas y se pasa una copia comprobada de la solicitud a través de la llamada al sistema. Por lo tanto, no hay que viajar muy lejos. El núcleo monolítico de Linux se puede hacer extremadamente pequeño no sólo por su capacidad de cargar módulos dinámicamente, sino también por su facilidad de personalización. De hecho, hay algunas versiones que son lo suficientemente pequeñas como para caber junto con una gran cantidad de utilidades y otros programas en un solo disquete y aún así proporcionar un sistema operativo completamente funcional (uno de los más populares es muLinux ). Esta capacidad de miniaturizar su núcleo también ha llevado a un rápido crecimiento en el uso de Linux en sistemas integrados .
Este tipo de núcleos se compone de las funciones básicas del sistema operativo y de los controladores de dispositivos con la capacidad de cargar módulos en tiempo de ejecución. Proporcionan abstracciones ricas y potentes del hardware subyacente. Proporcionan un pequeño conjunto de abstracciones de hardware simples y utilizan aplicaciones llamadas servidores para proporcionar más funcionalidad. Este enfoque particular define una interfaz virtual de alto nivel sobre el hardware, con un conjunto de llamadas al sistema para implementar servicios del sistema operativo como la gestión de procesos, la concurrencia y la gestión de memoria en varios módulos que se ejecutan en modo supervisor. Este diseño tiene varias fallas y limitaciones:
Microkernel (también abreviado μK o uK) es el término que describe un enfoque para el diseño de sistemas operativos mediante el cual la funcionalidad del sistema se traslada fuera del "kernel" tradicional, hacia un conjunto de "servidores" que se comunican a través de un kernel "mínimo", dejando lo menos posible en el "espacio del sistema" y lo más posible en el "espacio del usuario". Un microkernel diseñado para una plataforma o dispositivo específico solo tendrá lo que necesita para operar. El enfoque del microkernel consiste en definir una abstracción simple sobre el hardware, con un conjunto de primitivas o llamadas al sistema para implementar servicios mínimos del sistema operativo, como administración de memoria , multitarea y comunicación entre procesos . Otros servicios, incluidos los que normalmente proporciona el kernel, como la red , se implementan en programas del espacio de usuario, denominados servidores . Los microkernels son más fáciles de mantener que los kernels monolíticos, pero la gran cantidad de llamadas al sistema y cambios de contexto pueden ralentizar el sistema porque generalmente generan más sobrecarga que las llamadas a funciones simples.
Las únicas partes que realmente requieren estar en un modo privilegiado están en el espacio del núcleo: IPC (Inter-Process Communication), planificador básico o primitivas de planificación, manejo básico de memoria, primitivas básicas de E/S. Muchas partes críticas ahora se ejecutan en el espacio del usuario: el planificador completo, el manejo de memoria, los sistemas de archivos y las pilas de red. Los microkernels se inventaron como una reacción al diseño tradicional del núcleo "monolítico", por el cual toda la funcionalidad del sistema se colocaba en un programa estático que se ejecutaba en un modo "de sistema" especial del procesador. En el microkernel, solo se realizan las tareas más fundamentales, como poder acceder a parte (no necesariamente a todo) del hardware, administrar la memoria y coordinar el paso de mensajes entre los procesos. Algunos sistemas que utilizan microkernels son QNX y HURD. En el caso de QNX y Hurd, las sesiones de usuario pueden ser instantáneas completas del sistema en sí o vistas, como se lo conoce. La esencia misma de la arquitectura del microkernel ilustra algunas de sus ventajas:
La mayoría de los microkernels utilizan un sistema de paso de mensajes para gestionar las peticiones de un servidor a otro. El sistema de paso de mensajes funciona generalmente en base a puertos con el microkernel. Por ejemplo, si se envía una petición de más memoria, se abre un puerto con el microkernel y se envía la petición. Una vez dentro del microkernel, los pasos son similares a las llamadas al sistema. La razón era que aportaría modularidad a la arquitectura del sistema, lo que implicaría un sistema más limpio, más fácil de depurar o modificar dinámicamente, personalizable según las necesidades de los usuarios y con mayor rendimiento. Son parte de sistemas operativos como GNU Hurd , MINIX , MkLinux , QNX y Redox OS . Aunque los microkernels son muy pequeños por sí mismos, en combinación con todo el código auxiliar necesario son, de hecho, a menudo más grandes que los núcleos monolíticos. Los defensores de los núcleos monolíticos también señalan que la estructura de dos niveles de los sistemas de microkernel, en la que la mayor parte del sistema operativo no interactúa directamente con el hardware, crea un coste nada despreciable en términos de eficiencia del sistema. Estos tipos de núcleos normalmente proporcionan sólo los servicios mínimos, como la definición de espacios de direcciones de memoria, la comunicación entre procesos (IPC) y la gestión de procesos. Las demás funciones, como la ejecución de los procesos de hardware, no son gestionadas directamente por los micronúcleos. Los defensores de los micronúcleos señalan que los núcleos monolíticos tienen la desventaja de que un error en el núcleo puede provocar que todo el sistema se bloquee. Sin embargo, con un micronúcleo, si un proceso del núcleo se bloquea, todavía es posible evitar un bloqueo del sistema en su conjunto simplemente reiniciando el servicio que causó el error.
Otros servicios que proporciona el núcleo, como la conexión en red, se implementan en programas de espacio de usuario denominados servidores . Los servidores permiten modificar el sistema operativo simplemente iniciando y deteniendo programas. En el caso de una máquina sin soporte de red, por ejemplo, el servidor de red no se inicia. La tarea de entrar y salir del núcleo para mover datos entre las distintas aplicaciones y servidores crea una sobrecarga que es perjudicial para la eficiencia de los micronúcleos en comparación con los núcleos monolíticos.
Sin embargo, el microkernel también tiene desventajas, algunas de las cuales son:
Las desventajas de los microkernels se basan en gran medida en el contexto. Por ejemplo, funcionan bien para sistemas pequeños de propósito único (y críticos) porque, si no es necesario ejecutar muchos procesos, las complicaciones de la gestión de procesos se mitigan de manera efectiva.
Un microkernel permite la implementación de la parte restante del sistema operativo como un programa de aplicación normal escrito en un lenguaje de alto nivel , y el uso de diferentes sistemas operativos sobre el mismo kernel sin cambios. También es posible cambiar dinámicamente entre sistemas operativos y tener más de uno activo simultáneamente. [25]
A medida que el núcleo del ordenador crece, también crece el tamaño y la vulnerabilidad de su base de cómputo confiable ; y, además de reducir la seguridad, existe el problema de agrandar la huella de memoria . Esto se mitiga hasta cierto punto perfeccionando el sistema de memoria virtual , pero no todas las arquitecturas de ordenador tienen soporte de memoria virtual. [b] Para reducir la huella del núcleo, se debe realizar una edición extensa para eliminar cuidadosamente el código innecesario, lo que puede ser muy difícil con interdependencias no obvias entre partes de un núcleo con millones de líneas de código.
A principios de la década de 1990, debido a las diversas deficiencias de los núcleos monolíticos frente a los micronúcleos, los núcleos monolíticos fueron considerados obsoletos por prácticamente todos los investigadores de sistemas operativos. [ cita requerida ] Como resultado, el diseño de Linux como un núcleo monolítico en lugar de un micronúcleo fue el tema de un famoso debate entre Linus Torvalds y Andrew Tanenbaum . [35] Hay mérito en ambos lados del argumento presentado en el debate Tanenbaum-Torvalds .
Los núcleos monolíticos están diseñados para tener todo su código en el mismo espacio de direcciones ( espacio del núcleo ), lo que algunos desarrolladores sostienen que es necesario para aumentar el rendimiento del sistema. [36] Algunos desarrolladores también sostienen que los sistemas monolíticos son extremadamente eficientes si están bien escritos. [36] El modelo monolítico tiende a ser más eficiente [37] a través del uso de memoria de núcleo compartida, en lugar del sistema IPC más lento de los diseños de micronúcleo, que normalmente se basa en el paso de mensajes . [ cita requerida ]
El rendimiento de los microkernels fue pobre tanto en la década de 1980 como a principios de la de 1990. [38] [39] Sin embargo, los estudios que midieron empíricamente el rendimiento de estos microkernels no analizaron las razones de tal ineficiencia. [38] Las explicaciones de estos datos se dejaron al "folclore", con la suposición de que se debían a la mayor frecuencia de cambios de "modo kernel" a "modo usuario", a la mayor frecuencia de comunicación entre procesos y a la mayor frecuencia de cambios de contexto . [38]
De hecho, como se supuso en 1995, las razones del pobre rendimiento de los micronúcleos podrían haber sido: (1) una ineficiencia real de todo el enfoque de los micronúcleos , (2) los conceptos particulares implementados en esos micronúcleos, y (3) la implementación particular de esos conceptos. Por lo tanto, quedaba por estudiar si la solución para construir un micronúcleo eficiente era, a diferencia de los intentos anteriores, aplicar las técnicas de construcción correctas. [38]
Por otro lado, la arquitectura de dominios de protección jerárquica que conduce al diseño de un núcleo monolítico [33] tiene un inconveniente de rendimiento significativo cada vez que hay una interacción entre diferentes niveles de protección (es decir, cuando un proceso tiene que manipular una estructura de datos tanto en "modo usuario" como en "modo supervisor"), ya que esto requiere la copia de mensajes por valor . [40]
Los núcleos híbridos se utilizan en la mayoría de los sistemas operativos comerciales, como Microsoft Windows NT 3.1, NT 3.5, NT 3.51, NT 4.0, 2000, XP, Vista, 7, 8, 8.1 y 10. El propio macOS de Apple utiliza un núcleo híbrido llamado XNU , que se basa en el código del núcleo Mach de OSF/1 (OSFMK 7.3) [41] y el núcleo monolítico de FreeBSD . Los núcleos híbridos son similares a los micronúcleos, excepto que incluyen algún código adicional en el espacio del núcleo para aumentar el rendimiento. Estos núcleos representan un compromiso que fue implementado por algunos desarrolladores para acomodar las principales ventajas tanto de los núcleos monolíticos como de los micronúcleos. Estos tipos de núcleos son extensiones de los micronúcleos con algunas propiedades de los núcleos monolíticos. A diferencia de los núcleos monolíticos, estos tipos de núcleos no pueden cargar módulos en tiempo de ejecución por sí solos. [ cita requerida ] Esto implica ejecutar algunos servicios (como la pila de red o el sistema de archivos ) en el espacio del kernel para reducir la sobrecarga de rendimiento de un microkernel tradicional, pero aún así ejecutar el código del kernel (como los controladores de dispositivos) como servidores en el espacio del usuario.
Muchos núcleos tradicionalmente monolíticos ahora están al menos agregando (o usando) la capacidad de módulo. El más conocido de estos núcleos es el núcleo Linux. El núcleo modular esencialmente puede tener partes que están incorporadas en el binario del núcleo central o binarios que se cargan en la memoria según demanda. Un módulo contaminado con código tiene el potencial de desestabilizar un núcleo en ejecución. Es posible escribir un controlador para un micronúcleo en un espacio de memoria completamente separado y probarlo antes de "entrar" en funcionamiento. Cuando se carga un módulo del núcleo, accede al espacio de memoria de la porción monolítica añadiéndole lo que necesita, abriendo así la puerta a una posible contaminación. Algunas ventajas del núcleo modular (o) híbrido son:
Los módulos, por lo general, se comunican con el núcleo mediante una interfaz de módulo de algún tipo. La interfaz es generalizada (aunque particular para un sistema operativo determinado), por lo que no siempre es posible utilizar módulos. A menudo, los controladores de dispositivos pueden necesitar más flexibilidad que la que ofrece la interfaz del módulo. Básicamente, son dos llamadas al sistema y, a menudo, las comprobaciones de seguridad que solo se deben realizar una vez en el núcleo monolítico ahora se pueden realizar dos veces. Algunas de las desventajas del enfoque modular son:
Un nanokernel delega virtualmente todos los servicios –incluso los más básicos como los controladores de interrupciones o el temporizador– a controladores de dispositivos para hacer que el requerimiento de memoria del kernel sea incluso menor que el de un microkernel tradicional. [42]
Los exokernels son un enfoque aún experimental para el diseño de sistemas operativos. Se diferencian de otros tipos de kernels en que limitan su funcionalidad a la protección y multiplexación del hardware en bruto, sin proporcionar abstracciones de hardware sobre las que desarrollar aplicaciones. Esta separación de la protección del hardware y la gestión del hardware permite a los desarrolladores de aplicaciones determinar cómo hacer el uso más eficiente del hardware disponible para cada programa específico.
Los exokernels son extremadamente pequeños en sí mismos. Sin embargo, están acompañados por sistemas operativos de biblioteca (ver también unikernel ), que proporcionan a los desarrolladores de aplicaciones las funcionalidades de un sistema operativo convencional. Esto se reduce a que cada usuario escriba su propio resto del kernel desde casi cero, lo que es una tarea muy arriesgada, compleja y bastante abrumadora, particularmente en un entorno orientado a la producción con limitaciones de tiempo, por lo que los exokernels nunca se han popularizado. [ cita requerida ] Una ventaja importante de los sistemas basados en exokernels es que pueden incorporar múltiples sistemas operativos de biblioteca, cada uno exportando una API diferente , por ejemplo, una para el desarrollo de interfaz de usuario de alto nivel y otra para el control en tiempo real .
Un sistema operativo multinúcleo trata a una máquina multinúcleo como una red de núcleos independientes, como si fuera un sistema distribuido . No asume memoria compartida sino que implementa comunicaciones entre procesos como paso de mensajes . [43] [44] Barrelfish fue el primer sistema operativo que se describió como multinúcleo.
Estrictamente hablando, no se requiere un sistema operativo (y por lo tanto, un núcleo) para ejecutar una computadora. Los programas se pueden cargar y ejecutar directamente en la máquina "bare metal" , siempre que los autores de esos programas estén dispuestos a trabajar sin ninguna abstracción de hardware o soporte del sistema operativo. La mayoría de las primeras computadoras funcionaron de esta manera durante la década de 1950 y principios de la década de 1960, que se reiniciaban y recargaban entre la ejecución de diferentes programas. Finalmente, pequeños programas auxiliares como cargadores de programas y depuradores se dejaron en la memoria entre ejecuciones, o se cargaron desde ROM . A medida que se desarrollaron, formaron la base de lo que se convirtió en los primeros núcleos de sistemas operativos. El enfoque "bare metal" todavía se usa hoy en día en algunas consolas de videojuegos y sistemas integrados , [45] pero, en general, las computadoras más nuevas usan sistemas operativos y núcleos modernos.
En 1969, el sistema multiprogramación RC 4000 introdujo la filosofía de diseño de sistemas de un núcleo pequeño "sobre el cual se podrían construir de manera ordenada sistemas operativos para diferentes propósitos", [46] lo que se llamaría el enfoque del micronúcleo.
En la década anterior a Unix , las computadoras habían crecido enormemente en potencia, hasta el punto de que los operadores de computadoras buscaban nuevas formas de lograr que la gente usara su tiempo libre en sus máquinas. Uno de los principales avances durante esta era fue el tiempo compartido , mediante el cual varios usuarios obtenían pequeñas porciones de tiempo de computadora, a una velocidad en la que parecía que cada uno estaba conectado a su propia máquina, más lenta. [47]
El desarrollo de los sistemas de tiempo compartido generó una serie de problemas. Uno de ellos era que los usuarios, en particular en las universidades donde se estaban desarrollando los sistemas, parecían querer piratear el sistema para obtener más tiempo de CPU . Por esta razón, la seguridad y el control de acceso se convirtieron en un objetivo principal del proyecto Multics en 1965. [48] Otro problema constante era el manejo adecuado de los recursos informáticos: los usuarios pasaban la mayor parte del tiempo mirando la terminal y pensando en qué introducir en lugar de utilizar realmente los recursos del ordenador, y un sistema de tiempo compartido debería ceder el tiempo de CPU a un usuario activo durante estos períodos. Por último, los sistemas solían ofrecer una jerarquía de memoria de varias capas de profundidad, y la partición de este costoso recurso condujo a importantes avances en los sistemas de memoria virtual .
El Commodore Amiga se lanzó en 1985 y fue uno de los primeros ordenadores domésticos (y sin duda el más exitoso) en incorporar una arquitectura de núcleo avanzada. El componente ejecutivo del núcleo de AmigaOS, exec.library , utiliza un diseño de paso de mensajes de micronúcleo, pero hay otros componentes del núcleo, como graphics.library , que tienen acceso directo al hardware. No hay protección de memoria y el núcleo casi siempre se ejecuta en modo de usuario. Solo se ejecutan acciones especiales en modo de núcleo y las aplicaciones en modo de usuario pueden pedirle al sistema operativo que ejecute su código en modo de núcleo.
Durante la fase de diseño de Unix , los programadores decidieron modelar cada dispositivo de alto nivel como un archivo , porque creían que el propósito del cálculo era la transformación de datos . [49]
Por ejemplo, las impresoras se representaban como un "archivo" en una ubicación conocida: cuando se copiaban datos al archivo, se imprimían. Otros sistemas, para proporcionar una funcionalidad similar, tendían a virtualizar los dispositivos a un nivel inferior, es decir, tanto los dispositivos como los archivos serían instancias de algún concepto de nivel inferior . La virtualización del sistema a nivel de archivo permitía a los usuarios manipular todo el sistema utilizando sus utilidades y conceptos de gestión de archivos existentes , simplificando drásticamente la operación. Como una extensión del mismo paradigma, Unix permite a los programadores manipular archivos utilizando una serie de programas pequeños, utilizando el concepto de tuberías , que permitía a los usuarios completar operaciones en etapas, alimentando un archivo a través de una cadena de herramientas de un solo propósito. Aunque el resultado final era el mismo, el uso de programas más pequeños de esta manera aumentó drásticamente la flexibilidad, así como la facilidad de desarrollo y uso, lo que permitía al usuario modificar su flujo de trabajo agregando o eliminando un programa de la cadena.
En el modelo Unix, el sistema operativo consta de dos partes: primero, la enorme colección de programas de utilidad que controlan la mayoría de las operaciones; segundo, el núcleo que ejecuta los programas. [49] En Unix, desde un punto de vista de programación, la distinción entre los dos es bastante delgada; el núcleo es un programa, que se ejecuta en modo supervisor, [c] que actúa como cargador de programas y supervisor para los pequeños programas de utilidad que componen el resto del sistema, y para proporcionar servicios de bloqueo y E/S para estos programas; más allá de eso, el núcleo no intervino en absoluto en el espacio de usuario .
Con el paso de los años, el modelo informático cambió y el tratamiento que Unix daba a todo como un archivo o un flujo de bytes ya no era tan universalmente aplicable como antes. Aunque un terminal podía ser tratado como un archivo o un flujo de bytes, que se imprime o se lee, no parecía ocurrir lo mismo con una interfaz gráfica de usuario . Las redes planteaban otro problema. Aunque la comunicación en red se puede comparar con el acceso a archivos, la arquitectura orientada a paquetes de bajo nivel se ocupaba de fragmentos discretos de datos y no de archivos completos. A medida que crecía la capacidad de los ordenadores, Unix se fue llenando cada vez más de código. Esto se debe también a que la modularidad del núcleo de Unix es ampliamente escalable. [50] Mientras que los núcleos podían tener 100.000 líneas de código en los años setenta y ochenta, los núcleos como Linux , de los sucesores modernos de Unix como GNU , tienen más de 13 millones de líneas. [51]
Los derivados modernos de Unix se basan generalmente en núcleos monolíticos de carga de módulos. Ejemplos de esto son el núcleo Linux en las muchas distribuciones de GNU , IBM AIX , así como los núcleos variantes de Berkeley Software Distribution como FreeBSD , DragonFly BSD , OpenBSD , NetBSD y macOS . Aparte de estas alternativas, los desarrolladores aficionados mantienen una comunidad de desarrollo de sistemas operativos activa , poblada por núcleos de aficionados escritos por ellos mismos que en su mayoría terminan compartiendo muchas características con los núcleos Linux, FreeBSD, DragonflyBSD, OpenBSD o NetBSD y/o siendo compatibles con ellos. [52]
Apple lanzó por primera vez su sistema operativo clásico Mac OS en 1984, incluido en su computadora personal Macintosh . Apple pasó a un diseño de nanokernel en Mac OS 8.6. Frente a esto, el macOS moderno (originalmente llamado Mac OS X) se basa en Darwin , que utiliza un núcleo híbrido llamado XNU , que se creó combinando el núcleo 4.3BSD y el núcleo Mach . [53]
Microsoft Windows se lanzó por primera vez en 1985 como complemento de MS-DOS . Debido a su dependencia de otro sistema operativo, las versiones iniciales de Windows, anteriores a Windows 95, se consideraban un entorno operativo (que no debe confundirse con un sistema operativo ). Esta línea de productos continuó evolucionando durante las décadas de 1980 y 1990, con la serie Windows 9x que agregó direccionamiento de 32 bits y multitarea preventiva; pero finalizó con el lanzamiento de Windows Me en 2000.
Microsoft también desarrolló Windows NT , un sistema operativo con una interfaz muy similar, pero pensado para usuarios de alto nivel y empresariales. Esta línea comenzó con el lanzamiento de Windows NT 3.1 en 1993, y se introdujo a los usuarios generales con el lanzamiento de Windows XP en octubre de 2001, reemplazando a Windows 9x por un sistema operativo completamente diferente y mucho más sofisticado. Esta es la línea que continúa con Windows 11 .
La arquitectura del núcleo de Windows NT se considera un núcleo híbrido porque el núcleo en sí contiene tareas como el Administrador de ventanas y los Administradores de IPC, con un modelo de subsistema en capas cliente/servidor. [54] Fue diseñado como un microkernel modificado , ya que el núcleo de Windows NT fue influenciado por el microkernel Mach pero no cumple con todos los criterios de un microkernel puro.
Un programa de supervisión o supervisor es un programa de computadora , generalmente parte de un sistema operativo , que controla la ejecución de otras rutinas y regula la programación del trabajo , las operaciones de entrada/salida , las acciones de error y funciones similares y regula el flujo de trabajo en un sistema de procesamiento de datos .
Históricamente, este término se asociaba esencialmente con la línea de sistemas operativos mainframe de IBM , comenzando con OS/360 . En otros sistemas operativos, el supervisor se denomina generalmente kernel.
En la década de 1970, IBM abstrajo aún más el estado supervisor del hardware, lo que dio como resultado un hipervisor que permitió la virtualización completa , es decir, la capacidad de ejecutar múltiples sistemas operativos en la misma máquina de forma totalmente independiente entre sí. Por lo tanto, el primer sistema de este tipo se denominó máquina virtual o VM .
Aunque Mach , desarrollado por Richard Rashid en la Universidad Carnegie Mellon , es el microkernel de propósito general más conocido, se han desarrollado otros microkernels con objetivos más específicos. La familia de microkernels L4 (principalmente el kernel L3 y L4) se creó para demostrar que los microkernels no son necesariamente lentos. [55] Las implementaciones más nuevas, como Fiasco y Pistachio, pueden ejecutar Linux junto con otros procesos L4 en espacios de direcciones separados. [56] [57]
Además, QNX es un microkernel que se utiliza principalmente en sistemas integrados , [58] y el software de código abierto MINIX , aunque originalmente creado con fines educativos, ahora se centra en ser un sistema operativo de microkernel altamente confiable y autorreparador .
. . . casi todas las llamadas al sistema [se] invocan desde programas C llamando a un procedimiento de biblioteca . . . El procedimiento de biblioteca . . . ejecuta una instrucción TRAP para cambiar del modo de usuario al modo kernel e iniciar la ejecución . . .
25 años han demostrado que la investigación sobre la arquitectura del sistema operativo tuvo un efecto menor en los sistemas dominantes existentes .
La naturaleza estrechamente acoplada de un núcleo monolítico le permite hacer un uso muy eficiente del hardware subyacente [...] Los micronúcleos, por otro lado, ejecutan muchos más procesos básicos en el espacio de usuario. [...] Desafortunadamente, estos beneficios se obtienen a costa de que el micronúcleo tenga que pasar mucha información dentro y fuera del espacio del núcleo a través de un proceso conocido como cambio de contexto. Los cambios de contexto introducen una sobrecarga considerable y, por lo tanto, resultan en una pérdida de rendimiento.