Onder Finder, Safari, Terminal en elke applicatie die op een Mac draait, bevindt zich XNU, de kernel van macOS. Vaak wordt het uitgelegd als een combinatie van Mach en BSD, maar dit kan tot verkeerde conclusies leiden: de computer draait niet twee onafhankelijke kernels gelijktijdig. XNU is een hybride kernel die componenten van zowel Mach als BSD binnen hetzelfde kerneladresruimte integreert. Deze architectuur wordt door Apple gehandhaafd sinds de beginjaren van Mac OS X.
De kernpunten van XNU in 30 seconden
- macOS gebruikt één enkele hybride kernel genaamd XNU, niet twee kernels die gelijktijdig draaien.
- Mach levert mechanismen zoals taken, threads, virtueel geheugen en interprocescommunicatie (IPC).
- BSD biedt het grootste deel van de Unix-interface, inclusief POSIX-processen, bestandssystemen, sockets en vele systeemaanroepen.
- Beide componenten opereren binnen hetzelfde kerneladresruimte om de kosten van een pure microkernelarchitectuur te vermijden.
- De open source-code van XNU maakt het mogelijk om specifieke tabellen te zien voor Mach-traps en Unix-aanroepen.
Het verschil is niet alleen academisch. Het begrijpen van de organisatie van XNU helpt bij het interpreteren van prestatietrajecten, IPC-mechanismen, XPC-diensten, planningsproblemen, tracings en bepaalde kernel panics.
Het stelt ook uit waarom macOS zich kan gedragen als een Unix-systeem vanuit het perspectief van ontwikkelaars, terwijl het onder die interface toch concepten van Mach behoudt.
Apple beschrijft XNU officieel als een kernel gebaseerd op het ontwerp van de microkernel Mach, maar die ook BSD-functies integreert. Het bedrijf waarschuwt dat XNU geen strikt microkernel is, omdat veel onderdelen direct binnen de kernel zijn ingebouwd voor prestaties redenen.
Mach en BSD leven binnen dezelfde kernel
Mach werd in de jaren tachtig ontwikkeld aan de Carnegie Mellon University en was gebaseerd op communicatie via berichten.
Concepten zoals tasks, threads, ports en messages blijven belangrijk binnen XNU.
Een task is, eenvoudig gezegd, een uitvoeringsomgeving met een eigen adresruimte en toegewezen bronnen. Threads zijn de eenheden die instructies uitvoeren binnen dat kader.
Mach-ports fungeren als communicatielijnen en vormen een fundamenteel onderdeel van IPC.
De huidige code van XNU behoudt duidelijk deze architectuur. De tabel met Mach-traps bevat operaties voor virtueel geheugen, ports, semaforen, taken, timers en berichtgeving, zoals mach_msg_trap, mach_msg2_trap, thread_self_trap, task_self_trap, semaphore_wait_trap en verschillende operaties op Mach-ports.
De implementatie laat bovendien zien dat XNU blijft evolueren.
Apple markeert mach_msg_trap() als een interface die in toekomstige macOS-versies zal verdwijnen en introduceert mach_msg2_trap() als de moderne vervanging, met ondersteuning voor geneste berichten en Control-Flow Integrity (CFI).
BSD levert nog een andere kant van de persoonlijkheid die ontwikkelaars normaal observeren.
Unix-interfaces gerelateerd aan processen, bestandsbeschrijvingen, sockets, signalen en veel POSIX-ondersteuning komen voort uit dit deel van de architectuur.
Daarom kan een applicatie evenals in Linux gebruik maken van functies zoals read(), open() of close() zonder dat het systeem de onderliggende Mach-infrastructuur kent.
Waarom Apple geen puur Mach-microkernel gebruikt
Een klassieke microkernel-implementatie probeert binnen de kernel alleen de minimale mechanismen te behouden en meer services uit te besteden aan aparte gebruikersruimte-processen.
Deze scheiding brengt architectonische voordelen, maar ook kosten met zich mee.
Wanneer verschillende componenten veelvuldig via berichten communiceren over grensgebieden van bescherming, stijgt het aantal contextwissels en IPC-operaties.
Apple legt uit dat deze prestatiedrempel een belangrijke factor was bij de architectuur van XNU.
In plaats van BSD als een volledig gescheiden server boven op een minimale Mach te draaien, werd BSD-functionaliteit binnen de kernel geïntegreerd samen met Mach. Zo kunnen ze binnen hetzelfde geheugenruimtegebied opereren.
Daarom is het beschrijven van XNU als «Mach met BSD eraan vast» een goede eerste historische interpretatie, maar niet volledig voor de huidige implementatie.
Er bestaan niet twee onafhankelijke kernels, die elke operatie via berichten onderling uitwisselen.
Er is alleen XNU.
Binnen XNU bestaan subsystemen met verschillende roots, abstracties en interfaces.
Deze keuze maakt XNU juist een hybride kernel.
Wat gebeurt er precies wanneer een toepassing read() aanroept?
Een belangrijk nuanceverschil dat vaak wordt onderschat, is dat een dergelijke functie niet automatisch omgezet wordt in een Mach-bericht dat door een extern BSD-serverproces wordt afgehandeld.
De call gaat via de Unix-interface van XNU en kan door BSD-componenten, VFS en andere kernel-subsystemen worden geleid. Mach biedt de fundamentele infrastructuren, maar BSD en Mach bevinden zich niet aan weerszijden van een gebruikersruimte-grens die iedere systeemaanroep moet passeren.
Deze verschillen verklaart waarom Apple beide architecturen heeft samengevoegd.
XNU onderscheidt nog wel tussen Unix-aanroepen en Mach-traps.
De broncode bevat een specifieke tabel, mach_trap_table, waarin bepaalde nummers gereserveerd zijn voor Unix, terwijl de overige voor Mach-gerelateerde operaties bestemd zijn.
Hieronder vallen onder andere mach_msg2_trap voor berichtuitwisseling en operaties voor geheugen, ports en synchronisatie.
Dit betekent dat een macOS-proces afhankelijk van de operatie via verschillende interfacefamilies met de kernel communiceert.
Maar het impliceert niet dat elke BSD-systeemaanroep later automatisch wordt omgezet in een Mach-bericht.
Mach IPC is nog altijd zeer aanwezig op een moderne Mac
Hoewel veel ontwikkelaars jarenlang uitsluitend POSIX gebruiken, blijven de communicatie-mechanismen uit Mach zeer relevant binnen macOS.
Het meest duidelijke voorbeeld is het berichten-systeem.
mach_msg2_trap() maakt het mogelijk berichten te sturen en te ontvangen via IPC-infrastructuur van de kernel. De open source-code van XNU laat zien hoe info wordt verwerkt over lokale en externe ports, verzend- en ontvangstopties, berichtgroottes en andere parameters.
Via deze primitieve fundamenten worden hogere functies ontwikkeld.
Een applicatie hoeft doorgaans niet direct Mach-IPC aan te roepen. Frameworks en systeemdiensten bieden abstracties die communicatie eenvoudiger maken.
Deze scheiding is bewust. Applicaties kunnen bijvoorbeeld werken met bestanden, sockets, processen en threads zonder dat ze de interne Mach-structuren hoeven te kennen. Het feit dat macOS een Unix-compatibel systeem is met POSIX-APIs, ondersteunt deze compatibiliteit.
Maar wanneer de diagnose dieper gaat, komen termen naar voren die niet zomaar in Linux voorkomen.
mach_msg, Mach-ports, tasks, Mach-excepties en intrek- of synchronisatie-primitieven kunnen in traces, analysetools en systeemlogs opduiken.
Waarom macOS en Linux zich anders kunnen gedragen
Linux en XNU hebben vele van dezelfde problemen opgelost met totaal verschillende architecturen.
Linux wordt meestal gekarakteriseerd als een monolithisch modulair kernel. Processen, geheugen, netwerk, drivers en planning maken integraal deel uit van één kernel-architectuur.
XNU heeft een andere achtergrond.
De organisatie combineert concepten van Mach, BSD en andere latere componenten. Deze geschiedenis is nog zichtbaar in de APIs en interne structuren.
Dat betekent niet dat de ene aanpak automatisch sneller of beter is dan de andere.
Voor de meeste software wordt het verschil verhuld door hogere APIs. Een programma in C dat read() aanroept, zal op beide systemen vrijwel hetzelfde gedrag krijgen.
De verschillen worden relevant als men kijkt naar IPC, profilering, beveiliging, virtualisatie, low-level debugging of Darwin-specifieke componenten.
Ook bij het analyseren van kernelcode zelf worden deze verschillen zichtbaar.
Apple blijft de broncode van XNU publiceren, zodat je direct kunt zien hoe deze interfaces worden geïmplementeerd, in plaats van alleen te vertrouwen op historische diagrammen van Mac OS X.
Tracesystemen geven niet altijd het volledige verhaal
Tools zoals dtruss, gebaseerd op DTrace, zijn traditioneel nuttig gebleken om systeemoproepen te volgen.
Een voorbeeld van een typische opdracht:
sudo dtruss -f cat /etc/hostsEen trace kan tonen dat een applicatie bijvoorbeeld een bestand opent, attributen opvraagt, leest en sluit.
Maar applicaties kunnen tegelijkertijd gebruik maken van lagere interfaces die niet passen in die POSIX-visie.
Daarom is het nuttig om inzicht te hebben in de verschillende interfacefamilies van XNU bij het onderzoeken van IPC- of systeemgedrag.
Een bijkomend complicatiepunt bij moderne Macs is dat DTrace-mogelijkheden afhankelijk zijn van systeembeveiligingen. Sommige oude voorbeelden werken niet zonder meer op de nieuwste versies van macOS.
Het is dus niet verstandig om aan te nemen dat oude dtrace-recepten nog altijd zonder meer op nieuwe systemen werken, zeker niet zonder rekening te houden met macOS-versie, architectuur en beveiligingsinstellingen.
XNU is een fusie, geen competitie tussen twee kernels
De meest accurate manier om de architectuur van macOS te begrijpen, is door beide uitersten te vermijden.
Stel dat macOS gewoon Linux is, of dat het twee kernels gelijktijdig zou draaien, is incorrect. Het is complexer.
XNU is één enkele hybride kernel.
Mach levert essentiële abstracties voor geheugenbeheer, uitvoering en IPC. BSD brengt de Unix-kenmerken die applicaties gebruiken. Apple heeft beide binnen hetzelfde kerneladresruimte geïntegreerd, om de functionaliteit te behouden zonder de kosten van een volledig microkernelontwerp te maken.
Deze aanpak bestaat al meer dan twintig jaar in macOS en is nog steeds zichtbaar in de huidige XNU-code.
Voor een ontwikkelaar die alleen open(), read() of sockets gebruikt, is dit misschien een onmerkbaar detail. Maar voor diegene die IPC analyseert, systeemcomponenten debugt of diep in tracings duikt, helpt het om te weten waar de Unix-interfaces eindigen en waar Mach-abstractions beginnen.
Veelgestelde vragen
Draait macOS daadwerkelijk twee kernels tegelijk?
Nee. macOS gebruikt één enkele hybride kernel, XNU. Deze combineert Mach- en BSD-componenten binnen dezelfde geheugenruimte van de kernel.
Wat betekent XNU?
De naam wordt meestal geïnterpreteerd als «X is Not Unix». Echter, XNU biedt veel van de infrastructuur die macOS in staat stelt zijn Unix-omgeving te ondersteunen.
Wat levert Mach aan het macOS-kernel?
Mach biedt concepten en mechanismen voor taken, threads, virtueel geheugen, ports, IPC en andere interne primitieve operaties die door XNU worden gebruikt.
Wat levert BSD aan XNU?
BSD levert het grootste deel van de Unix-interfaces, zoals POSIX-processen, signalen, sockets, virtuele bestandssystemen en vele systeemaanroepen die applicaties gebruiken.
