NOTA: suppongo che la macchina disponga di un'unità di mappatura della memoria (MMU). Esiste una versione di Linux (µClinux) che non richiede una MMU e questa risposta non si applica qui.
Che cos'è un MMU? È hardware, parte del processore e / o controller di memoria. Comprendere il collegamento di librerie condivise non richiede di capire esattamente come funziona un MMU, ma solo che un MMU consente di distinguere gli indirizzi di memoria logica (quelli utilizzati dai programmi) e quelli fisiciindirizzi 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 esegue il mapping a pagine fisiche di RAM e ogni pagina logica può in genere essere mappata su 0 o 1 pagine fisiche. Una determinata pagina fisica può corrispondere a più pagine logiche (è così che la memoria è condivisa: più pagine logiche corrispondono alla stessa pagina fisica). Nota che questo vale indipendentemente dal sistema operativo; è una descrizione dell'hardware.
Al passaggio al processo, il kernel modifica i mapping della pagina MMU, in modo che ogni processo abbia il suo 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 di spazio utente quasi mai gestiscono indirizzi fisici.
Ora, ci sono anche diversi modi per creare librerie. Diciamo che un programma chiama la funzione foo()
nella libreria. La CPU non sa davvero nulla dei simboli o delle chiamate di funzione, sa solo come saltare a un indirizzo logico ed eseguire qualsiasi codice che trovi lì. Ci sono un paio di modi per farlo (e cose simili si applicano quando una libreria accede ai propri dati globali, ecc.):
- Potrebbe codificare un indirizzo logico per chiamarlo. Ciò richiede che la libreria sia sempre caricata nello stesso indirizzo logico esatto. 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 in pratica ciò richiede che tutte le librerie sul sistema dispongano di indirizzi logici univoci. È molto veloce, tuttavia, se funziona. (Ecco come hanno fatto le cose, e il tipo di installazione che fa il prelinking, in un certo senso).
- Potrebbe codificare un indirizzo logico falso e dire al linker dinamico di modificare quello corretto durante il caricamento della libreria. Questo richiede un bel po 'di tempo quando si caricano le librerie, ma dopo è molto veloce.
- Potrebbe aggiungere un livello di riferimento indiretto: utilizzare un registro CPU per contenere l'indirizzo logico in cui è caricata la libreria, quindi accedere a tutto come offset da quel registro. Ciò impone un costo prestazionale su ciascun accesso.
Praticamente nessuno usa più il numero 1, almeno non su sistemi di uso generale. Mantenere quell'elenco di indirizzi logici univoco è impossibile sui sistemi a 32 bit (non ce ne sono abbastanza per andare in giro) e un incubo amministrativo sui sistemi a 64 bit. Una sorta di pre-collegamento lo fa, tuttavia, per ogni sistema.
L'utilizzo del n. 2 o del n. 3 dipende dal fatto che la libreria sia stata creata con l' -fPIC
opzione GCC (codice indipendente dalla posizione). # 2 è senza, # 3 è con. Generalmente, le librerie sono costruite con -fPIC
, quindi # 3 è ciò che accade.
Per maggiori dettagli, vedere Come scrivere librerie condivise di Ulrich Drepper (PDF) .
Quindi, finalmente, puoi rispondere alla tua domanda:
- Se la libreria è costruita 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
potrebbero anche caricare la libreria su indirizzi logici diversi, ma quelli punteranno alle stesse pagine fisiche: la memoria sarà condivisa. Inoltre, i dati nella RAM corrispondono esattamente a ciò che è sul disco, quindi possono essere caricati solo quando necessario dal gestore degli errori di pagina.
- Se la libreria è costruita senza
-fPIC
, allora 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. Naturalmente può successivamente essere scambiato su disco (nel file di scambio).
Puoi esaminarlo con lo pmap
strumento o direttamente controllando vari file in /proc
. Ad esempio, ecco un output (parziale) di pmap -x
due diversi bc
s appena generati . Si noti che gli indirizzi mostrati da pmap sono, come tipico, 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 fornisce dettagli su ciascuna separatamente. Noterai che gli indirizzi logici sono diversi tra i due processi; ti aspetteresti ragionevolmente che siano uguali (dato che è lo stesso programma in esecuzione e i computer sono generalmente prevedibili in quel modo), ma esiste una funzionalità di sicurezza chiamata randomizzazione del layout dello spazio degli indirizzi che li randomizza intenzionalmente.
Dalla differenza di dimensioni (Kbyte) e dimensioni residenti (RSS) è possibile notare che l'intero segmento della libreria non è stato caricato. Infine, puoi vedere che per i mapping più grandi, dirty è 0, il che significa che corrisponde esattamente a ciò che è sul disco.
Puoi rieseguire pmap -XX
, e ti mostrerà - a seconda della versione del kernel che stai eseguendo, poiché l'output -XX varia in base alla versione del kernel - che il primo mapping ha un Shared_Clean
176, che corrisponde esattamente al RSS
. Shared
memoria significa che le pagine fisiche sono condivise tra più processi e, poiché corrisponde all'RSS, ciò significa che tutta la libreria in memoria è condivisa (vedere Vedi anche sotto per ulteriori spiegazioni su condiviso vs. privato):
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
Guarda anche
-fPIC
utilizzo è completamente cambiato qualche tempo fa)?