Предположительно, вы понимаете таблицы страниц и семантику копирования при записи.
Предположим, вы запускаете исполняемый файл a.out
, который инициализирует некоторые глобальные данные, а затем fork()
s. У вас не должно возникнуть проблем с пониманием того, что все доступные только для чтения (например, кодовые) страницы a.out
теперь совместно используются двумя процессами ( точно такие же страницы физической памяти mmap
записываются в обе виртуальные памяти пространства).
Теперь предположим, что a.out
также использовал libc.so.6
до fork
ing. У вас не должно возникнуть проблем с пониманием того, что страницы только для чтения, принадлежащие libc.so.6
, также распределяются между процессами точно таким же образом.
Теперь предположим, что у вас есть два отдельных исполняемых файла a.out
и b.out
, каждый из которых использует libc.so.6
. Предположим, что a.out запускается первым. Динамический загрузчик выполнит сопоставление только для чтения libc.so.6
в a.out
пространство виртуальной памяти, и теперь некоторые его страницы находятся в физической памяти. В этот момент запускается b.out
, и динамический загрузчик mmap
возвращает те же libc.so.6
страницы в свою виртуальную память. Поскольку ядро уже имеет сопоставление для этих страниц, у ядра нет причин создавать новые физические страницы для хранения сопоставления - оно может повторно использовать ранее отображенные физические страницы. Конечный результат такой же, как для двоичного файла fork
- одни и те же физические страницы совместно используются между несколькими пространствами виртуальной памяти (и несколькими процессами).
Так как два процесса могут иметь разные копии глобальной переменной,
Очень просто: сопоставления для чтения и записи (которые необходимы для доступных для записи данных) не совместно используются процессами (так что один процесс может записывать в переменную, и эта запись не будет виден другому процессу).