Ключевые слова:memory, mmap, linux, malloc, gcc, ps, process, (найти похожие документы)
From: Santa_Claus <http://santa-claus-rpm.livejournal.com>
Date: Mon, 25 Jun 2006 14:31:37 +0000 (UTC)
Subject: Перевод статьи "Understanding memory usage on Linux"
Оригинал: http://santa-claus-rpm.livejournal.com/380.htmlhttp://virtualthreads.blogspot.com/2006/02/understanding-memory-usage-on-linux.html
(c) Devin
Saturday, February 04, 2006
Вольный перевод Santa_Claus.
Содержание
- Что показывает ps
- Почему ps "ошибается"
- Просмотр process's memory map
- Что все это значит?
- Комментарии
Эта запись сделана для тех людей, которые когда-либо задавались
вопросом - "Какого [beep] простой редактор текста KDE занимает 25
мегабайтов памяти?" Много людей думают, что многие приложения Linux,
особенно программы KDE или GNOME "раздуты". Эти предположения
базируются исключительно на том, о чем сообщает ps. В то время как это
возможно неверно, в зависимости от программы. Многие программы
распоряжаются памятью намного более эффективно, чем кажется.
Что показывает ps
ps может вывести различную информацию о процессе: его pid, текущее
состояние и использование ресурсов. Две из возможных характеристик VSZ
- "virtual set size" и RSS - "resident set size", которые обычно
используют [beep] во всем мире, чтобы узнать сколько памяти занимают
процессы. Например, вот вывод ps aux для KEdit на моем компьютере:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
dbunker 3468 0.0 2.7 25400 14452 ? S 20:19 0:00 kdeinit: kedit
Согласно ps, KEdit имеет виртуальный размер приблизительно 25 Мб, и
резидентный размер приблизительно 14 Мб (оба числа выводятся в
килобайтах). Большинство людей, не разобравшись, принимают одно число
или другое как величину реального использования памяти процессом. Я не
собираюсь объяснять различие между VSZ и RSS прямо сейчас, но, само
собой разумеется, это - неправильный подход; оба числа не точно
отображают какой размер памяти занимает Kedit при выполнении.
Почему ps "ошибается"
В зависимости от того, как Вы смотрите на это, ps не сообщает о
реальном использовании памяти процессов. То, что это действительно
делает, показывает, сколько реальной памяти каждый процесс занял бы,
если бы это было единственное выполнение процесса. Конечно, типичная
машина Linux имеет несколько дюжин процессов, выполняющихся в любое
данное время, что означает, что VSZ и числа RSS, о которых сообщает ps
почти "определенно неправильны". Чтобы понять почему, необходимо
вспомнить как Linux работает с shared libraries в программах.
Большинство программ на Linux используют разделяемые библиотеки для
получения требуемой функциональности. Например, редактор текста KDE
будет использовать несколько разделяемых библиотек KDE (чтобы учесть
взаимодействие с другими компонентами KDE), несколько библиотек X (для
вывода изображений, копирования и вставки), и несколько общих
системных библиотек (для выполнения основных операций). Многие из этих
общедоступных библиотек, особенно libc, используются многими
программами в Linux. Из-за такого совместного использования, Linux
использует большую уловку: грузит единственную копию общедоступной
библиотеки в память и использует ее до тех пор, пока хоть одна копия
программы ссылается на нее.
Что бы там ни было, многие утилиты просто сообщают, сколько памяти
процесс использует, независимо от того, разделена ли та память с
другими процессами или нет. Поэтому две программы могут использовать
большую разделяемую библиотеку и ее размер в памяти будет считаться
два раза, что может вводить в заблуждение, если Вы не знаете что
происходит.
К сожалению, непросто получить точное представление об использовании
памяти процессом. Мало того, что Вы должны понять, как система
действительно работает, но Вы должны решить, как Вы будете отвечать на
некоторые сложные вопросы. Должна ли общедоступная библиотека, которая
необходима только для одного процесса, засчитываться в используемую
память этого же процесса? Если общедоступная библиотека используется
многими процессами, то ее использованная память должна быть равномерно
распределена среди различных процессов или должна игнорироваться?
Здесь нет твердого и быстрого правила; Вы можете иметь различные
ответы в зависимости от ситуации.
Просто теперь видно, почему ps не может уверенно сообщить "о
правильных" величинах использования памяти.
Просмотр process's memory map
Теперь давайте посмотрим ситуацию с "огромным" процессом KEdit. Чтобы
видеть, на что похожа память KEDIT, мы будем использовать команду pmap -d :
Address Kbytes Mode Offset Device Mapping
08048000 40 r-x-- 0000000000000000 0fe:00000 kdeinit
08052000 4 rw--- 0000000000009000 0fe:00000 kdeinit
08053000 1164 rw--- 0000000008053000 000:00000 [ anon ]
40000000 84 r-x-- 0000000000000000 0fe:00000 ld-2.3.5.so
40015000 8 rw--- 0000000000014000 0fe:00000 ld-2.3.5.so
40017000 4 rw--- 0000000040017000 000:00000 [ anon ]
40018000 4 r-x-- 0000000000000000 0fe:00000 kedit.so
40019000 4 rw--- 0000000000000000 0fe:00000 kedit.so
40027000 252 r-x-- 0000000000000000 0fe:00000 libkparts.so.2.1.0
40066000 20 rw--- 000000000003e000 0fe:00000 libkparts.so.2.1.0
4006b000 3108 r-x-- 0000000000000000 0fe:00000 libkio.so.4.2.0
40374000 116 rw--- 0000000000309000 0fe:00000 libkio.so.4.2.0
40391000 8 rw--- 0000000040391000 000:00000 [ anon ]
40393000 2644 r-x-- 0000000000000000 0fe:00000 libkdeui.so.4.2.0
40628000 164 rw--- 0000000000295000 0fe:00000 libkdeui.so.4.2.0
40651000 4 rw--- 0000000040651000 000:00000 [ anon ]
40652000 100 r-x-- 0000000000000000 0fe:00000 libkdesu.so.4.2.0
4066b000 4 rw--- 0000000000019000 0fe:00000 libkdesu.so.4.2.0
4066c000 68 r-x-- 0000000000000000 0fe:00000 libkwalletclient.so.1.0.0
4067d000 4 rw--- 0000000000011000 0fe:00000 libkwalletclient.so.1.0.0
4067e000 4 rw--- 000000004067e000 000:00000 [ anon ]
4067f000 2148 r-x-- 0000000000000000 0fe:00000 libkdecore.so.4.2.0
40898000 64 rw--- 0000000000219000 0fe:00000 libkdecore.so.4.2.0
408a8000 8 rw--- 00000000408a8000 000:00000 [ anon ]
... (trimmed) ...
mapped: 25404K writeable/private: 2432K shared: 1K
Я убрал ненужный вывод, т.к. остальные строки подобны уже показанным.
Даже без полного вывода, мы можем видеть некоторые очень интересные
вещи. Одна важная вещь,- то, что каждая общедоступная библиотека
перечислена дважды; один раз для сегмента кода и один - для сегмента
данных. Участки кода имеют режим "r-x-", в то время как данные
"rw---". Мы будем рассматривать только столбцы Kbytes, Mode и Mapping
поскольку остальные данные не играют важной роли.
Просматривая листинг Вы найдете строки с наибольшей занимаемой
памятью,- это сегменты кода включающие разделяемые библиотеки (их
названия начинаются с lib). Все они могут использоваться и другими
процессами. В этом случае смотрите итоговую строку
"writeable/private".
Таким образом, затраты на запуск одной копии KEdit в случае, если все
разделяемые библиотеки уже подгружены в память, составляют около 2 Мб.
Сравните это с теми 14 или 25 Мб о которых сообщает ps.
Что все это значит?
Мораль всей этой истории в том, что использование процессами памяти в
Linux является сложным вопросом. Вы не можете выполнить только ps и
знать, что происходит. Это особенно истинно, когда Вы имеете дело с
программами типа Apache, которые создают много идентичных дочерних
процессов.
ps мог бы сообщить, что каждый процесс Apache использует 10 Мб памяти,
когда в действительности наибольшая занимаемая память каждого процесса
Apache 1 Мб. Эта информация становится критичной при настройке опции
MaxClients, которая определяет, сколько одновременных запросов может
обработать ваш сервер.
Это также показывает, почему необходимо придерживаться одного
десктопного программного обеспечения в максимально возможной степени.
Если Вы выполняете KDE для вашего ПК, но в основном используете
приложения Gnome, то Вы платите большую цену за большое количество
избыточных (но различных) разделяемых библиотек. Придерживаясь только
KDE или только Gnome в максимально возможной степени, Вы уменьшаете
использование памяти и это позволяет Linux использовать больше памяти
для других интересных вещей (например, для файлового кэша для
ускорения доступа к файлам).
Комментарии
Anonymous: Здесь полностью игнорируется тот факт, что каждая из
разделяемых библиотек делает множество отображений виртуальных страниц
к физическим. Они, в отличие от памяти, являются очень драгоценным
ресурсом. Только, потому что люди имеют тенденцию игнорировать это, не
означает, что разделяемые библиотеки не имеют высокой цены. Пробуйте
выполнить что-нибудь типа KDE на не x86 железе, которое не имеет
огромных адресных таблиц. На mips аппаратных средствах, я видел, что
приложения тратят более чем 30 % их времени, обновляя tlb входы.
Emsi Guru: Вы парни, кажется, забыли, что каждая программа использует
текст и сегменты данных. В очень простом примере текст - это код
программы, который разделен среди всех копий данного процесса, как в
хорошо спроектированной ОС Linux. Данные, такие как стек или локально
распределенная память (malloced), отличны для каждого процесса. Очень
важно не забыть о таких данных, и плохо написанный редактор мог бы
распределить огромный кусок памяти чтобы открыть простой logfile в 500
Мб и это просто неправильно!
Anonymous: Цена фрагментации также требует рассмотрения. Хорошее
приложение может все еще потреблять много, много Мбайт памяти, которую
фактически не использует.
Например, приложение должно сделать множество больших распределений
памяти для некоторой временной рабочей области для некоторой операции.
Также необходимо распределить некоторое хранилище для дополнительной
информации (хронология отмены, например) и другие биты информации,
чтобы хранить/использовать вычисленные данные.
Когда распределения сделаны, куча растет. Однако, куча может сжаться
только усечением. Программа может освободить, отдать память
операционной системе назад, сокращая кучу целиком. Если приложение
делает много распределений, и освобождает только некоторые (или даже
большинство) из их, его куча будет похожа на это:
x: свободная память
U: используемая память
UUUxxxxxxxxxxxxxU
Т.е. 4-е страницы памяти занято и 12 страниц свободно, но которые не
могут быть освобождены.
Если приложение будет делать еще распределения, то оно может
использовать всю ту неиспользованную кучу. Но если нет, то будет
проглатывать память.
В Linux проблема не стоит остро, так как неиспользованные страницы
будут выгружены на диск. Однако, плохо то, что поскольку VM выгружает
полностью неиспользованные куски памяти и когда приложение должно
перераспределить память, подгружает их назад вместе с мусором
неиспользованных данных.
Этот феномен - одна из многих причин, почему хорошие современные
сборщики мусора намного более эффективны, чем ручное управление
памятью с помощью malloc/free. Сборщик мусора будет полностью решать
вышеупомянутую проблему.
Anonymous: Я в шоке, как немного людей пишут комментарии, очевидно все
программисты, знают об этом.
Я не потрясен. Много так называемых "программистов" не имеют понятия,
как работает компьютер и еще намного меньше знают нюансы различных
операционных систем. Многие изучают специфическую структуру парадигмы
программирования и останавливаются на этом. Я всегда любил компьютеры
и программирование и нахожу, что это трудно понимает любой, кто входит
в эту область только ради денег, не интересуясь ничем за пределами
"только программирования".
Ответ автора на сделанные комментарии
Много людей сделало хорошие комментарии. Вот некоторые из общих идей,
которые были упомянуты:
1.Некоторые отметили, что non-x86 аппаратные средства имеют различный
подход к памяти совместно используемой между процессами.
2.Много людей также отметили, что разделяемые библиотеки Linux не
присущи только этой ОС. Платформы Microsoft Windows несомненно имеют
такую же особенность совместного использования, точно так же как любая
полновесная современная операционная система.
3.В некоторых комментариях упоминается размер памяти занимаемый
Firefox. Я должен признать, что я начал эту статью с Firefox вместо
KEdit в качестве примера, но я был вынужден перейти к KEdit, когда
увидел, что KEdit иллюстрирует мою мысль гораздо лучше.:)