addr = mmap(0, 3 * ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
Тогда следующие вызовы создадут нелинейные отображения, показанные на рис. 45.5:
remap_file_pages(addr, ps, 0, 2, 0);
/* Отображает страницу файла 0 на страницу памяти 2 */
remap_file_pages(addr + 2 * ps, ps, 0, 0, 0);
/* Отображает страницу файла 2 на страницу памяти 0 */
Есть еще два аргумента для вызова remap_file_pages(), которые не упоминались ранее.
• Аргумент prot игнорируется и должен быть равен 0. В будущем с его помощью можно будет менять способ защиты участка памяти, с которым работает вызов remap_file_pages(). В текущей реализации действуют те же параметры защиты, что и во всей области VMA.
Области VMA также применяются в виртуальных машинах и сборщиках мусора, у которых должна быть возможность защищать от записи отдельные страницы. Изначально вызов remap_file_pages() должен был уметь это делать, но пока данный механизм не реализован.
• Аргумент flags, который на текущий момент не используется.
Рис. 45.5.
В своей текущей реализации вызов remap_file_pages() способен работать только с разделяемыми отображениями (MAP_SHARED).
Системный вызов remap_file_pages() поддерживается только в Linux; он не предусмотрен стандартом SUSv3 и отсутствует в других UNIX-системах.
Системный вызов mmap() создает отображение в виртуальном адресном пространстве вызывающего процесса. Системный вызов munmap() выполняет обратную операцию, удаляя отображение из этого пространства.
Отображения бывают двух видов: файловые и анонимные. В первом случае в память отображается содержимое определенного участка файла. Анонимное отображение (созданное с помощью флага MAP_ANONYMOUS или путем отображения устройства /dev/zero) не связано ни с каким файлом; его содержимое заполнено нулями.
Отображения могут быть либо приватными (MAP_PRIVATE), либо разделяемыми (MAP_SHARED). Разница между ними определяет, будут ли видны изменения разделяемой памяти другим процессам; в случае с файловым отображением это влияет на то, будет ли ядро записывать изменения участка памяти обратно в исходный файл. Когда процесс отображает файл с помощью флага MAP_PRIVATE, изменения, которые он вносит в данное отображение, не видны другим процессам и не возвращаются в отображенный файл. Флаг MAP_SHARED обеспечивает противоположное поведение: изменения участка памяти попадают в исходный файл и видны другим процессам.
Ядро автоматически применяет изменения, внесенные к отображению типа MAP_SHARED, к исходному файлу, однако не дает никаких гарантий относительно времени выполнения таких операций. Приложение может задействовать системный вызов msync(), чтобы инициировать немедленную синхронизацию участка памяти с отображенным файлом.
Отображения в память используются для многих задач, среди которых:
• выделение памяти, доступной только одному процессу (приватные анонимные отображения);
• инициализация содержимого сегментов с кодом и данными процесса (приватные файловые отображения);
• разделение памяти между процессами, связанными вызовом fork() (разделяемые анонимные отображения);
• отображение ввода/вывода в память, возможно, в сочетании с разделением памяти между неродственными процессами (разделяемые файловые отображения).
При доступе к содержимому отображений можно столкнуться с двумя сигналами: SIGSEGV и SIGBUS. Первый генерируется при попытке доступа, нарушающей защиту отображения (или если мы обращаемся к адресу, который не был отображен). Второй возникает для файловых отображений при обращении к участку, не связанному с соответствующим участком файла (то есть когда отображение больше, чем его исходный файл).
Перерасход пространства подкачки позволяет системе выделять для процессов память, объем которой превышает размер доступного физического и виртуального пространства. Такой подход возможен по той причине, что процессы обычно не задействуют всю память, выделенную им. Перерасходом можно управлять как на уровне вызова mmap(), используя флаг MAP_NORESERVE, так и на общесистемном уровне с помощью файлов /proc.
Системный вызов mremap() позволяет изменять размер существующих отображений. Для создания нелинейных файловых отображений можно применять системный вызов remap_file_pages().
Сведения о реализации вызова mmap() в Linux можно найти в [Bovet & Cesati, 2005]. Подробности реализации mmap() в других UNIX-системах доступны в таких источниках: [McKusick et al., 1996] (BSD), [Goodheart & Cox, 1994] и [Vahalia, 1996] (System V Release 4).
45.1. Напишите программу, аналогичную команде cp(1), применяющей вызовы mmap() и memcpy() (вместо read() или write()) для копирования файла из одного места в другое. (Используйте вызов fstat(), чтобы получить размер заданного файла, который затем можно будет указать при создании отображения в память; для задания размера итогового файла подойдет вызов ftruncate().)