Управление своп-файлом

Для хранения образов модифицированных страниц система должна выделить какое-то пространство на диске. Для этого может использоваться как раздел диска, так и файл, место для которого выделяется наравне с файлами данных. Большинство современных систем может использовать как тот, так и другой методы, и поддерживает динамическое подключение и отключение своп-файлов и своп-разделов.
При наличии в системе нескольких дисков рекомендуется разделить своп-пространство между всеми (или хотя бы между менее используемыми) дисками. Дело в том, что операции чтения и записи требуют времени на позиционирование считывающей головки. Пока один накопитель передвигает головку, второй вполне может передавать данные. Понятно, что линейного роста производительности с увеличением числа дисков таким способом не получить: всегда есть вероятность, что все требуемые данные окажутся на одном диске, да еще и находятся в разных местах, но все равно выигрыш в большинстве случаев оказывается значительным.
В те времена, когда компьютеры были большими, для свопа часто использовались не диски, а специальные устройства, магнитные барабаны. В отличие от диска, который имеет одну или, реже, несколько головок чтения-записи, барабан имел по одной головке на каждую дорожку. Конечно, это повышало стоимость устройства, но полностью исключало потери времени на позиционирование (рис. 5.23).

Рис. 5.23. Магнитный диск и магнитный барабан

В своп-файл попадают только страницы, которые изменились с момента загрузки процесса. Если ОС использует абсолютную загрузку или позиии-онно-независимып код, исполняющийся код не отличается от своего образа в загрузочном файле, поэтому страницы кода вполне можно подкачивать оттуда, и нет никакой необходимости копировать их в своп. Часто при загрузке программы система помещает в память только страницу, на которую указывает стартовый адрес, а весь остальной используемый код и данные подгружаются механизмом страничного обмена.
При загрузке статически инициализированных данных обычно используется стратегия copy-on-write (копирование при модификации): первоначально страница подкачивается из файла. Если она не будет модифицирована и ее объявят жертвой, то при повторном обращении ее снова подгрузят из того же файла (рис. 5.24). Только если страница будет изменена, ей выделят место в своп-файле.

Рис. 5.24. Копирование при модификации

Если же используется относительная загрузка или та или иная форма сборки в момент загрузки (разделяемые библиотеки или DLL), при загрузке кода происходит перенастройка адресов. В этом случае возможны два подхода: копировать перенастроенный код в своп или производить перенастройку заново после каждого страничного отказа.

Отображение файлов в память в Unix
Системы семейства Unix предоставляют пользователям доступ к механизмам, используемым при загрузке программ, в виде системного вызова гол-ар. Этот вызов отображает участок файла в память. Отображение возможно в двух режимах: MAP_SHARED изменения в памяти отображаются в файле— таким образом mmap можно использовать для реализации разделяемой памяти и MAP_PRIVATE (соответственно, изменения памяти в файле не отображаются — при этом измененные страницы копируются в своп-файл).
Широко используется выделение памяти при помощи отображения псевдофайла /dev/zero (файл бесконечной длины, состоящий из одних нулей) в память в режиме MAP_PRIVATE.

Даже когда место под своп-файл выделяется динамически, система обычно предоставляет возможность ограничивать его рост. У интерактивных систем
при приближении к границе емкости своп-файла система часто начинает выдавать предупреждения пользователю. Однако основным видом реакции на переполнение или превышение лимитов роста своп-файла является отказ выделять память прикладным программам. Поэтому грамотно написанные программы всегда должны проверять, нормально ли завершился запрос на выделение памяти, и по возможности разумно обрабатывать ненормальное завершение. Это нужно не только в том случае, когда программа будет переноситься в систему без виртуатьной памяти, но и во вполне штатной (хотя и относительно редкой) ситуации переполнения свопа.
Иногда, впрочем, система может выделять память (точнее, не память, а только
адресное пространство) программам без оглядки на то, сколько есть свободного свопа. Эта довольно опасная стратегия, называемая overcommit, на первый взгляд кажется бессмысленной или полезной только в очень специальных случаях, например при использовании разреженных массивов. В действительности эта стратегия оправдана и тогда, когда мы можем быть уверены, что большинство выделенных процессу страниц никогда не будут использованы, например, при широком применении стратегии copy-on-write.

Overcommit в Unix
В системах семейства Unix копирование при записи применяется не только при загрузке сегментов данных программ и отображений файлов в память, но и при создании задач. Системный вызов fork (подробнее обсуждался в главе 3) создает полную копию адресного пространства процесса, выполнившего этот вызов. Физического копирования, естественно, не происходит. Вместо этого система отображает память родительского процесса в адресное пространство потомка и устанавливает защиту от записи на все страницы обеих задач. Когда какая-то из задач пытается осуществить запись, соответствующая страница физически копируется и запись осуществляется уже в копию. Большинство порожденных задач исполняют системный вызов exec вскоре после создания, изменив лишь несколько переменных в своем адресном пространстве. При таком стиле работы с памятью, действительно, многие выделяемые страницы не используются никогда, а большинство из используемых только прочитываются, поэтому overcommit является стандартной стратегией выделения памяти в Unix.