Chargement de bibliothèques partagées et utilisation de la RAM

41

Je me demande comment Linux gère les bibliothèques partagées. (En fait, je parle de Maemo Fremantle, une distribution basée sur Debian publiée en 2009 et fonctionnant sur 256 Mo de RAM).

Supposons que nous avons deux exécutables liant à libQtCore.so.4 et utilisant ses symboles (utilisant ses classes et ses fonctions). Pour simplifier, appelons-les aet b. Nous supposons que les deux exécutables sont liés aux mêmes bibliothèques.

Nous lançons d’abord a. La bibliothèque doit être chargée. Est-il chargé en entier ou est-il chargé en mémoire uniquement dans la partie requise (comme nous n'utilisons pas chaque classe, seul le code relatif aux classes utilisées est en cours de chargement)?

Puis on se lance b. Nous supposons que cela afonctionne toujours. bliens vers libQtCore.so.4 également et utilise certaines des classes qui autilisent, mais aussi certaines qui ne sont pas utilisées par a. La bibliothèque sera-t-elle chargée deux fois (séparément pour aet séparément pour b)? Ou utiliseront-ils le même objet déjà dans la RAM? Si bn'utilise pas de nouveaux symboles et aest déjà en cours d'exécution, la RAM utilisée par les bibliothèques partagées augmentera-t-elle? (Ou sera la différence insignifiante)

Marmistrz
la source

Réponses:

54

REMARQUE: je suppose que votre machine est équipée d'une unité de cartographie de mémoire (MMU). Il existe une version Linux (µClinux) qui n'exige pas de MMU, et cette réponse ne s'applique pas là.

Qu'est-ce qu'un MMU? C'est une partie matérielle du processeur et / ou du contrôleur de mémoire. Comprendre la liaison de bibliothèque partagée ne vous oblige pas à comprendre exactement le fonctionnement d’une MMU, c’est qu’une telle unité permet de faire la différence entre les adresses de mémoire logiques (celles utilisées par les programmes) et les adresses physiques.adresses mémoire (celles réellement présentes sur le bus mémoire). La mémoire est divisée en pages, généralement de 4 Ko sous Linux. Avec 4k pages, les adresses logiques 0 à 4095 sont la page 0, les adresses logiques 4096 à 8191 sont la page 1, etc. La MMU les mappe sur des pages physiques de RAM et chaque page logique peut être mappée sur 0 ou 1 pages physiques. Une page physique donnée peut correspondre à plusieurs pages logiques (c’est ainsi que la mémoire est partagée: plusieurs pages logiques correspondent à la même page physique). Notez que cela s'applique quel que soit le système d'exploitation. c'est une description du matériel.

Au changement de processus, le noyau modifie les mappages de pages MMU, de sorte que chaque processus dispose de son propre espace. L'adresse 4096 dans le processus 1000 peut être (et est généralement) complètement différente de l'adresse 4096 dans le processus 1001.

Presque chaque fois que vous voyez une adresse, c’est une adresse logique. Les programmes d’espace utilisateur ne traitent presque jamais d’adresses physiques.

Maintenant, il y a aussi plusieurs façons de construire des bibliothèques. Supposons qu'un programme appelle la fonction foo()dans la bibliothèque. La CPU ne sait rien des symboles, ni des appels de fonction. Elle sait comment accéder à une adresse logique et exécuter le code trouvé. Cela peut être fait de différentes manières (et des choses similaires s'appliquent lorsqu'une bibliothèque accède à ses propres données globales, etc.):

  1. Il pourrait coder en dur une adresse logique pour l'appeler. Cela nécessite que la bibliothèque soit toujours chargée à la même adresse logique. Si deux bibliothèques nécessitent la même adresse, la liaison dynamique échoue et vous ne pouvez pas lancer le programme. Les bibliothèques peuvent nécessiter d'autres bibliothèques. Par conséquent, chaque bibliothèque du système doit posséder des adresses logiques uniques. C'est très rapide, cependant, si cela fonctionne. (Voici comment a.out a fait les choses, et le genre de configuration que fait la liaison préliminaire, en quelque sorte).
  2. Il pourrait coder en dur une fausse adresse logique et demander à l'éditeur de liens dynamique de modifier celle qui convient lors du chargement de la bibliothèque. Cela demande beaucoup de temps lors du chargement des bibliothèques, mais après cela, c'est très rapide.
  3. Cela pourrait ajouter une couche d'indirection: utilisez un registre de la CPU pour contenir l'adresse logique à laquelle la bibliothèque est chargée, puis accédez à tout comme un décalage par rapport à ce registre. Ceci impose un coût de performance sur chaque accès.

Pratiquement personne n’utilise plus le logiciel n ° 1, du moins pas sur les systèmes polyvalents. Conserver cette liste d'adresses logiques unique est impossible sur les systèmes 32 bits (il n'y en a pas assez) et un cauchemar administratif sur les systèmes 64 bits. Une sorte de pré-liaison fait cela, cependant, système par système.

Que N ° 2 ou N ° 3 soit utilisé dépend de si la bibliothèque a été construite avec l' -fPICoption de GCC (code indépendant de la position). Le n ° 2 est sans, le n ° 3 est avec. Généralement, les bibliothèques sont construites avec -fPIC, donc # 3 est ce qui se passe.

Pour plus de détails, voir Comment écrire des bibliothèques partagées (PDF) de Ulrich Drepper .

Donc, enfin, on peut répondre à votre question:

  1. Si la bibliothèque est construite avec -fPIC (comme il se doit presque certainement), la grande majorité des pages sont exactement les mêmes pour chaque processus qui les charge. Vos processus aet brisquent de charger la bibliothèque à différentes adresses logiques, mais celles-ci pointeront sur les mêmes pages physiques: la mémoire sera partagée. De plus, les données dans la RAM correspondent exactement à celles du disque, elles ne peuvent donc être chargées que lorsque le gestionnaire de défauts de page en a besoin.
  2. Si la bibliothèque est construite sans -fPIC , il s'avère que la plupart des pages de la bibliothèque nécessiteront des modifications de liens et seront différentes. Par conséquent, elles doivent être des pages physiques séparées (car elles contiennent des données différentes). Cela signifie qu'ils ne sont pas partagés. Les pages ne correspondent pas à ce qui est sur le disque, donc je ne serais pas surpris si toute la bibliothèque est chargée. Il peut bien entendu être ultérieurement échangé sur disque (dans le fichier d'échange).

Vous pouvez examiner cela avec l' pmapoutil ou directement en archivant divers fichiers /proc. Par exemple, voici une sortie (partielle) de pmap -xdeux s différents nouvellement créés bc. Notez que les adresses indiquées par pmap sont généralement des adresses logiques:

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

Vous pouvez voir que la bibliothèque est chargée en plusieurs parties et pmap -xvous donne des détails sur chacune d’elles séparément. Vous remarquerez que les adresses logiques sont différentes entre les deux processus. vous vous attendriez raisonnablement à ce qu'ils soient identiques (puisque le même programme est en cours d'exécution et que les ordinateurs sont généralement prévisibles), mais il existe une fonction de sécurité appelée randomisation de la disposition de l'espace d'adressage qui les aléatoirement.

La différence de taille (kilo-octets) et de taille résidente (RSS) indique que le segment entier de la bibliothèque n'a pas été chargé. Enfin, vous pouvez voir que, pour les mappages plus grands, sale est 0, ce qui signifie qu'il correspond exactement à ce qui se trouve sur le disque.

Vous pouvez le relancer avec pmap -XX, et cela vous montrera - en fonction de la version du noyau que vous exécutez, car la sortie -XX varie selon la version du noyau - que le premier mappage a un Shared_Clean176, ce qui correspond exactement à RSS. Sharedmémoire signifie que les pages physiques sont partagées par plusieurs processus et, étant donné que cela correspond au RSS, cela signifie que toute la bibliothèque en mémoire est partagée (consultez la section Voir aussi ci-dessous pour plus d'explications sur les domaines partagé et privé):

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


Voir également

derobert
la source
Cela signifie que la pré-liaison n’est plus utile (et que l’ -fPICutilisation a complètement changé il ya quelque temps)?
Hauke ​​Laging
@crisron Merci pour les corrections. Pour votre information, Markdown comptera pour vous - le rendu de mes répétitions 1. était correct. De plus, j'ai apporté quelques modifications à ce que vous avez fait - "adresse de départ" est un jargon technique, j'ai probablement causé de la confusion en mettant "logique" au milieu. Je l'ai changé pour me débarrasser du jargon. De plus, les pages sont équivalentes à ces adresses. Pour autant que je sache, ces adresses ne peuvent jamais être une page différente. J'ai essayé à nouveau, en échangeant la commande, j'espère que c'est plus clair.
derobert
zut, maintenant c'est une réponse !!!
Evan Carroll