- комментарий 0:
From: kmeaw
Date: 2021-05-17 20:01:04Z
> Говорю маленький "ulimit -m", но
> что Go, что Си программы упорно спокойно могут аллоцировать гигабайты.
ulimit -d 16384 -s 16384 -l 16384 -m 16384
(data, stack, mlocked & resident memory)
После этого ни mmap, ни (s)brk не выделяют память.
> А если OOM killer и сработает, то запросто грохнет
> какой-нибудь SSH.
Обычно для этого подкручивают oom_adj для sshd, чтобы его грохали в
последнюю очередь. А браузеры, в свою очередь, подкручивают в обратную
сторону oom_adj для процессов-вкладок, чтобы их OOM killer убивал
первыми.
> Знал что есть soft и hard, но даже примерно не представлял чем они на
> практике бы отличались.
Реально на процесс действует soft limit. Hard limit - административное
ограничение, выше которого soft limit поднять нельзя.
На практике администраторы обычно для почти всех ресурсов ставят soft
limit равным hard limit, а на файловые дескрипторы soft limit делают
меньше hard limit в несколько раз. Если программа знает про resource
limits, то она временно опустит soft limit до необходимого минимума или
поднимет soft limit на открытые файловые дескрипторы, если, например,
это какой-нибудь нагруженный сетевой сервис.
> Впрочем у меня всё равно ulimit никоим образом не
> отрабатывает (или я делаю что-то не так).
odin% zsh --version
zsh 5.4.2 (x86_64--netbsd)
odin% ulimit -t 5
odin% while true; do :; done
Connection to sdf-eu.org closed.
- комментарий 1:
From: Sergey Matveev
Date: 2021-05-18 07:24:23Z
*** kmeaw [2021-05-17 22:59]:
>ulimit -d 16384 -s 16384 -l 16384 -m 16384
>После этого ни mmap, ни (s)brk не выделяют память.
Похоже я совсем не знаю как всё устроено и работает с памятью. Я вот
написал вот такое:
int
main(void)
{
int i = 0;
while (true) {
if (malloc(10*(1<<20)) == NULL) {
abort();
};
usleep(10000);
printf("%d\n", i++);
};
return 0;
}
Сделал ulimit как у вас (только без -l, ибо у меня hard-limit сильно
меньше) и смотрю в top, дошло до:
PID USERNAME THR PRI NICE SIZE RES STATE C TIME WCPU COMMAND
38174 stargrave 1 20 0 43G 20M nanslp 0 0:00 0.35% main
Размер виртуальной памяти огромный -- понимаю ещё почему может быть, это
не напрягает. Хотя я считал что malloc то уж должен быть RES-ом, а
всякие SIZE учитывают mmap-нутые файлы например. У меня zsh 5.6.2,
FreeBSD 12.0, но, как видно, всё равно даже RES достиг больше чем 16MiB
указанных в ulimit и ничего не падает. Или ulimit влияет только и только
на сам shell, но не его детей? Буду читать про это. Ибо ваш пример с -t 5
то у меня честно отрабатывает.
>Обычно для этого подкручивают oom_adj для sshd, чтобы его грохали в
>последнюю очередь. А браузеры, в свою очередь, подкручивают в обратную
>сторону oom_adj для процессов-вкладок, чтобы их OOM killer убивал
>первыми.
Наслышан, наслышан. Сам правда ни с чем таким ни разу не встречался.
>Реально на процесс действует soft limit. Hard limit - административное
>ограничение, выше которого soft limit поднять нельзя.
>На практике администраторы обычно для почти всех ресурсов ставят soft
>limit равным hard limit
Спасибо за информацию! С этим всем пока ещё в курсе.
- комментарий 2:
From: Sergey Matveev
Date: 2021-05-18 09:49:15Z
У коллеги под bash и Gentoo какой-то тоже ничего не вышло.
Вроде у меня всё встаёт на свои места почему не работало. man setrlimit
говорит что:
RLIMIT_RSS When there is memory pressure and swap is available,
prioritize eviction of a process' resident pages beyond
this amount (in bytes). When memory is not under
pressure, this rlimit is effectively ignored. Even when
there is memory pressure, the amount of available swap
space and some sysctl settings like vm.swap_enabled and
vm.swap_idle_enabled can affect what happens to processes
that have exceeded this size.
Processes that exceed their set RLIMIT_RSS are not
signalled or halted. The limit is merely a hint to the
VM daemon to prefer to deactivate pages from processes
that have exceeded their set RLIMIT_RSS.
то есть RLIMIT_RSS никому не скажет ENOMEM и ничего не сделает.
RLIMIT_DATA The maximum size (in bytes) of the data segment for a
process; this defines how far a program may extend its
break with the sbrk(2) function.
влияет только честно на sbrk. Но у меня из коробки используется
jemalloc, который использует по умолчанию для больших блоков mmap, а не
sbrk, который не учитывается RLIMIT_DATA. В итоге у нас получилось
ограничить память только по RLIMIT_VMEM, который как-раз и делает (в том
числе) softlimit из daemontools.
- комментарий 3:
From: kmeaw
Date: 2021-05-18 20:08:09Z
Проверил программу из комментария 1 на разных ОС.
Linux 4.19, 5.2.11, 5.4.31, 5.10.27: ломается сразу, напечатав "0".
NetBSD 9.1: не ломается, быстро растёт SIZE, медленно растёт RES.
Если добавить bzero на выделяемую память, то начинает быстро расти RES.
Возможно, что реализация malloc у NetBSD вызывает mmap без BSD-аналога
MAP_POPULATE, отдавая "непрогретые" странички. При таком вызове страница
будет выделена "по-настоящему" тогда, когда её потрогают.
В Linux это можно отключить настройкой vm.overcommit_ratio=0, но этого
делать не рекомендуется, потому что многие программы расчитывают на
такое поведение. В Windows такого нет, зато драйверы ядра умеют уходить
в своп.
FreeBSD 13.0-RELEASE, тоже не ломается. Без bzero растёт SIZE, поведение
аналогично NetBSD.
OpenBSD 6.9: ломается сразу, напечатав "0".
Вариант с RLIMIT_VMEM мне не очень нравится, потому что на 64-битных
платформах нет особого смысла экономить адресное пространство, а
лимитируемая программа может при нормальной работе отображать
(non-anonymous mmap) большие файлы на память, не приводя к расходу
резидентной памяти. Но, похоже, что на BSD без этого никак, так как "[t]he
limit is merely a hint". А в OpenBSD вовсе нет RLIMIT_VMEM (или аналога,
как RLIMIT_AS в Linux).
Попытался включить rctl, но настройка user:foo:memoryuse:deny=40m тоже
не ломает эту программу. user:foo:vmemoryuse:deny=40m работает, так же,
как и RLIMIT_VMEM. Но можно написать правило для monit, которое будет
следить и перезапускать прожорливую программу.
> limit влияет только и только на сам shell, но не его детей?
При execve resource limits наследуются от родителя, но могут быть
изменены дочерним процессом для себя. В Linux ещё есть prlimit, который
позволяет менять resource limits чужим процессам (если есть
capability CAP_SYS_RESOURCE).
- комментарий 4:
From: Sergey Matveev
Date: 2021-05-18 20:38:03Z
*** kmeaw [2021-05-18 23:06]:
>Linux 4.19, 5.2.11, 5.4.31, 5.10.27: ломается сразу, напечатав "0".
У коллеги с Gentoo не знаю что за версии ядра, но было такое же
поведение как у меня -- ни разу не "сломалось", пока VMEM не ограничили.
>Если добавить bzero на выделяемую память, то начинает быстро расти RES.
Ага, это тоже проделывали на Gentoo. С этим оно действительно начинает
есть память, а не просто "резервировать" с неспешным ростом RES.
>В Linux это можно отключить настройкой vm.overcommit_ratio=0, но этого
>делать не рекомендуется, потому что многие программы расчитывают на
>такое поведение.
Во-во, именно про overcommit я и подумал и понимаю что поведение
"непрогретых" страниц связано с ним.
>Вариант с RLIMIT_VMEM мне не очень нравится
Вот и мне тоже. Я поэтому всё что касается VMEM-а и не трогал и не
пробовал. Плюс ещё можно mmap-ать файлы, что тоже не имеет смысла
ограничивать.
>Но можно написать правило для monit, которое будет
>следить и перезапускать прожорливую программу.
Ну это уже совсем костыль, так сказать. Работать то конечно будет, если
процесс не разрастётся в окне между проверками monit. Завтра попробую
ограничить в Jail-е через rctl. https://wiki.freebsd.org/JailResourceLimits
вроде бы можно RSS ограничивать.
- комментарий 5:
From: Sergey Matveev
Date: 2021-05-19 10:02:59Z
*** Sergey Matveev [2021-05-18 23:38]:
>ограничить в Jail-е через rctl
Ограничение по RSS работает... top показывает что процесс более не
вылезает за указанный лимит. Вот только размер виртуальной памяти при
этом всё равно упорно продолжает расти. Сейчас использую цикл с
malloc-ами и arc4random заполнением этих страниц, чтобы точно там были
недедуплицируемые данные.
В общем, я не вижу как можно было бы ограничить размер памяти в FreeBSD,
кроме как только ограничением размера виртуальной.