Учитываются ли нулевые страницы mmap/mprotect-readonly для выделенной памяти?

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

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

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

2 ответа

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

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

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

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

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

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

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

Другие вопросы по тегам