мне было интересно, является ли это прямым следствием копирования при записи принцип или не
Нет, это не. FWIW, вы могли бы разделять сегменты кода без COW, и вы могли бы иметь COW без разделяемых сегментов кода. Это независимо.
Если общий код программы должен был быть достигнут вследствие COW, то от этого могут выиграть только связанные процессы.
Например, если процесс A
вилок дважды и создает процессы B
и C
, а затем B
и C
вызов один из семи exec
функций на двоичный код, то вы могли сказать, что сегмент кода разделяется из-за COW - так как сегмент кода никогда не записывается во время выполнения и отображается только для чтения, то он должен быть автоматически общим, не так ли?
Что делать, если вы запускаете тот же исполняемый файл из другой оболочки? (Или какие-то другие несвязанные процессы вилки и выполняет одну и ту же программу? Это не обязательно должна быть оболочка ...)
Если разделение сегмента кода было следствием COW, в этом случае мы не получили бы преимущества от совместного использования сегмент кода, потому что процессы не связаны между собой (поэтому для начала используются не общие страницы COW с другими экземплярами).
Вместо этого сегмент кода используется совместно с файлами с отображением памяти. При загрузке нового исполняемого файла в память вызывается mmap(2)
для отображения содержимого двоичного файла в память.
и если это не так, то, что это процесс, который гарантирует, что никакие ненужные копии кода программы не находятся в памяти?
Точные детали реализации зависят от операционной системы, но это не так сложно. Концептуально mmap(2)
отображает файлы в память, поэтому вам нужно просто сохранить некоторое состояние в представлении основного файла, чтобы отслеживать, какие (если есть) отображения памяти активны для этого файла. Такая информация обычно хранится в файле inode.
Linux, например, связывает файлы с адресными пространствами памяти с полем i_mapping
struct inode
. Итак, когда mmap(2)
вызывается в двоичном формате в первый раз, страницы физической памяти назначаются для хранения информации, и устанавливается поле i_mapping
этого inode файла; более поздние вызовы будут использовать поле i_mapping
и поймут, что существует такое адресное пространство, связанное с этим inode, и потому что оно доступно только для чтения, а физические страницы не выделяются, поэтому все заканчивается совместным использованием. Обратите внимание, что виртуальная память может отличаться в каждом процессе, хотя она относится к одной и той же физической странице (что означает, что ядро будет по меньшей мере распределять и обновлять таблицы страниц каждого процесса, но это о нем).
The inode
structure is defined in fs.h
- Я могу только догадываться, что другие варианты UNIX делают это аналогичным образом.
Конечно, это все работает до тех пор, пока используется один и тот же двоичный файл. Если вы копируете двоичный файл и выполняете обе копии отдельно, по понятным причинам сегмент кода не будет использоваться совместно.