GNU/Linux >> Linux Esercitazione >  >> Linux

Caricamento di librerie condivise e utilizzo della RAM?

Mi chiedo come Linux gestisce le librerie condivise. (in realtà sto parlando di Maemo Fremantle, una distro basata su Debian rilasciata nel 2009 con 256 MB di RAM).

Supponiamo di avere due eseguibili che si collegano a libQtCore.so.4 e che usano i suoi simboli (usando le sue classi e funzioni). Per semplicità chiamiamola a e b . Assumiamo che entrambi gli eseguibili siano collegati alle stesse librerie.

Per prima cosa lanciamo a . La libreria deve essere caricata. Viene caricato per intero o viene caricato in memoria solo nella parte richiesta (poiché non utilizziamo ogni classe, viene caricato solo il codice relativo alle classi utilizzate)?

Quindi lanciamo b . Assumiamo che a è ancora in esecuzione. b si collega anche a libQtCore.so.4 e utilizza alcune delle classi che a usi, ma anche alcuni non utilizzati da a . La libreria verrà caricata due volte (separatamente per a e separatamente per b )? Oppure utilizzeranno lo stesso oggetto già nella RAM. Se b non usa nuovi simboli e a è già in esecuzione aumenterà la RAM utilizzata dalle librerie condivise? (O la differenza sarà insignificante)

Risposta accettata:

NOTA:suppongo che la tua macchina abbia un'unità di mappatura della memoria (MMU). Esiste una versione Linux (µClinux) che non richiede una MMU e questa risposta non si applica lì.

Cos'è una MMU? È hardware, parte del processore e/o del controller di memoria. Comprendere il collegamento di librerie condivise non richiede di capire esattamente come funziona una MMU, solo che una MMU consente che ci sia una differenza tra logico indirizzi di memoria (quelli usati dai programmi) e fisici indirizzi di memoria (quelli effettivamente presenti sul bus di memoria). La memoria è suddivisa in pagine, in genere di dimensioni 4K su Linux. Con 4k pagine, gli indirizzi logici 0–4095 sono la pagina 0, gli indirizzi logici 4096–8191 sono la pagina 1, ecc. La MMU li mappa su pagine fisiche della RAM e ogni pagina logica può essere tipicamente mappata su 0 o 1 pagine fisiche. Una determinata pagina fisica può corrispondere a più pagine logiche (così si condivide la memoria:più pagine logiche corrispondono alla stessa pagina fisica). Nota che questo vale indipendentemente dal sistema operativo; è una descrizione dell'hardware.

Al cambio di processo, il kernel cambia le mappature delle pagine MMU, in modo che ogni processo abbia il proprio spazio. L'indirizzo 4096 nel processo 1000 può essere (e di solito è) completamente diverso dall'indirizzo 4096 nel processo 1001.

Praticamente ogni volta che vedi un indirizzo, è un indirizzo logico. I programmi User Space non si occupano quasi mai di indirizzi fisici.

Ora, ci sono anche diversi modi per creare librerie. Diciamo che un programma chiama la funzione foo() nella biblioteca. La CPU non sa nulla di simboli o chiamate di funzioni in realtà:sa solo come saltare a un indirizzo logico ed eseguire qualsiasi codice trovi lì. Ci sono un paio di modi per farlo (e cose simili si applicano quando una biblioteca accede ai propri dati globali, ecc.):

  1. Potrebbe codificare un indirizzo logico a cui chiamarlo. Ciò richiede che la libreria sempre essere caricato allo stesso identico indirizzo logico. Se due librerie richiedono lo stesso indirizzo, il collegamento dinamico non riesce e non è possibile avviare il programma. Le librerie possono richiedere altre librerie, quindi ciò richiede fondamentalmente che ogni libreria sul sistema abbia indirizzi logici univoci. È molto veloce, però, se funziona. (Questo è il modo in cui a.out ha fatto le cose, e il tipo di impostazione che fa il prelinking, in qualche modo).
  2. Potrebbe codificare un indirizzo logico falso e dire al linker dinamico di modificare quello corretto durante il caricamento della libreria. Questo costa un bel po' di tempo durante il caricamento delle librerie, ma dopo è molto veloce.
  3. Potrebbe aggiungere un livello di indirizzamento:utilizzare un registro della CPU per contenere l'indirizzo logico a cui è caricata la libreria, quindi accedere a tutto come offset da quel registro. Ciò impone un costo di prestazioni su ogni accesso.
Correlati:Linux – cp perde i metadati del file?

Praticamente nessuno usa più il numero 1, almeno non su sistemi generici. Mantenere quell'elenco di indirizzi logici univoci è impossibile sui sistemi a 32 bit (non ce ne sono abbastanza per andare in giro) e un incubo amministrativo sui sistemi a 64 bit. Tuttavia, il pre-collegamento lo fa in base al sistema.

L'utilizzo di #2 o #3 dipende dal fatto che la libreria sia stata creata con -fPIC di GCC (codice indipendente dalla posizione). #2 è senza, #3 è con. In genere, le librerie vengono create con -fPIC , quindi #3 è ciò che accade.

Per maggiori dettagli, vedere How to Write Shared Libraries (PDF) di Ulrich Drepper.

Quindi, finalmente, puoi rispondere alla tua domanda:

  1. Se la libreria è stata creata con -fPIC (come quasi certamente dovrebbe essere), la stragrande maggioranza delle pagine è esattamente la stessa per ogni processo che la carica. I tuoi processi a e b potrebbe anche caricare la libreria a indirizzi logici diversi, ma quelli punteranno alle stesse pagine fisiche:la memoria sarà condivisa. Inoltre, i dati nella RAM corrispondono esattamente a quelli presenti sul disco, quindi possono essere caricati solo quando necessario dal gestore degli errori di pagina.
  2. Se la libreria è stata creata senza -fPIC , quindi risulta che la maggior parte delle pagine della libreria avrà bisogno di modifiche ai collegamenti e sarà diversa. Pertanto, devono essere pagine fisiche separate (in quanto contengono dati diversi). Ciò significa che non sono condivisi. Le pagine non corrispondono a ciò che è sul disco, quindi non sarei sorpreso se l'intera libreria fosse caricata. Ovviamente può essere successivamente scambiato su disco (nel file di scambio).

Puoi esaminarlo con la pmap strumento o direttamente controllando vari file in /proc . Ad esempio, ecco un output (parziale) di pmap -x su due diversi bc appena generati S. Si noti che gli indirizzi mostrati da pmap sono, come tipici, indirizzi logici:

pmap -x 14739
Address           Kbytes     RSS   Dirty Mode  Mapping
00007f81803ac000     244     176       0 r-x-- libreadline.so.6.2
00007f81803e9000    2048       0       0 ----- libreadline.so.6.2
00007f81805e9000       8       8       8 r---- libreadline.so.6.2
00007f81805eb000      24      24      24 rw--- libreadline.so.6.2


pmap -x 17739
Address           Kbytes     RSS   Dirty Mode  Mapping
00007f784dc77000     244     176       0 r-x-- libreadline.so.6.2
00007f784dcb4000    2048       0       0 ----- libreadline.so.6.2
00007f784deb4000       8       8       8 r---- libreadline.so.6.2
00007f784deb6000      24      24      24 rw--- libreadline.so.6.2

Puoi vedere che la libreria è caricata in più parti e pmap -x ti dà i dettagli su ciascuno separatamente. Noterai che gli indirizzi logici sono diversi tra i due processi; ti aspetteresti ragionevolmente che siano gli stessi (dal momento che è lo stesso programma in esecuzione e i computer di solito sono prevedibili in questo modo), ma esiste una funzione di sicurezza chiamata randomizzazione del layout dello spazio degli indirizzi che li casualizza intenzionalmente.

È possibile vedere dalla differenza di dimensione (Kbyte) e dimensione residente (RSS) che l'intero segmento della libreria non è stato caricato. Infine, puoi vedere che per le mappature più grandi, dirty è 0, il che significa che corrisponde esattamente a ciò che è sul disco.

Puoi eseguire nuovamente con pmap -XX e ti mostrerà, a seconda della versione del kernel in esecuzione, poiché l'output -XX varia in base alla versione del kernel, che la prima mappatura ha un Shared_Clean di 176, che corrisponde esattamente al RSS . Shared memoria significa che le pagine fisiche sono condivise tra più processi e, poiché corrisponde all'RSS, significa che tutta la libreria che è in memoria è condivisa (guarda anche sotto per ulteriori spiegazioni su condivisi e privati):

pmap -XX 17739
         Address Perm   Offset Device   Inode  Size  Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous AnonHugePages Swap KernelPageSize MMUPageSize Locked                   VmFlagsMapping
    7f784dc77000 r-xp 00000000  fd:00 1837043   244  176  19          176            0             0             0        176         0             0    0              4           4      0       rd ex mr mw me sd  libreadline.so.6.2
    7f784dcb4000 ---p 0003d000  fd:00 1837043  2048    0   0            0            0             0             0          0         0             0    0              4           4      0             mr mw me sd  libreadline.so.6.2
    7f784deb4000 r--p 0003d000  fd:00 1837043     8    8   8            0            0             0             8          8         8             0    0              4           4      0       rd mr mw me ac sd  libreadline.so.6.2
    7f784deb6000 rw-p 0003f000  fd:00 1837043    24   24  24            0            0             0            24         24        24             0    0              4           4      0    rd wr mr mw me ac sd  libreadline.so.6.2

Vedi anche

  • Ottenere informazioni sull'utilizzo della memoria di un processo da /proc/pid/smaps per una spiegazione dell'intera cosa pulita/sporca condivisa/privata.
Relazionato:Linux – Come ottenere sar da mostrare per il giorno precedente?
Linux
  1. Misurare l'utilizzo della RAM di un programma?

  2. Convertire una libreria statica in una libreria condivisa?

  3. Rimozione delle librerie condivise di Linux

  4. Come incorporare le informazioni sulla versione nella libreria condivisa e nel binario?

  5. libstdc++.so.5:impossibile aprire il file oggetto condiviso, ma la libreria è installata e aggiornata

Libreria condivisa Jenkins:come creare, configurare e utilizzare

Controlla l'utilizzo di RAM e CPU da parte di Kodi in tempo reale

Come inizializzare una libreria condivisa su Linux

Errore durante il caricamento della libreria condivisa (glew)

Ubuntu riconosce l'eseguibile come libreria condivisa e non lo eseguirà facendo clic

conda.exe:errore durante il caricamento delle librerie condivise:libz.so.1