7.5. Configuring System Memory Capacity
This section discusses memory-related kernel parameters that may be useful in improving memory utilization on your system. These parameters can be temporarily set for testing purposes by altering the value of the corresponding file in the /proc file system. Once you have determined the values that produce optimal performance for your use case, you can set them permanently by using the sysctl command.
Memory usage is typically configured by setting the value of one or more kernel parameters. These parameters can be set temporarily by altering the contents of files in the /proc file system, or they can be set persistently with the sysctl tool, which is provided by the procps-ng package.
# echo 1 > /proc/sys/vm/overcommit_memory
To set this value persistently, add sysctl vm.overcommit_memory=1 in /etc/sysctl.conf then run the following command:
Setting a parameter temporarily is useful for determining the effect the parameter has on your system. You can then set the parameter persistently when you are sure that the parameter’s value has the desired effect.
To expand your expertise, you might also be interested in the Red Hat Enterprise Linux Performance Tuning (RH442) training course.
7.5.1. Virtual Memory Parameters
A percentage value. When this percentage of total system memory is modified, the system begins writing the modifications to disk with the pdflush operation. The default value is 20 percent.
A percentage value. When this percentage of total system memory is modified, the system begins writing the modifications to disk in the background. The default value is 10 percent.
The default value is 0 . By default, the kernel performs heuristic memory overcommit handling by estimating the amount of memory available and failing requests that are too large. However, since memory is allocated using a heuristic rather than a precise algorithm, overloading memory is possible with this setting.
When this parameter is set to 1 , the kernel performs no memory overcommit handling. This increases the possibility of memory overload, but improves performance for memory-intensive tasks.
When this parameter is set to 2 , the kernel denies requests for memory equal to or larger than the sum of total available swap space and the percentage of physical RAM specified in overcommit_ratio . This reduces the risk of overcommitting memory, but is recommended only for systems with swap areas larger than their physical memory.
Specifies the percentage of physical RAM considered when overcommit_memory is set to 2 . The default value is 50 .
Defines the maximum number of memory map areas that a process can use. The default value ( 65530 ) is appropriate for most cases. Increase this value if your application needs to map more than this number of files.
Specifies the minimum number of kilobytes to keep free across the system. This is used to determine an appropriate value for each low memory zone, each of which is assigned a number of reserved free pages in proportion to their size.
Extreme values can damage your system. Setting min_free_kbytes to an extremely low value prevents the system from reclaiming memory, which can result in system hangs and OOM-killing processes. However, setting min_free_kbytes too high (for example, to 5–10% of total system memory) causes the system to enter an out-of-memory state immediately, resulting in the system spending too much time reclaiming memory.
In the event that the system runs out of memory and the panic_on_oom parameter is set to 0 , the oom_killer function kills processes until the system can recover, starting from the process with the highest oom_score .
The oom_adj parameter helps determine the oom_score of a process. This parameter is set per process identifier. A value of -17 disables the oom_killer for that process. Other valid values are from -16 to 15 .
The swappiness value, ranging from 0 to 100 , controls the degree to which the system favors anonymous memory or the page cache. A high value improves file-system performance while aggressively swapping less active processes out of RAM. A low value avoids swapping processes out of memory, which usually decreases latency at the cost of I/O performance. The default value is 60 .
Setting swappiness==0 will very aggressively avoids swapping out, which increase the risk of OOM killing under strong memory and I/O pressure.
7.5.2. File System Parameters
Defines the maximum allowed number of events in all active asynchronous input/output contexts. The default value is 65536 . Modifying this value does not pre-allocate or resize any kernel data structures.
Determines the maximum number of file handles for the entire system. The default value on Red Hat Enterprise Linux 7 is the maximum of either 8192 , or one tenth of the free memory pages available at the time the kernel starts.
7.5.3. Kernel Parameters
Default values for the following parameters, located in the /proc/sys/kernel/ directory, can be calculated by the kernel at boot time depending on available system resources.
Defines the maximum allowable size in bytes of any single message in a message queue. This value must not exceed the size of the queue ( msgmnb ). To determine the current msgmax value on your system, use:
Defines the maximum size in bytes of a single message queue. To determine the current msgmnb value on your system, use:
Defines the maximum number of message queue identifiers, and therefore the maximum number of queues. To determine the current msgmni value on your system, use:
Defines the total amount of shared memory pages that can be used on the system at one time. A page is 4096 bytes on the AMD64 and Intel 64 architecture, for example.
Defines the maximum size (in bytes) of a single shared memory segment allowed by the kernel. To determine the current shmmax value on your system, use:
Defines the system-wide maximum number of shared memory segments. The default value is 4096 on all systems.
Defines the system-wide maximum number of threads available to the kernel at one time. To determine the current threads-max value on your system, use:
mempages / (8 * THREAD_SIZE / PAGE SIZE )
Борьба за ресурсы, часть 3: Памяти мало не бывает
Продолжаем изучать Control Groups (Cgroups) в Red Hat Enterprise Linux 7. Займемся памятью. Вы помните, что для распределения процессорного времени есть две регулировки: CPUShares для настройки относительных долей и CPUQuota для того, чтобы ограничивать пользователя, службу или виртуальную машину (ВМ) в абсолютных величинах (процентах) процессорного времени. Причем, обе эти регулировки можно использовать одновременно. Например, если для пользователя задана CPU-квота в 50 %, то его CPU-шара тоже будет приниматься во внимание до тех пор, пока он полностью не выберет свою квоту в 50 % процессорного времени.
Что касается оперативной памяти, то systemd предлагает только один способ регулировки, а именно…
Объем памяти, который может быть выделен пользователю или службе. Допустим, мы хотим ограничить пользователя mrichter 200 МБ ОЗУ. Если помните, его UID равен 1000, поэтому мы вводим следующую команду:
systemctl set-property user-1000.slice MemoryLimit=200M
Теперь mrichter хочет проверить свои границы и запускает утилиту нагрузочного тестирования stress, которая начинает усиленно потреблять память. И stress очень быстро выдает ошибку:
По системному журналу видно, что stress был попросту прерван OOM (Out Of Memory) Killer.
Здесь важно обратить внимание вот на что: по умолчанию ограничение на ОЗУ распространяется только на резидентную память. То есть, если процесс может уходить в файл подкачки («своп»), то он обойдет установленное ограничение. В нашем примере stress вылетел потому, что превысил ограничение на резидентную память.
А если мы не хотим, чтобы программа сливалась в своп?
Это, в общем-то, легко запретить. Ну или относительно легко… В общем, придется кое-куда залезть.
Есть такие настройки cgroup, до которых не добраться ни через команду systemctl, ни через юнит-файлы. Однако эти настройки можно менять на лету через файлы в папке /sys/fs/cgroup/. Вот как, к примеру, выглядит cgroup пользователя mrichter в части памяти:
Файл, отвечающий за то, сколько памяти может уходить в своп, вполне очевидно называется memory.swappiness. Посмотрим, что у него внутри:
Если вам случалось играться с настройками ядра и подсистемой свопинга, то вы сразу увидите здесь стандартное значение параметра swappiness по умолчанию. Если поменять его на ноль, то ОЗУ-регулятор для пользователя mrichter вообще запретит ему использовать своп.
Кстати, здесь же можно глянуть статистику памяти для пользователя mrichter:
Значение параметра hierarchical_memory_limit – это тот самый MemoryLimit, который мы задали командой systemctl. Параметр hierarchical_memsw_limit представляет собой суммарный лимит (резидентная память и память в файле подачки). Мы запретили пользователю mrichter использовать файл подкачки, поэтому значение этого параметра такое странное.
Теперь о проблемах только что описанного подхода:
- Вносить изменения в эти файлы можно только тогда, когда пользователь mrichter залогинился в систему. Пока он не войдет, его cgroup будет неактивна.
- Эти настройки не сохраняются после перезагрузки. Более того, они потеряются, если mrichter перелогинится.
Вот какой сценарий мы создадим в папке /usr/local/bin:
А затем добавим его вызов в последнюю строку /etc/pam.d/sshd. В результате, этот сценарий будет запускаться при каждом входе пользователя через ssh. Именно поэтому мы и проверяем в сценарии, что это пользователь mrichter, прежде чем менять настройки.
Итак, мы отрезали пользователя mrichter от файла подкачки.
Можно конечно пойти еще дальше и менять конфигурационные файлы активной cgroup на лету, но мы пока отложим это рисковое дело. Тем не менее, общий метод, как менять настройки пользователя, вы уловили.
А со службами все еще проще. В юнит-файле службы можно использовать директиву ExecStartPost=, чтобы запускать сценарий, меняющий настройки. Например, вот как надо изменить юнит-файл службы foo, чтобы выключить свопинг:
Запускаем foo – и никакого свопинга:
Ладно, на сегодня, пожалуй, хватит с нас этого шаманства.
Но прежде чем закончить, давайте остановимся на документации по cgroup, в которой можно найти информацию обо всех этих скрытых настройках регуляторов. Вы можете установить пакет kernel-doc на свой компьютер, как это сделал я, загрузив его из репозитория «rhel-7-server-rpms».
После установки откройте папку /usr/share/docs, соответствующую вашему ядру, и перейдите в папку cgroups, где и содержится последняя информация по всем регуляторам.
В следующем раз мы поговорим о вводе-выводе. И, кстати, мы уже почти подошли к тому, чтобы узнать, как cgroups привели к появлению контейнеров (на самом деле cgroups – это ключевой компонент контейнеров в Red Hat Enterprise Linux и Red Hat OpenShift Container Platform).
Chapter 5. Memory
Read this chapter for an overview of the memory management features available in Red Hat Enterprise Linux, and how to use these management features to optimize memory utilization in your system.
5.1. Huge Translation Lookaside Buffer (HugeTLB)
Physical memory addresses are translated to virtual memory addresses as part of memory management. The mapped relationship of physical to virtual addresses is stored in a data structure known as the page table. Since reading the page table for every address mapping would be time consuming and resource-expensive, there is a cache for recently-used addresses. This cache is called the Translation Lookaside Buffer (TLB).
However, the TLB can only cache so many address mappings. If a requested address mapping is not in the TLB, the page table must still be read to determine the physical to virtual address mapping. This is known as a «TLB miss». Applications with large memory requirements are more likely to be affected by TLB misses than applications with minimal memory requirements because of the relationship between their memory requirements and the size of the pages used to cache address mappings in the TLB. Since each miss involves reading the page table, it is important to avoid these misses wherever possible.
The Huge Translation Lookaside Buffer (HugeTLB) allows memory to be managed in very large segments so that more address mappings can be cached at one time. This reduces the probability of TLB misses, which in turn improves performance in applications with large memory requirements.
Information about configuring the HugeTLB can be found in the kernel documentation: /usr/share/doc/kernel-doc-version/Documentation/vm/hugetlbpage.txt