Учитываются ли нулевые страницы 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