Mach ( / m ɑː k / ) [1] es un núcleo desarrollado en la Universidad Carnegie Mellon por Richard Rashid y Avie Tevanian para apoyar la investigación de sistemas operativos , principalmente computación distribuida y paralela . Mach se considera a menudo uno de los primeros ejemplos de un microkernel . Sin embargo, no todas las versiones de Mach son microkernels. Los derivados de Mach son la base del núcleo del sistema operativo en GNU Hurd y del núcleo XNU de Apple utilizado en macOS , iOS , iPadOS , tvOS y watchOS .
El proyecto de Carnegie Mellon se desarrolló entre 1985 y 1994, [2] y terminó con Mach 3.0, que es un verdadero microkernel . Mach se desarrolló como reemplazo del kernel en la versión BSD de Unix , sin necesidad de diseñar un nuevo sistema operativo en torno a él. Mach y sus derivados existen en varios sistemas operativos comerciales. Entre ellos, todos utilizan el kernel del sistema operativo XNU, que incorpora un Mach anterior sin microkernel como componente principal. El sistema de gestión de memoria virtual Mach también fue adoptado en 4.4BSD por los desarrolladores de BSD en CSRG , [3] y aparece en los sistemas Unix modernos derivados de BSD, como FreeBSD .
Mach es el sucesor lógico del núcleo Accent de Carnegie Mellon . El desarrollador principal de Mach, Richard Rashid, ha trabajado en Microsoft desde 1991; fundó la división Microsoft Research . El desarrollador y cofundador de Mach, Avie Tevanian, fue anteriormente jefe de software en NeXT y luego director de tecnología de software en Apple Inc. hasta marzo de 2006. [4] [2]
Los desarrolladores fueron en bicicleta a almorzar a través de los charcos de barro de la lluviosa Pittsburgh, y Tevanian bromeó con que la palabra "muck" podría formar un acrónimo inverso para su núcleo de comunicación multiusuario (o multiprocesador universal). El ingeniero italiano de CMU Dario Giuse [5] más tarde le preguntó al líder del proyecto Rick Rashid sobre el título actual del proyecto y recibió "MUCK" como respuesta, aunque no escrito sino simplemente pronunciado en AFI: [mʌk] . Según el alfabeto italiano , escribió "Mach". A Rashid le gustó tanto la ortografía de Giuse "Mach" que prevaleció. [6] : 103
Un concepto clave en el sistema operativo Unix original es la idea de una tubería . Una tubería es una abstracción que permite mover datos como un flujo no estructurado de bytes entre programas. Mediante tuberías, los usuarios pueden vincular varios programas para completar tareas, alimentando datos a través de varios programas pequeños consecutivos. Esto contrasta con los sistemas operativos típicos de la época, que requieren un solo programa grande que pueda manejar toda la tarea o, alternativamente, utilizan archivos para pasar datos, lo que era costoso en recursos y consumía mucho tiempo.
Los conductos se construyeron sobre el sistema de entrada/salida subyacente . Este sistema, a su vez, se basa en un modelo en el que se espera que los controladores se "bloqueen" periódicamente mientras esperan que se completen las tareas. Por ejemplo, un controlador de impresora puede enviar una línea de texto a una impresora de líneas y luego no tener nada que hacer hasta que la impresora termine de imprimir esa línea. En este caso, el controlador indica que se bloqueó y el sistema operativo permite que se ejecute algún otro programa hasta que la impresora indique que está lista para recibir más datos. En el sistema de conductos, el recurso limitado era la memoria y, cuando un programa llenaba la memoria asignada al conducto, se bloqueaba de forma natural. Normalmente, esto hacía que el programa que lo consumía se ejecutara, vaciando el conducto de nuevo. A diferencia de un archivo, donde se debe leer o escribir todo el archivo antes de que el siguiente programa pueda usarlo, los conductos hicieron que el movimiento de datos entre varios programas se produjera de forma fragmentada sin ninguna intervención del programador.
Sin embargo, la implementación de tuberías en los buffers de memoria obligaba a copiar datos de un programa a otro, una operación que consumía mucho tiempo y recursos. Esto hacía que el concepto de tubería no fuera adecuado para tareas en las que se necesitaba una respuesta rápida o una baja latencia, como en la mayoría de los controladores de dispositivos . El núcleo del sistema operativo y la mayor parte de la funcionalidad principal se escribieron en un único programa grande. Cuando se añadieron nuevas funciones, como las redes informáticas , al sistema operativo, el tamaño y la complejidad del núcleo también crecieron.
Los pipes de Unix ofrecían un sistema conceptual que podía utilizarse para crear soluciones de cualquier complejidad a partir de pequeños programas cooperativos. Estos programas más pequeños eran más fáciles de desarrollar y mantener, y tenían interfaces bien definidas que simplificaban la programación y la depuración. Estas cualidades son aún más valiosas para los controladores de dispositivos, donde el tamaño pequeño y el rendimiento sin errores eran extremadamente importantes. Había un fuerte deseo de modelar el núcleo sobre la misma base de pequeños programas cooperativos.
Uno de los primeros sistemas en utilizar un sistema de tipo canalización como base del sistema operativo fue el núcleo Aleph desarrollado en la Universidad de Rochester . Éste introdujo el concepto de puertos, que eran esencialmente una implementación de memoria compartida . En Aleph, el núcleo se reducía a proporcionar acceso al hardware, incluida la memoria y los puertos, mientras que los programas convencionales que utilizaban el sistema de puertos implementaban todo el comportamiento, desde los controladores de dispositivos hasta los programas de usuario. Este concepto reducía enormemente el tamaño del núcleo y permitía a los usuarios experimentar con diferentes controladores simplemente cargándolos y conectándolos entre sí en tiempo de ejecución. Esto alivió en gran medida los problemas al desarrollar un nuevo código de sistema operativo, que de otro modo requeriría que se reiniciara la máquina. El concepto general de un núcleo pequeño y controladores externos se conoció como micronúcleo.
Aleph se implementó en minicomputadoras Data General Eclipse y estaba estrechamente vinculado a ellas. Esta máquina estaba lejos de ser ideal, ya que requería que se copiara memoria entre programas, lo que generaba una considerable sobrecarga de rendimiento. También era bastante costosa. Sin embargo, Aleph demostró que el sistema básico era sólido y luego demostró la agrupación de computadoras copiando la memoria a través de una interfaz Ethernet temprana .
En esa época, se estaba lanzando al mercado una nueva generación de procesadores centrales (CPU), que ofrecían un espacio de direcciones de 32 bits y compatibilidad (inicialmente opcional) con una unidad de gestión de memoria (MMU). La MMU manejaba las instrucciones necesarias para implementar un sistema de memoria virtual , haciendo un seguimiento de las páginas de memoria que utilizaban los distintos programas. Esto ofrecía una nueva solución al concepto de puerto, utilizando el mecanismo de copia en escritura (COW) proporcionado por el sistema de memoria virtual. En lugar de copiar datos entre programas, todo lo que se requería era indicar a la MMU que proporcionara acceso a esa misma memoria. Este sistema implementaría el sistema de comunicaciones entre procesos (IPC) con un rendimiento considerablemente superior.
Este concepto fue retomado en Carnegie-Mellon, que adaptó Aleph para la estación de trabajo PERQ y lo implementó utilizando el método de copia en escritura. La adaptación tuvo éxito, pero el núcleo Accent resultante tenía un uso práctico limitado porque no ejecutaba el software existente. Además, Accent estaba tan estrechamente vinculado a PERQ como Aleph lo estaba a Eclipse.
El cambio más importante entre estos núcleos experimentales y Mach fue la decisión de hacer una versión del núcleo 4.2BSD existente reimplementada sobre los conceptos de paso de mensajes de Accent. Un núcleo de este tipo sería compatible a nivel binario con el software BSD existente , lo que haría que el sistema estuviera inmediatamente disponible para el uso diario y al mismo tiempo siguiera siendo una plataforma experimental útil. Además, el nuevo núcleo estaría diseñado desde el principio para soportar múltiples arquitecturas de procesador, permitiendo incluso la construcción de clústeres heterogéneos. Para poner en marcha el sistema lo más rápido posible, el sistema se implementaría comenzando con el código BSD existente y reimplementándolo gradualmente como programas basados en comunicación entre procesos (IPC). De este modo, Mach comenzaría como un sistema monolítico similar a los sistemas UNIX existentes y progresaría hacia el concepto de micronúcleo con el tiempo. [4]
Mach comenzó siendo en gran medida un esfuerzo por producir un Accent claramente definido, basado en UNIX y altamente portable. El resultado fue una lista corta de conceptos genéricos: [7] [8]
Mach se basó en los conceptos de IPC de Accent, pero hizo que el sistema fuera mucho más parecido a UNIX, lo que hizo posible ejecutar programas UNIX con poca o ninguna modificación. Para ello, Mach introdujo el puerto, que representa cada punto final de un IPC bidireccional. Los puertos tenían un concepto de permisos como los archivos en UNIX, lo que permitía aplicarles un modelo de protección muy similar al de UNIX. Además, Mach permitía que cualquier programa manejara privilegios que normalmente se le darían solo al sistema operativo, para permitir que los programas del espacio de usuario manejaran cosas como el control del hardware.
En Mach, y al igual que en UNIX, el sistema operativo vuelve a ser principalmente una colección de utilidades. Al igual que en UNIX, Mach mantiene el concepto de un controlador para manejar el hardware. Por lo tanto, todos los controladores para el hardware actual deben incluirse en el micronúcleo. Otras arquitecturas basadas en la capa de abstracción de hardware o exonúcleos podrían sacar los controladores del micronúcleo.
La principal diferencia con UNIX es que en lugar de que las utilidades se ocupen de los archivos, pueden ocuparse de cualquier "tarea". Se trasladó más código del sistema operativo fuera del núcleo y al espacio de usuario, lo que dio como resultado un núcleo mucho más pequeño y el surgimiento del término micronúcleo . A diferencia de los sistemas tradicionales, en Mach un proceso, o "tarea", puede constar de varios subprocesos. Si bien esto es común en los sistemas modernos, Mach fue el primer sistema en definir tareas e subprocesos de esta manera. El trabajo del núcleo se redujo de ser esencialmente el sistema operativo a ejecutar las "utilidades" y proporcionarles acceso al hardware.
La existencia de puertos y el uso de IPC es quizás la diferencia más fundamental entre Mach y los núcleos tradicionales. En UNIX, llamar al núcleo consiste en una operación llamada llamada al sistema o trap . El programa utiliza una biblioteca para colocar datos en una ubicación bien conocida en la memoria y luego provoca un fallo , un tipo de error. Cuando se inicia un sistema por primera vez, su núcleo está configurado para ser el "controlador" de todos los fallos; por lo tanto, cuando un programa provoca un fallo, el núcleo toma el control, examina la información que se le pasa y luego lleva a cabo las instrucciones.
En Mach, para esta función se utilizaba el sistema IPC. Para solicitar una función del sistema, un programa solicitaba al núcleo acceso a un puerto y luego utilizaba el sistema IPC para enviar mensajes a ese puerto. Aunque enviar un mensaje requiere una llamada al sistema, al igual que una solicitud de una función del sistema en otros sistemas requiere una llamada al sistema, en Mach enviar el mensaje es prácticamente todo lo que hace el núcleo; gestionar la solicitud real quedaría a cargo de otro programa.
El soporte de subprocesos y concurrencia se benefició con el paso de mensajes con mecanismos IPC, ya que las tareas ahora consisten en múltiples subprocesos de código que Mach puede congelar y descongelar durante el manejo de mensajes. Esto permite que el sistema se distribuya entre múltiples procesadores, ya sea utilizando memoria compartida directamente como en la mayoría de los mensajes Mach, o agregando código para copiar el mensaje a otro procesador si es necesario. En un núcleo tradicional, esto es difícil de implementar; el sistema tiene que estar seguro de que diferentes programas no intenten escribir en la misma región de memoria desde diferentes procesadores. Sin embargo, el uso de puertos Mach hace que esto esté bien definido y sea fácil de implementar, por lo que los puertos Mach se convirtieron en ciudadanos de primera clase en ese sistema.
El sistema IPC inicialmente tuvo problemas de rendimiento, por lo que se desarrollaron algunas estrategias para mejorarlo. Al igual que su predecesor, Accent, Mach utilizó un único mecanismo de memoria compartida para pasar físicamente el mensaje de un programa a otro. Copiar físicamente el mensaje sería demasiado lento, por lo que Mach depende de la unidad de gestión de memoria (MMU) de la máquina para asignar rápidamente los datos de un programa a otro. Solo si se escriben los datos, es necesario copiarlos físicamente, un proceso llamado " copiar en escritura ".
El núcleo también comprobaba la validez de los mensajes para evitar que datos erróneos hicieran que uno de los muchos programas que componen el sistema se bloqueara. Los puertos se modelaron deliberadamente según los conceptos del sistema de archivos de UNIX. Esto permite al usuario encontrar puertos utilizando conceptos de navegación del sistema de archivos existentes, así como asignar derechos y permisos como lo haría en el sistema de archivos.
El desarrollo con un sistema de este tipo sería más fácil. No sólo el código en el que se está trabajando existiría en un programa tradicional que podría construirse utilizando herramientas existentes, sino que también podría iniciarse, depurarse y eliminarse utilizando las mismas herramientas. Con un mononúcleo, un error en el código nuevo haría que se parara toda la máquina y obligaría a reiniciarla, mientras que con Mach esto sólo requeriría que se reiniciara el programa. Además, el usuario podría adaptar el sistema para incluir o excluir las funciones que necesitara. Dado que el sistema operativo era simplemente una colección de programas, podían añadir o eliminar partes simplemente ejecutándolas o eliminándolas como lo harían con cualquier otro programa.
Finalmente, en Mach, todas estas características fueron diseñadas deliberadamente para ser extremadamente neutrales en cuanto a plataformas. Por citar un texto sobre Mach:
Sin embargo, existen varias desventajas. Una relativamente común es que no está claro cómo encontrar los puertos. En UNIX, este problema se resolvió con el tiempo, ya que los programadores acordaron una serie de ubicaciones "bien conocidas" en el sistema de archivos para cumplir diversas funciones. Si bien este mismo enfoque funcionó también para los puertos de Mach, en Mach se suponía que el sistema operativo era mucho más fluido, con puertos que aparecían y desaparecían todo el tiempo. Sin algún mecanismo para encontrar los puertos y los servicios que representaban, se perdería gran parte de esta flexibilidad.
Mach se alojó inicialmente como código adicional escrito directamente en el núcleo 4.2BSD existente, lo que permitió al equipo trabajar en el sistema mucho antes de que estuviera completo. El trabajo comenzó con el sistema de puerto/IPC Accent ya funcional y se trasladó a las otras partes clave del sistema operativo: tareas, subprocesos y memoria virtual. A medida que se completaban partes, se reescribieron varias partes del sistema BSD para llamar a Mach y también se realizó un cambio a 4.3BSD durante este proceso.
En 1986, el sistema estaba completo hasta el punto de poder ejecutarse por sí solo en el DEC VAX . Aunque no hizo mucho valor práctico, el objetivo de crear un microkernel se hizo realidad. A esto pronto le siguieron versiones para IBM RT PC y para estaciones de trabajo basadas en Sun Microsystems 68030 , lo que demostró la portabilidad del sistema. En 1987, la lista incluía las máquinas Encore Multimax y Sequent Balance , lo que probaba la capacidad de Mach para ejecutarse en sistemas multiprocesador. Se realizó una versión pública Release 1 ese año, y la Release 2 le siguió al año siguiente.
Durante todo este tiempo, la promesa de un microkernel "verdadero" aún no se había cumplido. Estas primeras versiones de Mach incluían la mayoría de 4.3BSD en el kernel, un sistema conocido como POE Server, lo que dio como resultado un kernel que en realidad era más grande que el UNIX en el que se basaba. Sin embargo, la idea era sacar la capa UNIX del kernel y llevarla al espacio de usuario, donde se podía trabajar con ella más fácilmente e incluso reemplazarla por completo. Desafortunadamente, el rendimiento resultó ser un problema importante y se realizaron varios cambios arquitectónicos para resolverlo. Los problemas de licencias de UNIX también plagaron a los investigadores, por lo que este esfuerzo inicial por proporcionar un entorno de sistema similar a UNIX sin licencia siguió encontrando uso, hasta bien entrado el desarrollo posterior de Mach.
El Mach 3 resultante se lanzó en 1990 y generó un gran interés. Un pequeño equipo había creado Mach y lo había adaptado a varias plataformas, incluidos sistemas multiprocesador complejos que estaban causando serios problemas para los núcleos de estilo antiguo. Esto generó un interés considerable en el mercado comercial, donde varias empresas estaban considerando cambiar las plataformas de hardware. Si el sistema existente podía adaptarse para ejecutarse en Mach, parecía que sería fácil cambiar la plataforma subyacente.
Mach recibió un gran impulso en visibilidad cuando la Open Software Foundation (OSF) anunció que alojaría futuras versiones de OSF/1 en Mach 2.5, y que también estaban investigando Mach 3. Mach 2.5 también fue seleccionado para el sistema NeXTSTEP y una serie de proveedores comerciales de multiprocesadores. Mach 3 condujo a una serie de esfuerzos para portar otras partes de sistemas operativos para el microkernel, incluido Workplace OS de IBM y varios esfuerzos de Apple para construir una versión multiplataforma del Mac OS clásico . [10] Los investigadores demostraron el soporte para ejecutar aplicaciones DOS en un entorno Mach 3.0, siguiendo el trabajo anterior ejecutando el Mac OS clásico y MultiFinder bajo Mach 2.5. [11]
Mach fue originalmente pensado para reemplazar al clásico UNIX monolítico, y por esta razón contenía muchas ideas similares a UNIX. Por ejemplo, Mach proporcionaba un sistema de permisos y seguridad similar al utilizado por el sistema de archivos de UNIX. Dado que el núcleo tenía privilegios (se ejecutaba en el espacio del núcleo ) sobre otros servidores y software del sistema operativo, era posible que programas maliciosos o que funcionaran mal le enviaran comandos que causaran daños al sistema, y por esta razón el núcleo verificaba la validez de cada mensaje. Además, la mayor parte de la funcionalidad del sistema operativo debía estar ubicada en programas del espacio de usuario, por lo que esto significaba que debía haber alguna forma para que el núcleo otorgara a estos programas privilegios adicionales, por ejemplo, para acceder directamente al hardware.
Algunas de las características más esotéricas de Mach también se basaban en este mismo mecanismo de IPC. Por ejemplo, Mach podía soportar máquinas multiprocesador con facilidad. En un núcleo tradicional se necesita realizar un trabajo extenso para hacerlo reentrante o interrumpible , ya que los programas que se ejecutan en diferentes procesadores podrían llamar al núcleo al mismo tiempo. Con Mach, los bits del sistema operativo están aislados en servidores, que pueden ejecutarse, como cualquier otro programa, en cualquier procesador. Aunque en teoría el núcleo de Mach también tendría que ser reentrante, en la práctica esto no es un problema porque sus tiempos de respuesta son tan rápidos que simplemente puede esperar y atender las solicitudes por turno. Mach también incluía un servidor que podía reenviar mensajes no solo entre programas, sino incluso a través de la red, que fue un área de intenso desarrollo a fines de la década de 1980 y principios de la de 1990.
Lamentablemente, el uso de IPC para casi todas las tareas tuvo un impacto serio en el rendimiento. Las pruebas comparativas realizadas con hardware de 1997 mostraron que las implementaciones de servidor único de UNIX basadas en Mach 3.0 eran aproximadamente un 50% más lentas que las de UNIX nativo. [12] [13]
El estudio de la naturaleza exacta de los problemas de rendimiento reveló una serie de hechos interesantes. Uno de ellos era que el IPC no era el problema: había cierta sobrecarga asociada con el mapeo de memoria necesario para soportarlo, pero esto sólo añadía una pequeña cantidad de tiempo a la realización de una llamada. El resto, el 80% del tiempo empleado, se debía a tareas adicionales que el núcleo estaba ejecutando en los mensajes. Entre ellas, la principal era la comprobación de los derechos de puerto y la validez de los mensajes. En las pruebas comparativas realizadas en un 486 DX-50, una llamada al sistema UNIX estándar tardaba una media de 21 μs en completarse, mientras que la operación equivalente con Mach IPC promediaba 114 μs. Sólo 18 μs de esto estaban relacionados con el hardware; el resto era el núcleo Mach ejecutando varias rutinas en el mensaje. [14] Dada una llamada al sistema que no hace nada, un viaje de ida y vuelta completo en BSD requeriría unos 40 μs, mientras que en un sistema Mach de espacio de usuario tardaría poco menos de 500 μs.
Cuando Mach empezó a utilizarse seriamente en las versiones 2.x, el rendimiento era más lento que el de los sistemas operativos monolíticos tradicionales, quizás hasta un 25 %. [1] Sin embargo, este coste no se consideró especialmente preocupante, porque el sistema también ofrecía compatibilidad con múltiples procesadores y una portabilidad sencilla. Muchos pensaron que era un coste esperado y aceptable. Cuando Mach 3 intentó trasladar la mayor parte del sistema operativo al espacio de usuario, la sobrecarga se hizo aún mayor: las pruebas comparativas entre Mach y Ultrix en un MIPS R3000 mostraron una pérdida de rendimiento de hasta un 67 % en algunas cargas de trabajo. [15]
Por ejemplo, para obtener la hora del sistema se necesita una llamada IPC al servidor de espacio de usuario que mantiene el reloj del sistema . El que llama primero entra en el núcleo, lo que provoca un cambio de contexto y un mapeo de memoria. A continuación, el núcleo comprueba que el que llama tiene los derechos de acceso necesarios y que el mensaje es válido. Si es así, se produce otro cambio de contexto y un mapeo de memoria para completar la llamada al servidor de espacio de usuario. A continuación, el proceso debe repetirse para devolver los resultados, lo que suma un total de cuatro cambios de contexto y mapeos de memoria, más dos verificaciones de mensajes. Esta sobrecarga se agrava rápidamente con servicios más complejos, en los que a menudo hay rutas de código que pasan por muchos servidores.
Esta no era la única fuente de problemas de rendimiento. Otra se centraba en los problemas de tratar de manejar la memoria correctamente cuando la memoria física se agotaba y era necesario realizar una paginación. En los sistemas operativos monolíticos tradicionales, los autores tenían experiencia directa con qué partes del núcleo llamaban a otras, lo que les permitía ajustar su paginador para evitar paginar el código que estaba a punto de usarse. Con Mach esto no era posible porque el núcleo no tenía idea real de en qué consistía el sistema operativo. En su lugar, tenían que utilizar una única solución que se ajustara a todos, lo que se sumaba a los problemas de rendimiento. Mach 3 intentó abordar este problema proporcionando un paginador simple, que se basaba en paginadores de espacio de usuario para una mejor especialización. Pero esto resultó tener poco efecto. En la práctica, cualquier beneficio que tuviera se vio anulado por el costoso IPC necesario para llamarlo.
Otros problemas de rendimiento estaban relacionados con el soporte de Mach para sistemas multiprocesador . Desde mediados de los años 1980 hasta principios de los años 1990, las CPU de consumo masivo crecieron en rendimiento a una tasa de aproximadamente el 60% anual, pero la velocidad de acceso a la memoria aumentó solo un 7% anual. Esto significó que el costo de acceso a la memoria aumentó enormemente durante este período y, dado que Mach se basaba en mapear la memoria entre programas, cualquier "error de caché" hacía que las llamadas IPC fueran lentas.
La sobrecarga de IPC es un problema importante para los sistemas Mach 3. Sin embargo, el concepto de un sistema operativo multiservidor sigue siendo prometedor, aunque aún requiere algo de investigación. Los desarrolladores deben tener cuidado de aislar el código en módulos que no llamen de un servidor a otro. Por ejemplo, la mayoría del código de red se colocaría en un solo servidor, minimizando así la sobrecarga de IPC para las tareas de red normales.
La mayoría de los desarrolladores, en cambio, se quedaron con el concepto original de POE de un único servidor grande que proporcionaba la funcionalidad del sistema operativo. [16] Para facilitar el desarrollo, permitieron que el servidor del sistema operativo se ejecutara en el espacio de usuario o en el espacio del núcleo. Esto les permitió desarrollar en el espacio de usuario y tener todas las ventajas de la idea original de Mach, y luego mover el servidor depurado al espacio del núcleo para obtener un mejor rendimiento. Desde entonces, se han construido varios sistemas operativos utilizando este método, conocido como co-ubicación , entre ellos Lites , MkLinux , OSF/1 y NeXTSTEP/OPENSTEP/macOS. El microkernel Chorus hizo de esto una característica del sistema básico, lo que permitió que los servidores se elevaran al espacio del núcleo utilizando mecanismos integrados.
Mach 4 intentó solucionar estos problemas con un conjunto de mejoras más radicales. En particular, se descubrió que el código de programa no era escribible, por lo que los posibles impactos debidos a la copia en escritura eran poco frecuentes. Por lo tanto, tenía sentido no mapear la memoria entre programas para IPC, sino migrar el código de programa que se estaba utilizando al espacio local del programa. Esto llevó al concepto de "lanzaderas" y parecía que el rendimiento había mejorado, pero los desarrolladores siguieron adelante con el sistema en un estado semi-utilizable. Mach 4 también introdujo primitivas de coubicación integradas, lo que lo convirtió en parte del núcleo.
A mediados de la década de 1990, el trabajo en sistemas de microkernel estaba en gran medida estancado, aunque el mercado generalmente había creído que todos los sistemas operativos modernos estarían basados en microkernel para la década de 1990. Los principales usos generalizados restantes del kernel Mach son macOS de Apple y su hermano iOS, que se ejecutan sobre un núcleo Mach híbrido de Open Software Foundation (OSFMK 7.3) muy modificado llamado " XNU " [17] también utilizado en OSF/1. [10] En XNU, los sistemas de archivos, las pilas de redes y las funciones de gestión de procesos y memoria se implementan en el kernel; y el sistema de archivos, la red y algunas funciones de gestión de procesos y memoria se invocan desde el modo de usuario a través de llamadas al sistema ordinarias en lugar de pasar mensajes; [18] [19] Los mensajes Mach de XNU se utilizan para la comunicación entre procesos en modo usuario y para algunas solicitudes del código en modo usuario al kernel y del kernel a servidores en modo usuario.
Un análisis posterior demostró que el problema de rendimiento de IPC no era tan obvio como parecía. Recordemos que una llamada al sistema de un solo lado tardaba 20 μs en BSD [3] y 114 μs en Mach ejecutándose en el mismo sistema. [2] De los 114, 11 se debían al cambio de contexto, idéntico a BSD. [13] La MMU utilizó 18 más para mapear el mensaje entre el espacio de usuario y el espacio del núcleo. [3] Esto suma solo 29 μs, más que una llamada al sistema tradicional, pero no por mucho.
El resto, la mayor parte del problema real, se debía a que el núcleo realizaba tareas como comprobar el mensaje para los derechos de acceso a los puertos. [6] Aunque parecería que se trata de un problema de seguridad importante, de hecho, sólo tiene sentido en un sistema tipo UNIX. Por ejemplo, un sistema operativo monousuario que ejecute un teléfono móvil o un robot podría no necesitar ninguna de estas funciones, y este es exactamente el tipo de sistema en el que el sistema operativo de selección automática de Mach sería más valioso. Del mismo modo, Mach causaba problemas cuando el sistema operativo había movido la memoria, otra tarea que sólo tiene sentido si el sistema tiene más de un espacio de direcciones. DOS y los primeros Mac OS tienen un único espacio de direcciones grande compartido por todos los programas, por lo que en estos sistemas el mapeo no proporcionaba ningún beneficio.
Estas realizaciones condujeron a una serie de microkernels de segunda generación, que redujeron aún más la complejidad del sistema y colocaron casi toda la funcionalidad en el espacio de usuario. Por ejemplo, el kernel L4 (versión 2) incluye solo siete llamadas al sistema y utiliza 12k de memoria, [3] mientras que Mach 3 incluye alrededor de 140 funciones y utiliza alrededor de 330k de memoria. [3] Las llamadas IPC bajo L4 en un 486DX-50 toman solo 5μs, [19] más rápido que una llamada al sistema UNIX en el mismo sistema, y más de 20 veces más rápido que Mach. Por supuesto, esto ignora el hecho de que L4 no maneja permisos ni seguridad; pero al dejar esto en manos de los programas del espacio de usuario, pueden seleccionar tanta o tan poca sobrecarga como requieran.
Las posibles mejoras de rendimiento de L4 se ven atenuadas por el hecho de que las aplicaciones del espacio de usuario a menudo tendrán que proporcionar muchas de las funciones que antes admitía el núcleo. Para probar el rendimiento de extremo a extremo, se comparó MkLinux en modo coubicado con un puerto L4 que se ejecutaba en el espacio de usuario. L4 agregó aproximadamente un 5%–10% de sobrecarga, [13] en comparación con el 29% de Mach. [13]
La siguiente es una lista de núcleos de sistemas operativos derivados de Mach y sistemas operativos con núcleos derivados de Mach: