Считают ли нулевые страницы mmap / mprotect-readonly только выделенную память? - PullRequest
8 голосов
/ 01 декабря 2010

Я хочу сохранить виртуальное адресное пространство в моем процессе зарезервированным для памяти, которая ранее использовалась, но в настоящее время не требуется. Меня интересует ситуация, когда ядром хоста является Linux, и оно настроено на предотвращение чрезмерной загрузки (что происходит путем подробного учета всей выделенной памяти).

Если я просто хочу, чтобы данные, которые мое приложение больше не использует, не занимали физической памяти или не переставлялись на диск (тратя ресурсы в любом случае), я могу madvise ядро, которое ему не нужно, или mmap new ноль страниц поверх него. Но ни один из этих подходов не обязательно уменьшит объем памяти, который считается зарезервированным, который затем запрещается использовать другим процессам.

Что, если я заменим страницы новыми нулевыми страницами, помеченными только для чтения? Мое намерение состоит в том, чтобы они не учитывались в фиксированной памяти, и, кроме того, позже я могу использовать mprotect, чтобы сделать их доступными для записи, и что произойдет сбой, если сделать их доступными для записи превысит предел выделенной памяти. Правильно ли мое понимание? Будет ли это работать?

Ответы [ 2 ]

1 голос
/ 10 февраля 2011

В Linux, предполагая, что overcommit не отключен, вы можете использовать флаг MAP_NORESERVE для mmap, что гарантирует, что соответствующая страница не будет считаться выделенной памятью до доступа. Если overcommit полностью отключен, см. Ниже о страницах с несколькими сопоставлениями.

Обратите внимание, что в прошлом поведение Linux для нулевых страниц менялось; в некоторых версиях ядра простое чтение страницы приведет к ее выделению. С другими, запись необходима. Обратите внимание, что флаги защиты не вызывают выделение напрямую; однако они могут помешать вам случайно инициировать распределение. Поэтому для получения наиболее надежных результатов вам вообще следует избегать доступа к странице, набрав mprotect с PROT_NONE.

В качестве другого, более портативного варианта, вы можете сопоставить одну и ту же страницу в нескольких местах. То есть создайте и откройте пустой временный файл, отсоедините его, ftruncate от некоторого разумного количества страниц, затем несколько раз mmap со смещением 0 в файле. Это абсолютно гарантирует, что память будет считаться только один раз от использования памяти вашей программой. Вы даже можете использовать MAP_PRIVATE для автоматического перераспределения при записи на страницу.

Однако это может потреблять больше памяти, чем метод MAP_NORESERVE (как для данных отслеживания ядра, так и для страниц самого временного файла), поэтому я рекомендую вместо этого использовать MAP_NORESERVE, когда он доступен. Если вы используете эту технику, попробуйте сделать область сопоставления достаточно большой (и укажите ее в /dev/shm, если в Linux, чтобы избежать реального дискового ввода-вывода). Каждый отдельный mmap вызов потребляет определенное количество (не поддающейся замене) памяти ядра для его отслеживания, поэтому хорошо бы уменьшить этот отсчет.

1 голос
/ 10 февраля 2011

Если вы не используете страницу (для чтения или записи на нее), она не будет передана в ваше адресное пространство (только зарезервировано).

Но ваше адресное пространство ограничено, поэтому вы можетеНе играйте как хотите / нравится с ним.

См., например, ElectricFence, которая может не работать из-за большого количества выделений из-за вставки «nul page / guard page» (анонимная память без доступа).Посмотрите на этот поток: «mprotect () не удалось: невозможно выделить память»: http://thread.gmane.org/gmane.comp.lib.glibc.user/538/focus=976052

...