NOTA: Eu vou assumir que sua máquina possui uma unidade de mapeamento de memória (MMU). Existe uma versão do Linux (µClinux) que não requer uma MMU e esta resposta não se aplica a ela.
O que é uma MMU? É hardware - parte do processador e / ou controlador de memória. Para entender o link da biblioteca compartilhada, você não precisa entender exatamente como uma MMU funciona, apenas que uma MMU permite que haja uma diferença entre os endereços de memória lógica (os usados pelos programas) e os físicos.endereços de memória (aqueles realmente presentes no barramento de memória). A memória é dividida em páginas, geralmente com tamanho de 4K no Linux. Com páginas de 4k, os endereços lógicos 0 a 4095 são a página 0, os endereços lógicos 4096 a 8191 são a página 1 etc. A MMU os mapeia para páginas físicas da RAM, e cada página lógica pode ser tipicamente mapeada para 0 ou 1 páginas físicas. Uma determinada página física pode corresponder a várias páginas lógicas (é assim que a memória é compartilhada: várias páginas lógicas correspondem à mesma página física). Observe que isso se aplica independentemente do sistema operacional; é uma descrição do hardware.
Na troca de processo, o kernel altera os mapeamentos de página da MMU, para que cada processo tenha seu próprio espaço. O endereço 4096 no processo 1000 pode ser (e geralmente é) completamente diferente do endereço 4096 no processo 1001.
Praticamente sempre que você vê um endereço, é um endereço lógico. Os programas de espaço do usuário quase nunca lidam com endereços físicos.
Agora, existem várias maneiras de construir bibliotecas também. Digamos que um programa chama a função foo()
na biblioteca. A CPU não sabe nada sobre símbolos ou chamadas de função - apenas sabe como pular para um endereço lógico e executar o código que encontrar lá. Existem várias maneiras de fazer isso (e coisas semelhantes se aplicam quando uma biblioteca acessa seus próprios dados globais, etc.):
- Poderia codificar algum endereço lógico para chamá-lo. Isso requer que a biblioteca sempre seja carregada exatamente no mesmo endereço lógico. Se duas bibliotecas exigirem o mesmo endereço, a vinculação dinâmica falhará e você não poderá iniciar o programa. As bibliotecas podem exigir outras bibliotecas, portanto, basicamente, exige que todas as bibliotecas do sistema tenham endereços lógicos exclusivos. É muito rápido, no entanto, se funcionar. (Foi assim que a.out fez as coisas, e o tipo de configuração que a pré-ligação faz, mais ou menos).
- Ele pode codificar um endereço lógico falso e solicitar ao vinculador dinâmico que edite no endereço apropriado ao carregar a biblioteca. Isso custa um pouco de tempo ao carregar as bibliotecas, mas depois disso é muito rápido.
- Poderia adicionar uma camada de indireção: use um registro da CPU para manter o endereço lógico no qual a biblioteca está carregada e, em seguida, acesse tudo como um deslocamento desse registro. Isso impõe um custo de desempenho em cada acesso.
Praticamente ninguém mais usa o número 1, pelo menos não em sistemas de uso geral. Manter essa lista de endereços lógica exclusiva é impossível em sistemas de 32 bits (não há o suficiente para circular) e um pesadelo administrativo em sistemas de 64 bits. No entanto, a pré-vinculação faz isso por sistema.
O uso de # 2 ou # 3 depende se a biblioteca foi criada com a opção GCC -fPIC
(código independente de posição). # 2 está sem, # 3 está com. Geralmente, as bibliotecas são construídas com -fPIC
, então o número 3 é o que acontece.
Para obter mais detalhes, consulte Como escrever bibliotecas compartilhadas (PDF) , de Ulrich Drepper .
Então, finalmente, sua pergunta pode ser respondida:
- Se a biblioteca é construída com
-fPIC
(como quase certamente deveria ser), a grande maioria das páginas é exatamente a mesma para todos os processos que a carregam. Seus processos a
e b
pode muito bem carregar a biblioteca em endereços lógicos diferentes, mas aqueles irá apontar para as mesmas páginas físicas: a memória será compartilhada. Além disso, os dados na RAM correspondem exatamente ao que está no disco, para que possam ser carregados somente quando necessário pelo manipulador de falhas da página.
- Se a biblioteca for criada sem
-fPIC
, então a maioria das páginas da biblioteca precisará de edições de link e será diferente. Portanto, eles devem ser páginas físicas separadas (pois contêm dados diferentes). Isso significa que eles não são compartilhados. As páginas não correspondem ao que está no disco, portanto, não ficaria surpreso se a biblioteca inteira estiver carregada. Obviamente, pode ser posteriormente trocado para o disco (no arquivo de troca).
Você pode examinar isso com a pmap
ferramenta ou diretamente, verificando vários arquivos /proc
. Por exemplo, aqui está uma saída (parcial) de pmap -x
dois s diferentes recém-gerados bc
. Observe que os endereços mostrados pelo pmap são, como típicos, endereços lógicos:
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
Você pode ver que a biblioteca está carregada em várias partes e pmap -x
fornece detalhes sobre cada uma separadamente. Você notará que os endereços lógicos são diferentes entre os dois processos; você esperaria razoavelmente que eles sejam os mesmos (já que é o mesmo programa em execução e os computadores geralmente são previsíveis assim), mas existe um recurso de segurança chamado randomização de layout do espaço de endereço que os randomiza intencionalmente.
Você pode ver pela diferença no tamanho (Kbytes) e no tamanho residente (RSS) que o segmento inteiro da biblioteca não foi carregado. Finalmente, você pode ver que, para os mapeamentos maiores, sujo é 0, o que significa que corresponde exatamente ao que está no disco.
Você pode executar novamente pmap -XX
, e isso mostrará a você - dependendo da versão do kernel em execução, já que a saída -XX varia de acordo com a versão do kernel - que o primeiro mapeamento possui Shared_Clean
176, o que corresponde exatamente ao RSS
. Shared
memória significa que as páginas físicas são compartilhadas entre vários processos e, uma vez que corresponde ao RSS, significa que toda a biblioteca que está na memória é compartilhada (consulte o Veja também abaixo para obter mais explicações sobre compartilhado versus privado):
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
Veja também
-fPIC
uso mudou completamente há algum tempo)?