Nei computer, affinché un processo sia eseguibile, deve essere messo in memoria. Per questo, un campo deve essere assegnato a un processo in memoria. L'allocazione della memoria è una questione importante da tenere presente, specialmente nel kernel e nelle architetture di sistema.

Diamo un'occhiata in dettaglio all'allocazione della memoria di Linux e capiamo cosa succede dietro le quinte.

Come viene eseguita l'allocazione della memoria?

La maggior parte degli ingegneri del software non conosce i dettagli di questo processo. Ma se sei un candidato programmatore di sistema, dovresti saperne di più. Quando si esamina il processo di allocazione, è necessario entrare in un piccolo dettaglio su Linux e il glibc biblioteca.

Quando le applicazioni necessitano di memoria, devono richiederla al sistema operativo. Questa richiesta dal kernel richiederà naturalmente una chiamata di sistema. Non è possibile allocare memoria da soli in modalità utente.

Il malloc() famiglia di funzioni è responsabile dell'allocazione della memoria nel linguaggio C. La domanda da porsi qui è se malloc(), come funzione glibc, effettua una chiamata di sistema diretta.

instagram viewer

Non esiste una chiamata di sistema chiamata malloc nel kernel Linux. Tuttavia, ci sono due chiamate di sistema per le richieste di memoria delle applicazioni, che sono br e mmmap.

Dal momento che richiederai memoria nella tua applicazione tramite le funzioni glibc, ti starai chiedendo quale di queste chiamate di sistema sta usando glibc a questo punto. La risposta è entrambe le cose.

La prima chiamata di sistema: brk

Ogni processo ha un campo dati contiguo. Con la chiamata di sistema brk, il valore di interruzione del programma, che determina il limite del campo dati, viene aumentato e viene eseguito il processo di assegnazione.

Sebbene l'allocazione della memoria con questo metodo sia molto veloce, non è sempre possibile restituire lo spazio inutilizzato al sistema.

Ad esempio, considera di allocare cinque campi, ciascuno di 16 KB di dimensione, con la chiamata di sistema brk tramite la funzione malloc(). Quando hai finito con il numero due di questi campi, non è possibile restituire la risorsa pertinente (deallocation) in modo che il sistema possa utilizzarla. Perché se riduci il valore dell'indirizzo per mostrare il punto in cui inizia il tuo campo numero due, con una chiamata a brk, avrai fatto deallocazione per i campi numero tre, quattro e cinque.

Per prevenire la perdita di memoria in questo scenario, l'implementazione malloc in glibc monitora le posizioni allocate nel campo dei dati di processo e quindi specifica di restituirlo al sistema con la funzione free(), in modo che il sistema possa utilizzare lo spazio libero per ulteriore memoria assegnazioni.

In altre parole, dopo che sono state allocate cinque aree da 16 KB, se la seconda area viene restituita con la funzione free() e un'altra area da 16 KB viene richiesto nuovamente dopo un po', invece di ampliare l'area dati tramite la chiamata di sistema brk, viene restituito l'indirizzo precedente.

Tuttavia, se l'area appena richiesta è maggiore di 16 KB, l'area dati verrà ingrandita assegnando una nuova area con la chiamata di sistema brk poiché l'area due non può essere utilizzata. Sebbene l'area numero due non sia in uso, l'applicazione non può utilizzarla a causa della differenza di dimensioni. A causa di scenari come questo, c'è una situazione chiamata frammentazione interna e, infatti, è possibile utilizzare raramente tutte le parti della memoria al massimo.

Per una migliore comprensione, prova a compilare ed eseguire la seguente applicazione di esempio:

#includere <stdio.h>
#includere <stdlib.h>
#includere <unistd.h>
intprincipale(int argc, car* argv[])
{
car *ptr[7];
int n;
printf("Pid di %s: %d", argv[0], getpid());
printf("Interruzione iniziale del programma: %p", numero (0));
per (n=0; n<5; n++) ptr[n] = malloc (16 * 1024);
printf("Dopo 5 x 16kB malloc: %p", numero (0));
libero(pt[1]);
printf("Dopo il secondo 16kB libero: %p", numero (0));
ptr[5] = malloc (16 * 1024);
printf("Dopo aver allocato il 6° di 16kB: %p", numero (0));
libero(pt[5]);
printf("Dopo aver liberato l'ultimo blocco: %p", numero (0));
ptr[6] = malloc (18 * 1024);
printf("Dopo aver allocato un nuovo 18kB: %p", numero (0));
getchar();
Restituzione0;
}

Quando esegui l'applicazione, otterrai un risultato simile al seguente output:

Pid di ./a.out: 31990
Programma iniziale rompere: 0x55ebcadf4000
Dopo 5 x 16 kB malloc: 0x55ebcadf4000
Dopo il secondo 16kB libero: 0x55ebcadf4000
Dopo aver allocato il 6° di 16kB: 0x55ebcadf4000
Dopo aver liberato l'ultimo blocco: 0x55ebcadf4000
Dopo aver assegnato a nuovo18kB: 0x55ebcadf4000

L'output per brk con strace sarà il seguente:

freno(NULLO) = 0x5608595b6000
freno (0x5608595d7000) = 0x5608595d7000

Come potete vedere, 0x21000 è stato aggiunto all'indirizzo finale del campo dati. Puoi capirlo dal valore 0x5608595d7000. Quindi approssimativamente 0x21000o sono stati allocati 132 KB di memoria.

Ci sono due punti importanti da considerare qui. Il primo è l'assegnazione di un importo superiore a quello specificato nel codice di esempio. Un altro è quale riga di codice ha causato la chiamata brk che ha fornito l'allocazione.

Randomizzazione del layout dello spazio degli indirizzi: ASLR

Quando esegui l'applicazione di esempio sopra una dopo l'altra, vedrai ogni volta valori di indirizzo diversi. Modificare lo spazio degli indirizzi in modo casuale in questo modo complica notevolmente il lavoro di attacchi di sicurezza e aumenta la sicurezza del software.

Tuttavia, nelle architetture a 32 bit, vengono generalmente utilizzati otto bit per randomizzare lo spazio degli indirizzi. L'aumento del numero di bit non sarà appropriato poiché l'area indirizzabile sui bit rimanenti sarà molto bassa. Inoltre, l'uso di sole combinazioni a 8 bit non rende le cose abbastanza difficili per l'attaccante.

Nelle architetture a 64 bit, invece, poiché ci sono troppi bit che possono essere allocati per il funzionamento ASLR, viene fornita una casualità molto maggiore e il grado di sicurezza aumenta.

Anche il kernel Linux alimenta Dispositivi basati su Android e la funzione ASLR è completamente attivata su Android 4.0.3 e versioni successive. Anche solo per questo motivo, non sarebbe sbagliato affermare che uno smartphone a 64 bit offre un notevole vantaggio di sicurezza rispetto alle versioni a 32 bit.

Disabilitando temporaneamente la funzione ASLR con il comando seguente, sembrerà che l'applicazione di test precedente restituisca gli stessi valori di indirizzo ogni volta che viene eseguita:

eco0 | sudo tee /proc/sys/kernel/randomize_va_space

Per riportarlo allo stato precedente, sarà sufficiente scrivere 2 anziché 0 nello stesso file.

La seconda chiamata di sistema: mmap

mmap è la seconda chiamata di sistema utilizzata per l'allocazione della memoria su Linux. Con la chiamata mmap, lo spazio libero in qualsiasi area della memoria viene mappato allo spazio degli indirizzi del processo di chiamata.

In un'allocazione di memoria eseguita in questo modo, quando si desidera restituire la seconda partizione da 16 KB con la funzione free() nell'esempio precedente di brk, non esiste alcun meccanismo per impedire questa operazione. Il relativo segmento di memoria viene rimosso dallo spazio di indirizzi del processo. Viene contrassegnato come non più utilizzato e restituito al sistema.

Poiché le allocazioni di memoria con mmap sono molto lente rispetto a quelle con brk, è necessaria l'allocazione di brk.

Con mmap, qualsiasi area di memoria libera viene mappata allo spazio degli indirizzi del processo, quindi il contenuto dello spazio allocato viene reimpostato prima del completamento del processo. Se il ripristino non è stato eseguito in questo modo, i dati appartenenti al processo che in precedenza utilizzava l'area di memoria pertinente potrebbero essere accessibili anche dal successivo processo non correlato. Ciò renderebbe impossibile parlare di sicurezza nei sistemi.

Importanza dell'allocazione della memoria in Linux

L'allocazione della memoria è molto importante, soprattutto per quanto riguarda l'ottimizzazione e i problemi di sicurezza. Come visto negli esempi precedenti, non comprendere appieno questo problema può significare distruggere la sicurezza del tuo sistema.

Anche concetti simili a push e pop che esistono in molti linguaggi di programmazione si basano su operazioni di allocazione della memoria. Essere in grado di utilizzare e padroneggiare bene la memoria di sistema è vitale sia nella programmazione di sistemi embedded che nello sviluppo di un'architettura di sistema sicura e ottimizzata.

Se vuoi anche tuffarti nello sviluppo del kernel Linux, considera prima di tutto padroneggiare il linguaggio di programmazione C.

Una breve introduzione al linguaggio di programmazione C

Leggi Avanti

CondividereTwittaCondividereE-mail

Argomenti correlati

  • Linux
  • Memoria del computer
  • kernel Linux

Circa l'autore

Fatih Kuçükkarakurt (7 articoli pubblicati)

Un ingegnere e sviluppatore di software che è un fan della matematica e della tecnologia. Gli sono sempre piaciuti i computer, la matematica e la fisica. Ha sviluppato progetti di motori di gioco, machine learning, reti neurali artificiali e librerie di algebra lineare. Inoltre continua a lavorare su machine learning e matrici lineari.

Altro da Fatih Küçükkarakurt

Iscriviti alla nostra Newsletter

Iscriviti alla nostra newsletter per suggerimenti tecnici, recensioni, ebook gratuiti e offerte esclusive!

Clicca qui per iscriverti