Файлы отображения памяти Linux резервируют много физической памяти

У меня есть проблема, которая была описана в нескольких потоках, касающихся отображения памяти и растущего потребления памяти под Linux.

Когда я открываю файл размером 1 ГБ в Linux или MacOS X и сопоставляю его с памятью, используя

me.data_begin = mmap(NULL, capacity(me), prot, MAP_SHARED, me.file.handle, 0);

и последовательно читать сопоставленную память, моя программа использует все больше и больше физической памяти, хотя я использовал posix_madvise (даже вызывал ее несколько раз в процессе чтения):

posix_madvise(me.data_begin, capacity(me), MMAP_SEQUENTIAL);

безуспешно. :-(

Я старался:

  • разные флаги MMAP_RANDOM, MMAP_DONTNEED, MMAP_NORMAL без успеха
  • posix_fadvise(me.file.handle, 0, capacity(me), POSIX_FADV_DONTNEED) до и после вызова mmap -> безуспешно

Он работает под Mac OS X !!! когда я комбинирую

posix_madvise(.. MMAP_SEQUENTIAL)

а также

msync(me.data_begin, capacity(me), MS_INVALIDATE).

Резидентная память ниже 16M (я периодически вызывал msync после 16mio шагов).

Но под Linux ничего не работает. У кого-нибудь есть идея или история успеха для моей проблемы в Linux?

Привет, Дэвид


person Dave    schedule 24.09.2010    source источник
comment
Это может иметь значение или не иметь значения, но полезно знать: вы используете 32-разрядную или 64-разрядную систему? Знаете ли вы, что вы не должны mmap 1 ГБ в 32-битной системе? (даже если вы используете 64-битную систему, вас может беспокоить переносимость).   -  person Juliano    schedule 24.09.2010
comment
Все системы 64-битные (с 64-битными файловыми указателями и смещениями), и я смог успешно сопоставить файлы размером 40 ГБ. Я просто сократил проблему до 1 ГБ для воспроизводимости.   -  person Dave    schedule 26.09.2010
comment
@Свен. Бывают случаи, когда использование отображения памяти неизбежно, например, когда для вызова библиотеки требуется область памяти, а не файл. Таким образом, ваше предложение бесполезно и не отвечает на вопрос. Что касается ответа, по-видимому, в Linux MMAP_SEQUENTIAL в значительной степени не работает. Часть упреждающего чтения работает, часть восстановления страницы - нет. И единственный способ убедить Linux в том, что эти страницы действительно являются хорошими кандидатами, — это отменить сопоставление региона (и снова сопоставить его).   -  person Dmitry Chichkov    schedule 06.03.2015


Ответы (1)


Управление памятью в Linux отличается от других систем. Ключевой принцип заключается в том, что память, которая не используется, является потраченной впустую. Во многих отношениях Linux пытается максимизировать использование памяти, что приводит (в большинстве случаев) к повышению производительности.

Дело не в том, что «ничего не работает» в Linux, а в том, что его поведение немного отличается от того, что вы ожидаете.

Когда страницы памяти извлекаются из mmapped-файла, операционная система должна решить, какие страницы физической памяти она освободит (или выгрузит) для использования. Он будет искать страницы, которые легче заменить (не требуют немедленной записи на диск) и которые с меньшей вероятностью будут использоваться снова.

POSIX-вызов madvice() служит для того, чтобы сообщить системе, как ваше приложение будет использовать страницы. Но, как следует из названия, это рекомендация, чтобы операционная система лучше ориентировалась в принятии решений о пейджинге и свопинге. Это не политика и не приказ.

Чтобы продемонстрировать влияние madvice() на Linux, я модифицировал одно из упражнений, которое даю своим студентам. См. полный исходный код здесь. Моя система 64-битная и имеет 2 ГБ ОЗУ, из которых сейчас используется около 50%. С помощью программы mmap файл размером 2 Гб, прочитать его последовательно и все выкинуть. Он сообщает об использовании RSS каждые 200 МБ чтения. Результаты без madvice():

<juliano@home> ~% ./madvtest file.dat n
     0 :     3 MB
   200 :   202 MB
   400 :   402 MB
   600 :   602 MB
   800 :   802 MB
  1000 :  1002 MB
  1200 :  1066 MB
  1400 :  1068 MB
  1600 :  1078 MB
  1800 :  1113 MB
  2000 :  1113 MB

Linux продолжал выталкивать данные из памяти, пока не было прочитано около 1 ГБ. После этого начал давить на сам процесс (поскольку остальные 50% памяти были активны другими процессами) и стабилизировался до конца файла.

Теперь с madvice():

<juliano@home> ~% ./madvtest file.dat y
     0 :     3 MB
   200 :   202 MB
   400 :   402 MB
   600 :   494 MB
   800 :   501 MB
  1000 :   518 MB
  1200 :   530 MB
  1400 :   530 MB
  1600 :   530 MB
  1800 :   595 MB
  2000 :   788 MB

Обратите внимание, что Linux решил выделять страницы процессу только до тех пор, пока он не достигал примерно 500 МБ, гораздо раньше, чем без madvice(). Это связано с тем, что после этого страницы, находящиеся в настоящее время в памяти, казались гораздо более ценными, чем страницы, которые были помечены как последовательный доступ этим процессом. В VMM есть порог, определяющий, когда начинать удалять старые страницы из процесса.

Вы можете спросить, почему Linux продолжал выделять страницы размером до 500 МБ и не остановился намного раньше, поскольку они были помечены как последовательный доступ. Дело в том, что либо в системе было достаточно свободных страниц памяти, либо другие резидентные страницы были слишком стары, чтобы их можно было сохранить. Между сохранением в памяти старых страниц, которые больше не кажутся полезными, и увеличением количества страниц для обслуживания программы, которая работает сейчас, Linux выбирает второй вариант.

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

person Juliano    schedule 24.09.2010
comment
Спасибо Джулиано, интересно поведение 50%. Я просто удивляюсь, почему нет способа заставить Linux освобождать страницы, которые я больше никогда не читаю. Вместо этого он жертвует буферами и кешем файловой системы. В MacOS X жертвование этим буфером останавливает систему до тех пор, пока она не станет полностью непригодной для использования. Но, к счастью, мы можем предотвратить это с помощью msync(... MS_INVALIDATE) В Linux похоже поведение, которое вы наблюдали с madvice, предотвращает зависание системы. - person Dave; 28.09.2010
comment
@Dave: считай, что нет смысла преждевременно освобождать эти страницы. Linux не жертвует кешем и буфером, вместо этого он делает именно это. По мере того, как вы читаете больше данных с диска, Linux все равно должен заносить их в память. Он как бы кэширует то, что было прочитано с диска, но вместо того, чтобы считать это кешем, он учитывает это как часть RSS процесса, который сопоставил этот файл. Когда Linux снова понадобится кеш, он освободит те страницы, которые сопоставлены с этим приложением. Вам не нужно беспокоиться об этом! - person Juliano; 28.09.2010
comment
@Juliano: учтите, что MADV_SEQUENTIAL специально сообщает системе, что доступ к страницам будет осуществляться через последовательное чтение только один раз. Эти страницы — идеальные кандидаты на восстановление. Вместо этого я вижу, что на моем компьютере до тех пор, пока не будет достигнуто 50% памяти (в данном случае 32 ГБ), кеш файлов восстанавливается. И я вижу, что производительность других процессов ухудшается. Теперь я нашел смешной способ заставить Linux не делать этого. Отменяя сопоставление и снова сопоставляя файл, каждые 1 Гб или около того. Это ДЕЙСТВИТЕЛЬНО решает проблему, и после этого я не вижу снижения производительности для других процессов. - person Dmitry Chichkov; 06.03.2015