Cos’è e Come Funziona il Kernel di Linux

In molti sono all’oscuro del funzionamento di Linux, molti lo interpretano come un’interfaccia grafica, ormai che anche questo sistema operativo è diventato trasparente per complessità come Windows. Eppure il sistema operativo è composto da diverse componenti, dalle più evidenti alle più nascoste, ma che tutte poggiano su di un elemento base: il kernel che equivale a nucleo in Italiano.
L’importanza del kernel è tale che in molti ambienti universitari si studia solo questo, anche Linux è nato da un kernel, poi attorno sono nati dei pacchetti, soprattutto quelli basilari della GNU, che lo fanno diventare un sistema operativo completo, infatti andrebbe chiamato GNU/Linux.
I kernel possono essere suddivisi in due grandi categorie: quelli monolitici di cui anche Linux fa parte ed i microkernel come ad esempio il Mac ( non in quelli attuali dove è ibrido ). Nei kernel monolitici, il nucleo è un componente molto complesso e dalle dimensioni notevoli, mentre nei microkernel abbiamo una struttura più semplice occupandosi di gestire soltanto la memoria e demandando le altre operazioni a moduli esterni al kernel che dialogano tramite dei protocolli simili a quelli di rete. Veramente anche Linux presenta un kernel modulare con i moduli caricabili a runtime, ma la sua struttura interna è sempre quella di un blocco monolitico, perché un microkernel ha bisogno di una fase di progettazione molto complessa e la creazione dei componenti esterni deve essere armonica, quindi tutto il contrario di Linux in cui attorno al kernel di Linus Torvalds è stato costruito l’involucro software da parte di GNU.
Linux quindi poggia su basi vecchie, infatti proviene da un sistema operativo Unix nato oltre 40 anni fa in ambito universitario, ma la struttura monolitica del suo kernel ha dei vantaggi in termini di velocità, altresì presenta dei limiti perché un kernel così grande è difficile da gestire, decine di protocolli di rete, decine di filesystem, migliaia di driver diversi. Ad un certo punto della sua vita quindi si è sentita la necessità di dare una struttura modulare al kernel introducendo i moduli, in modo tale da snellirlo, ma non variando la sua natura monolitica, ad esempio un’unica istanza di esecuzione per kernel e moduli.
Il successo di Unix è stato soprattutto per la suddivisione del sistema in livelli, ognuno con compiti specifici che può assolvere richiedendo al livello immediatamente inferiore di fornirgli il servizio. In pratica ogni livello inferiore fornisce servizi ai livelli superiori e sfrutta quelli esportati dal livello inferiore. Ecco i vantaggi di una suddivisione di questo tipo:

  • Il sistema operativo diventa più portabile riducendo al minimo il codice in assembly legato all’architettura.
  • Si può scrivere dei componenti del kernel senza conoscere i dettagli degli strati inferiori, basta implementare determinate interfacce standard per il passaggio di informazioni tra uno strato e l’altro.
  • Viene aumentata la robustezza del sistema visto che eventuali problemi agli strati superiori non hanno ripercussioni in quelli inferiori.

In uno scorso articolo abbiamo visto cosa sono i processi ed abbiamo fatto un esempio sulla fork che crea una copia esatta del processo padre ma in un’area di memoria distinta. Se tutti i processi sono creati da un padre chi crea il primo processo ( init ) è proprio il kernel. Durante la loro vita, i processi possono attraversare diversi stati, ad esempio un processo che non ha dati si mette in attesa e viene risvegliato al momento della comparsa di questi dati. Ma chi decide quale dei processi in attesa deve essere destato? Lo schedulatore del kernel, quando ci sono diversi processi nello stato “pronto”, ne sceglie uno da mandare in esecuzione, di solito la precedenza viene data ai processi interattivi in modo che all’utilizzatore il sistema dia il senso di maggior prontezza anche se in effetti la CPU, come al solito, viene suddivisa tra i vari processi.
Come dicevamo ogni processo ha un proprio spazio di memoria suddiviso in tre segmenti: testo, dati e stack. Il testo contiene codice binario con le istruzioni macchina che costituiscono il programma, per risparmiare memoria Linux utilizza un solo segmento testo per più processi che eseguono lo stesso programma. Il segmento dati contiene le informazioni necessarie al processo come stringhe, vettori, variabili ed altro, Linux adotta una tecnica detta COW ( Copy On Write ) per questa zona di memoria, ossia quando viene creato dalla fork il nuovo processo figlio, questo continua a condividere la stessa memoria del padre, ma quando occorre modificare un valore, allora viene copiata quella zona di memoria. Questo rende la creazione dei processi molto più veloce rispetto al dover copiare fin dall’inizio tutta la memoria del padre nel figlio. Lo stack al momento della creazione del processo ha già un insieme di informazioni, la riga di comando utilizzata per invocare l’eseguibile ed altri, poi serve al processo come zona d’appoggio per una serie di informazioni utili allo svolgimento dei compiti.
Un’altro compito importantissimo del kernel è la gestione del filesystem che non potrebbe essere demandata alle applicazioni perché ci sarebbero alcune che scriverebbero magari dei tratti che altre stanno leggendo, è il kernel che svolge il compito di supervisore per impedire questi fenomeni. Il kernel quindi si occupa di mascherare la complessità del filesystem alle applicazioni e di controllarne l’accesso alle risorse che lavorano su descrittori di file fornitigli dallo stesso kernel.
In questo articolo ho cercato di far capire ai neofiti di Linux e non solo, cosa sia il kernel ed il compito importantissimo che svolge nell’interazione delle applicazioni con l’hardware del sistema.

Informazioni su Giampaolo Rossi

Sviluppatore di software gestionale da oltre 28 anni.
Questa voce è stata pubblicata in Linux e contrassegnata con . Contrassegna il permalink.