XNU: il kernel ibrido per eccellenza!

Ben ritrovati nella parte finale del primo capitolo di questa rubrica! Abbiamo terminato di vedere le syscall e le app supportate in macOS. Oggi vedremo a grandi linee da cosa è composto XNU, il kernel che muove sia macOS che iOS.

Partiamo dal principio: cos’è XNU?

XNU è un kernel, cioè un particolare programma che permette al sistema operativo di funzionare senza problemi sull’hardware nel quale è installato. E’ un elemento fondamentale, non a caso kernel significa nucleo. Bisogna immaginare XNU come un intermediario, tra il software (ad esempio, iOS) e l’hardware (le componenti di un iPhone); è il primo programma ad essere caricato in memoria quando si accende un dispositivo ed è l’ultimo ad essere chiuso in fase di spegnimento.

XNU quindi è il kernel utilizzato nel sistema operativo open source Darwin, che Apple usa come base per il suo sistema operativo macOS; è ibrido, significa praticamente che è composto da due tipi di kernel, uno basato su Mach e uno basato su BSD (ricordate il discorso delle syscall che abbiamo già visto?).

Possiamo quindi assumere che XNU è basato su tre componenti principali:

  • Parte del microkernel Mach
  • Parte del kernel BSD
  • I/O Kit

Ma prima di addentrarci nella spiegazione di ogni singola parte è bene fare un’osservazione: XNU è anche modulare che semplicemente significa che si può utilizzare qualche modulo aggiuntivo attivabile o disattivabile a piacere durante il funzionamento. Per l’appunto esistono le Kernel Extensions (KExts) che sono le estensioni che si attivano dinamicamente su richiesta.

Mach

Il cuore di XNU è Mach.

Mach è stato originariamente sviluppato presso la Carnegie Mellon University come progetto di ricerca per creare un kernel leggero ed efficiente per i sistemi operativi. Il risultato di questa ricerca è stato il microkernel Mach, che gestisce le responsabilità più primitive, quelle più remote del sistema operativo:

  1. Elaborazione dei thread
  2. Gestione della memoria virtuale
  3. Pianificazione delle attività
  4. Scambio di messaggi tra processi

Le API che questo kernel offre sono molto limitate, tanto che Apple ne scoraggia l’uso, anche se – come vedremo – sono fondamentali e senza di esse nulla funzionerebbe. Di qualsiasi funzionalità aggiuntiva, (come l’accesso ai file e ai dispositivi) se ne occupa il livello BSD.

BSD

Questo livello presenta un’API solida e moderna che fornisce la piena compatibilità POSIX discussa in un precedente articolo. Il livello BSD fornisce delle astrazioni di livello superiore, tra cui:

  1. Il modello di processi Unix
  2. Il modello di threading POSIX (Pthread) con le relative primitive di sincronizzazione
  3. Gli utenti e i gruppi Unix
  4. Lo stack di rete (BSD Socket API – grazie a @Valkyagan)
  5. L’accesso al file system
  6. L’accesso ai dispositivi – mediante la directory /dev)

L’implementazione BSD su XNU è ampiamente compatibile con FreeBSD, ma presenta alcune variazioni degne di nota; dopo aver trattato Mach, porteremo la nostra attenzione su BSD, concentrandoci sulle implementazioni del core BSD e fornendo dettagli specifici sullo switch del file system virtuale e lo stack di rete in dei futuri articoli dedicati.

I/O Kit

La modifica più importante che Apple ha attuato su XNU è stata l’introduzione del framework I/O Kit.  Essendo completamente autonomo dal kernel ed attraverso un’ambiente C++ limitato consente agli sviluppatori di creare rapidamente driver per i dispositivi (a patto che siano stabili) mantenendo le funzionalità più importanti offerte dal linguaggio: ereditarietà e overloading

La scrittura di un driver I/O Kit diventa quindi semplice perché basta trovare un driver esistente da usare come superclasse per ereditarne tutte le funzionalità in runtime. (cioè in fase di esecuzione)  L’ereditarietà evita quindi del codice che in gergo viene detto boilerplate (ripetitivo) che potrebbe portare a bug di stabilità, e inoltre rende il codice del driver molto piccolo – che è sempre cosa buona e giusta, dovendo rispettare i vincoli di memoria in kernelspace. Qualsiasi modifica alle funzionalità può essere introdotta aggiungendo dei nuovi metodi ai driver o eseguendo l’overloading/hiding di quelli già esistenti.

Un altro vantaggio nell’uso di un’ambiente C++ è che i driver operano in un ambiente orientato agli oggetti: ciò li rende profondamente differenti rispetto a qualsiasi altro driver su altri sistemi operativi, che sono limitati al C e all’assembly e richiedono del codice ‘pesante’ anche per la funzionalità più basilari. Per supportare il runtime di C++, XNU include libkern, che è una libreria C++ integrata e autonoma che, pur non esportando le API direttamente in modalità utente (usermode), resta comunque fondamentale poichè senza di essa non sarebbe possibile disporre della grande quantità di funzionalità avanzate alle quali siamo abituati.

 

Anche per questo articolo è tutto. Per eventuali domande, curiosità o feedback potete lasciare un commento qui in basso, a presto!

Approfondimenti