Microkernels contra kernels monolítics: compromisos revisitats
Pocs debats de disseny de sistemes operatius han durat tant com el debat entre microkernels i kernels monolítics.
A primer cop d’ull, la diferència sembla senzilla:
- els kernels monolítics mantenen la majoria dels serveis del sistema operatiu dins del kernel
- els microkernels mouen la majoria dels serveis a l’espai d’usuari
A la pràctica, el compromís és més subtil.
La pregunta real no és si una estructura és universalment més ràpida, més neta o més segura. La pregunta real és on han de viure l’autoritat, la complexitat, els errors i els costos de rendiment.
Aquesta entrada revisita aquest compromís, explica per què molts arguments antics sobre microkernels es van simplificar massa i mostra per què sistemes moderns com EriX tornen a fer pràctic el model de microkernel.
La forma històrica del debat⌗
Els primers sistemes operatius es van construir sota restriccions de maquinari molt severes.
La memòria era limitada. Les CPU eren més lentes. Els canvis de context eren cars. Les memòries cau, les TLB, els sistemes multiprocessador i les rutes ràpides de syscall eren molt menys capaces que avui.
Sota aquestes restriccions, els kernels monolítics eren una opció natural.
Els sistemes semblants a Unix col·locaven sistemes de fitxers, controladors de dispositius, xarxes, gestió de processos i molts altres serveis dins d’un únic espai d’adreces privilegiat del kernel. Aquest disseny feia barates moltes operacions:
- un sistema de fitxers podia cridar directament la capa de blocs
- una pila de xarxa podia accedir directament a estructures del controlador
- els subsistemes del kernel podien compartir dades sense IPC
El resultat era eficient i pragmàtic.
També volia dir que grans quantitats de codi s’executaven amb privilegis complets de kernel.
Per què van aparèixer els microkernels⌗
Els microkernels van néixer d’una altra observació:
La major part del codi d’un sistema operatiu no necessita autoritat completa sobre la màquina.
Un sistema de fitxers no necessita modificar taules de pàgines arbitràries. Un controlador de teclat no necessita accedir a tots els processos. Una pila de xarxa no necessita controlar el planificador.
Els microkernels mantenen dins del kernel només els mecanismes més fonamentals, normalment:
- planificació
- gestió d’espais d’adreces
- comunicació entre processos
- gestió de capacitats o identificadors
- lliurament d’interrupcions i excepcions
Els serveis de més alt nivell s’executen com a processos ordinaris d’espai d’usuari.
Això dona al sistema un aïllament més fort. Una fallada d’un controlador no ha de ser necessàriament una fallada del kernel. Un error en un sistema de fitxers no es converteix automàticament en corrupció arbitrària de memòria del kernel. L’autoritat es pot distribuir amb més precisió.
La idea era convincent, però les primeres implementacions sovint van tenir problemes de rendiment i compatibilitat.
El primer problema de rendiment⌗
La crítica clàssica als microkernels és que són lents.
Aquesta crítica no va aparèixer del no-res.
Alguns primers sistemes microkernel col·locaven serveis tradicionals del sistema operatiu darrere de molts servidors separats en espai d’usuari, i després intentaven preservar interfícies Unix familiars per sobre. Una operació senzilla podia convertir-se en una cadena de missatges:
- aplicació al servidor de fitxers
- servidor de fitxers al gestor de memòria
- gestor de memòria al paginador
- paginador al servei de blocs
- servei de blocs al controlador
Cada pas podia implicar un canvi de context, validació de missatges, una decisió de planificació i de vegades còpies.
Si les interfícies són massa conversacionals, el cost s’acumula.
L’error va ser convertir això en una regla universal:
Els microkernels són lents.
Una regla més precisa és:
Les rutes IPC mal dissenyades i els límits de servei massa conversacionals són lents.
Aquesta distinció importa.
La ruta ràpida monolítica⌗
Els kernels monolítics poden ser extremadament ràpids perquè eviten molts límits de protecció.
Un sistema de fitxers dins del kernel pot cridar una capa de blocs dins del kernel amb una crida de funció normal. Un controlador pot compartir memòria directament amb un altre subsistema. No cal serialitzar cada petició en un format de missatge.
Això és un avantatge real.
Però no és gratuït.
La ruta ràpida monolítica sovint comporta:
- més codi privilegiat
- més estat mutable compartit
- més complexitat de bloquejos interns del kernel
- més maneres que un subsistema en corrompi un altre
- una base de còmput de confiança més gran
El rendiment no és només comptar instruccions. També és comportament de cau, contenció de bloquejos, contenció de fallades, recuperació i el cost de mantenir la correcció al llarg del temps.
Un kernel monolític pot guanyar un microbenchmark cru i alhora fer més difícils l’aïllament i l’auditabilitat.
Mite de rendiment: cada límit és fatal⌗
Un mite comú és que cada límit de microkernel és tan car que el disseny no pot competir.
Aquesta visió està antiquada.
Un límit té un cost, però els sistemes moderns poden fer que aquest cost sigui gestionable:
- rutes ràpides de syscall i retorn
- millors heurístiques de planificació
- rutes de dades amb memòria compartida
- mapatge de pàgines en lloc de còpies massives
- peticions per lots
- lliurament asíncron d’esdeveniments
- ABI d’IPC dissenyades amb cura
L’objectiu important de disseny és mantenir la política fora del kernel sense forçar que cada byte de dades passi pel kernel.
El kernel ha de mediar autoritat. No necessàriament ha de moure totes les dades.
Mite de rendiment: IPC vol dir copiar-ho tot⌗
IPC sovint s’imagina com “copia tot aquest buffer del procés A al procés B”.
Això només és un disseny possible.
Un microkernel pot passar petits missatges de control mentre transfereix autoritat sobre memòria compartida, frames, endpoints o objectes de dispositiu. La ruta de dades cara pot continuar mapada, mentre el kernel només valida qui està autoritzat a accedir-hi.
Això és central en el disseny basat en capacitats.
En lloc de copiar grans estructures de dades a través d’un subsistema privilegiat, un procés pot rebre una capacitat que autoritza l’accés a un objecte específic amb drets específics.
El kernel continua sent responsable d’aplicar la transferència. No necessita entendre tots els protocols d’alt nivell construïts damunt d’aquesta transferència.
Mite de rendiment: els controladors en espai d’usuari no són pràctics⌗
Els controladors en espai d’usuari sovint es tracten com una idea de recerca.
La preocupació és comprensible. L’accés al maquinari és sensible, les interrupcions depenen del temps i els controladors sovint són en rutes calentes.
Però la majoria de controladors no necessiten autoritat completa de kernel.
Un controlador normalment necessita accés a:
- un rang concret de ports d’E/S
- una regió MMIO concreta
- una línia d’interrupció concreta
- una disposició concreta de DMA o buffers
Aquestes són formes d’autoritat més estretes que “tot el kernel”.
Si el kernel pot delegar exactament aquests recursos, un controlador pot executar-se fora del kernel i continuar fent feina útil. Si falla, el sistema té l’oportunitat d’aturar, reiniciar o substituir aquest controlador sense tractar la fallada com corrupció de memòria del kernel.
El compromís és real: els controladors en espai d’usuari necessiten bon IPC, lliurament acurat d’interrupcions i propietat explícita dels recursos. Però el model no és intrínsecament impràctic.
Què posa EriX al kernel⌗
EriX està dissenyat com un microkernel de capacitats.
El kernel d’EriX és intencionadament mínim en política. Els seus documents d’arquitectura defineixen que el kernel és responsable de:
- validar el traspàs del bootloader al kernel
- gestionar objectes bàsics del kernel i semàntica de capacitats
- crear la tasca arrel
- exposar punts d’entrada de traps, syscalls i interrupcions
El kernel explícitament no és responsable de:
- política del sistema
- política d’orquestració de processos
- política de memòria d’alt nivell
- política de cicle de vida de serveis
Aquesta és la línia del microkernel a la pràctica.
El kernel comença amb autoritat de màquina, però ha de convertir aquesta autoritat en objectes explícits del kernel i referències de capacitat. No s’ha de filtrar autoritat ambiental a l’espai d’usuari.
Què mou EriX fora del kernel⌗
EriX col·loca la funcionalitat que porta política en serveis d’espai d’usuari.
Per exemple:
rootdés la primera autoritat d’espai d’usuari que porta políticaprocdposseeix la gestió del cicle de vida de processosdevicedposseeix la política de controladors i l’orquestració del seu inicivfsdposseeix l’espai de noms públic del sistema de fitxers- proveïdors de sistema de fitxers com
ramfsd,e2fsdifatdcontinuen sent parells backend privats darrere devfsd
Això no és només “moure codi fora del kernel” com una elecció estètica.
Cada límit de servei defineix un límit d’autoritat.
rootd distribueix capacitats d’arrencada de privilegi mínim. procd crea i
inicia processos mitjançant creació de fills per etapes i permisos
d’instal·lació. deviced no es converteix directament en el kernel; demana a
procd que gestioni processos de controlador i passa només l’autoritat de
controlador requerida per a cada rol.
Aquesta estructura és més verbosa que un graf de crides de kernel monolític, però fa visible el flux d’autoritat.
Autoritat estreta en lloc de privilegi ampli⌗
Un dels detalls d’implementació més importants d’EriX és l’abandonament d’un endpoint arrel ampli com a superfície normal de control en temps d’execució.
El kernel actual exposa famílies estretes d’endpoints de control del kernel per a feines específiques:
- control de temps
- control d’interrupcions
- esdeveniments de hotplug
- lectures de configuració PCI
- accés a consola i framebuffer
- E/S COM1
- E/S i8042
- retyping de memòria
- mapatge de VSpace
- resolució de fallades del paginador
- control de processos
- lectures ACPI
El despatx en temps d’execució es determina per l’objecte endpoint i el seu tipus, no per un número de slot global privilegiat.
Això importa perquè una tasca no obté autoritat només per conèixer un valor de slot convencional. Ha de tenir realment la capacitat correcta en el seu propi espai local de capacitats.
Per exemple, drv-serial rep autoritat d’E/S específica de COM1. drv-i8042
rep autoritat d’E/S específica d’i8042. drv-acpi rep autoritat de lectura
ACPI. probed rep autoritat de lectura de configuració PCI.
Aquesta és una forma de seguretat diferent de posar totes aquestes operacions darrere d’un únic manejador ampli del kernel.
Memòria de dispositiu com a objecte explícit⌗
EriX també tracta l’autoritat sobre memòria de dispositiu com a explícita i tipada.
El kernel té un CAP_TYPE_DEVICE_FRAME distint per a memòria de dispositiu
validada. En la ruta d’emmagatzematge, es pot derivar per a deviced un frame
MMIO recolzat per BAR, i deviced pot instal·lar només aquest frame de
dispositiu derivat al paquet d’inici per etapes del controlador.
La qüestió no és que els controladors de dispositiu es tornin simples.
La qüestió és que l’autoritat MMIO no es confon amb frames de RAM ordinaris i no s’exposa mitjançant una via genèrica de “fer qualsevol cosa amb memòria de dispositiu”.
Aquest és exactament el tipus de detall que fa viables els microkernels moderns: l’accés al maquinari es delega com un objecte precís amb drets precisos.
IPC com a ABI, no com a accident⌗
En un kernel monolític, moltes interfícies internes són crides de funció ordinàries.
En un microkernel, IPC passa a formar part de l’ABI del sistema. Això el fa més important, no menys.
EriX tracta IPC com un contracte compartit:
- les capçaleres de missatge tenen versió
- els layouts són fixos
- l’anàlisi usa aritmètica comprovada
- les càrregues mal formades fallen de manera tancada
- les transferències de capacitats són explícites
- els missatges de runtime que porten transferències requereixen
GRANT
Això és el contrari de tractar IPC com una idea posterior.
El cost d’IPC es controla en part amb implementació, però també amb disseny d’interfícies. Una ABI dissenyada amb cura evita viatges innecessaris, manté els missatges acotats i separa la transferència de control del moviment de dades.
Per què els microkernels tornen a ser viables⌗
Els microkernels són més viables avui per diverses raons.
1. El maquinari ha canviat⌗
El cost relatiu d’un límit de protecció ha canviat.
Els canvis de context i les syscalls encara no són gratuïts, però les CPU modernes, els sistemes de memòria i els mecanismes d’interrupció fan que el cost brut sigui menys decisiu que quan es van jutjar els primers experiments amb microkernels.
Alhora, els sistemes moderns són més complexos i estan més exposats. El cost de comprometre el kernel ha augmentat.
L’aïllament és ara més valuós.
2. Entenem millor IPC⌗
La lliçó dels sistemes anteriors no és “evitar IPC”.
La lliçó és:
- evitar IPC innecessari
- evitar protocols massa conversacionals
- evitar copiar grans dades quan transferir autoritat és suficient
- dissenyar límits de servei al voltant de propietat real
Els microkernels són viables quan IPC es tracta com un problema de disseny de primera classe.
3. Les capacitats fan útils els límits⌗
Moure codi a l’espai d’usuari és només la meitat de la història.
Si tots els servidors d’espai d’usuari encara reben privilegi implícit ampli, el sistema ha recreat en gran part un monòlit amb canvis de context addicionals.
Les capacitats donen significat al límit.
En EriX, l’autoritat es representa mitjançant capacitats tipades amb drets explícits. Els serveis validen les capacitats que reben. Els paquets d’inici descriuen l’autoritat declarada. El codi del kernel i dels serveis evita tractar els números de slot canònics com a permís ambiental.
Això fa que la descomposició sigui més que modularitat. Fa que la descomposició sigui part del model de seguretat.
4. El llenguatge i les eines han millorat⌗
Els llenguatges d’implementació i les eines modernes també canvien el compromís.
Rust no elimina els errors de sistemes operatius, però fa més difícil escriure accidentalment molts errors de seguretat de memòria. També fa visibles els límits insegurs durant la revisió.
Per a un sistema microkernel, això és especialment útil. El kernel pot continuar sent petit i auditable, mentre que els serveis d’espai d’usuari encara es poden escriure amb garanties de seguretat més fortes que els components de sistema tradicionals dominats per C.
EriX combina això amb un enfocament de sala neta i sense crates de tercers, cosa que manté el sistema més fàcil d’auditar encara que augmenti el treball d’implementació.
Els costos restants⌗
Els microkernels encara tenen costos reals.
Requereixen:
- lògica d’inici més explícita
- contractes IPC acuradament versionats
- supervisió robusta de serveis
- més atenció al processament per lots i al moviment de dades
- propietat clara de cada capacitat
- bon traçat i mesura del rendiment
També mouen part de la complexitat fora del kernel en lloc d’eliminar-la.
rootd, procd, deviced i els serveis de sistema de fitxers encara
necessiten un disseny acurat. Poden estar fora del kernel, però encara poden ser
components de confiança per a parts específiques del sistema.
La diferència és que la seva autoritat pot ser més estreta que l’autoritat del kernel, i les seves fallades es poden contenir més deliberadament.
El compromís revisitat⌗
L’antic marc sovint era:
- els kernels monolítics són ràpids
- els microkernels són nets però lents
Aquest marc és massa simple.
Un marc millor és:
- els kernels monolítics optimitzen la cooperació directa dins del kernel
- els microkernels optimitzen l’autoritat explícita i l’aïllament de fallades
- qualsevol dels dos dissenys pot ser ràpid o lent segons la implementació
- qualsevol dels dos dissenys pot esdevenir complex si els límits s’escullen malament
Per a EriX, l’elecció de microkernel segueix dels objectius del sistema:
- base de còmput de confiança mínima
- autoritat explícita mitjançant capacitats
- separació estricta entre kernel i espai d’usuari
- límits de servei auditables
- arrencada i comportament de fallada deterministes
Aquests objectius no fan que el rendiment sigui irrellevant.
Defineixen on ha de passar la feina de rendiment: IPC ràpid, interfícies de servei acurades, rutes de dades amb memòria compartida, famílies estretes d’endpoints i transferència explícita de capacitats.
Mirant endavant⌗
Els microkernels no són una drecera.
Exigeixen més disciplina de disseny inicial que un simple graf de crides dins del kernel. Forcen el sistema a definir aviat l’autoritat, la propietat i el comportament davant fallades.
Precisament per això són interessants.
EriX usa el model de microkernel no perquè estigui de moda, sinó perquè encaixa amb l’arquitectura: un kernel petit, autoritat mediada per capacitats i política implementada per serveis explícits d’espai d’usuari.
La propera entrada examinarà la idea que motiva gran part d’aquesta estructura: la base de còmput de confiança.
Veurem què inclou realment la TCB, per què la seva mida afecta la superfície d’atac i com EriX intenta mantenir petit el codi de confiança movent la política a serveis d’espai d’usuari explícits i restringits per capacitats.