ПРИМЕЧАНИЕ. Я предполагаю, что на вашей машине установлен модуль отображения памяти (MMU). Существует версия для Linux (µClinux), для которой не требуется MMU, и этот ответ там не применим.
Что такое MMU? Это аппаратное обеспечение - часть процессора и / или контроллера памяти. Понимание связывания с общей библиотекой не требует от вас точного понимания того, как работает MMU, просто MMU позволяет различать адреса логической памяти (используемые программами) и физические адреса.адреса памяти (те, которые действительно присутствуют на шине памяти). Память разбита на страницы, обычно размером 4K в Linux. Для страниц 4k логические адреса 0–4095 - это страницы 0, логические адреса 4096–8191 - страницы 1 и т. Д. MMU отображает их на физические страницы ОЗУ, и каждая логическая страница обычно может быть отображена на 0 или 1 физических страницах. Данная физическая страница может соответствовать нескольким логическим страницам (так распределяется память: несколько логических страниц соответствуют одной и той же физической странице). Обратите внимание, что это относится независимо от ОС; это описание оборудования.
При переключении процессов ядро изменяет отображения страниц MMU, чтобы у каждого процесса было свое пространство. Адрес 4096 в процессе 1000 может быть (и обычно) полностью отличным от адреса 4096 в процессе 1001.
Практически всякий раз, когда вы видите адрес, это логический адрес. Пользовательские космические программы почти никогда не имеют дело с физическими адресами.
Теперь есть несколько способов создания библиотек. Допустим, программа вызывает функцию foo()
в библиотеке. Процессор ничего не знает о символах или вызовах функций на самом деле - он просто знает, как перейти к логическому адресу и выполнить любой код, который он там найдет. Это можно сделать несколькими способами (и аналогичные вещи применяются, когда библиотека обращается к своим глобальным данным и т. Д.):
- Он может жестко закодировать некоторый логический адрес для вызова. Это требует, чтобы библиотека всегда загружалась по одному и тому же логическому адресу. Если двум библиотекам требуется один и тот же адрес, динамическое связывание завершается ошибкой, и вы не можете запустить программу. Для библиотек могут потребоваться другие библиотеки, поэтому для каждой библиотеки в системе должны быть уникальные логические адреса. Это очень быстро, хотя, если это работает. (Это то, как все делалось, и тип настройки, которую выполняет предварительная ссылка, вроде).
- Он может жестко закодировать поддельный логический адрес и указать динамическому компоновщику редактировать правильный адрес при загрузке библиотеки. При загрузке библиотек это занимает немало времени, но после этого происходит очень быстро.
- Это может добавить слой косвенности: используйте регистр ЦП для хранения логического адреса, по которому загружена библиотека, а затем получите доступ ко всему как смещение от этого регистра. Это накладывает затраты производительности на каждый доступ.
Почти никто больше не использует # 1, по крайней мере, не в системах общего назначения. Сохранение этого уникального списка логических адресов невозможно в 32-разрядных системах (их недостаточно для обхода) и административный кошмар в 64-разрядных системах. Предварительное связывание делает это, однако, для каждой системы.
Будет ли использоваться # 2 или # 3, зависит от того, была ли библиотека построена с -fPIC
опцией GCC (позиционно-независимый код). № 2 без, № 3 с. Как правило, библиотеки построены с -fPIC
, так что # 3 это то, что происходит.
Для получения более подробной информации см. Ульрих Дреппер « Как писать общие библиотеки» (PDF) .
Итак, наконец, на ваш вопрос можно ответить:
- Если библиотека построена с
-fPIC
(как это почти наверняка должно быть), подавляющее большинство страниц одинаково для каждого процесса, который ее загружает. Ваши процессы a
и b
вполне могут загружать библиотеку по разным логическим адресам, но те будут указывать на одни и те же физические страницы: память будет распределена. Кроме того, данные в ОЗУ точно совпадают с данными на диске, поэтому они могут быть загружены только тогда, когда это необходимо обработчику ошибок страницы.
- Если библиотека построена без
-fPIC
, то оказывается, что большинство страниц библиотеки будут нуждаться в редактировании ссылок, и будут отличаться. Следовательно, они должны быть отдельными физическими страницами (поскольку они содержат разные данные). Это означает, что они не являются общими. Страницы не соответствуют тому, что находится на диске, поэтому я не удивлюсь, если вся библиотека загружена. Впоследствии он, конечно, может быть выгружен на диск (в файле подкачки).
Вы можете проверить это с помощью pmap
инструмента или непосредственно, проверив различные файлы в /proc
. Например, вот (частичный) вывод pmap -x
двух разных вновь порожденных bc
s. Обратите внимание, что адреса, отображаемые pmap, как правило, являются логическими адресами:
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
Вы можете видеть, что библиотека загружена несколькими частями и pmap -x
предоставляет подробные сведения о каждой из них в отдельности. Вы заметите, что логические адреса отличаются между двумя процессами; можно разумно ожидать, что они будут одинаковыми (поскольку выполняется одна и та же программа, а компьютеры обычно предсказуемы подобным образом), но есть функция безопасности, называемая рандомизацией расположения адресного пространства, которая намеренно рандомизирует их.
Из разницы в размерах (Кбайт) и резидентном размере (RSS) видно, что весь сегмент библиотеки не был загружен. Наконец, вы можете видеть, что для больших отображений грязное значение равно 0, что означает, что оно точно соответствует тому, что находится на диске.
Вы можете повторно запустить с pmap -XX
, и он покажет вам - в зависимости от используемой версии ядра, так как вывод -XX зависит от версии ядра - что первое отображение имеет значение Shared_Clean
176, что в точности соответствует RSS
. Shared
память означает, что физические страницы совместно используются несколькими процессами, и, поскольку она соответствует RSS, это означает, что вся библиотека, находящаяся в памяти, является общей (см. также раздел «См. также ниже» для более подробного объяснения общего и частного):
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
Смотрите также
-fPIC
использование некоторое время назад полностью изменилось)?1.
был правильным. Кроме того, я внес несколько изменений в то, что вы сделали: «начальный адрес» - это технический жаргон, я, вероятно, вызвал путаницу, поставив «логический» в середине. Я изменил его, чтобы избавиться от жаргона. Кроме того, страницы эквивалентны этим адресам, AFAIK невозможно, чтобы эти адреса когда-либо были другой страницей. Я попробовал еще раз, меняя порядок, надеюсь, это будет понятнее.