Ben*_*lts 207 linux memory ulimit
我正在pdftoppm将用户提供的 PDF 转换为 300DPI 图像。这很好用,除非用户提供页面非常大的 PDF。 pdftoppm将分配足够的内存来在内存中保存该大小的 300DPI 图像,对于 100 英寸的方形页面,它是 100*300 * 100*300 * 4 字节/像素 = 3.5GB。恶意用户可以给我一个愚蠢的大 PDF 并导致各种问题。
所以我想做的是对我即将运行的子进程的内存使用设置某种硬限制——如果它试图分配超过 500MB 的内存,就让进程死掉。那可能吗?
我不认为 ulimit 可以用于此,但是否有一个等价的进程?
小智 160
另一种限制这种情况的方法是使用 Linux 的控制组。如果您想将一个进程(或一组进程)的物理内存分配与虚拟内存区分开来,这将特别有用。例如:
cgcreate -g memory:myGroup
echo 500M > /sys/fs/cgroup/memory/myGroup/memory.limit_in_bytes
echo 5G > /sys/fs/cgroup/memory/myGroup/memory.memsw.limit_in_bytes
Run Code Online (Sandbox Code Playgroud)
将创建一个名为 的控制组myGroup,限制在myGroup最多 500 MB 的物理内存memory.limit_in_bytes和最多 5000 MB 的物理内存和交换内存下运行的一组进程memory.memsw.limit_in_bytes。有关这些选项的更多信息,请访问:https : //access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-memory
要在控制组下运行进程:
cgexec -g memory:myGroup pdftoppm
Run Code Online (Sandbox Code Playgroud)
请注意,在现代 Ubuntu 发行版上,此示例需要安装cgroup-bin软件包并编辑/etc/default/grub以更改GRUB_CMDLINE_LINUX_DEFAULT为:
GRUB_CMDLINE_LINUX_DEFAULT="cgroup_enable=memory swapaccount=1"
Run Code Online (Sandbox Code Playgroud)
然后运行sudo update-grub并重新启动以使用新的内核启动参数启动。
小智 92
如果您的进程没有产生更多消耗最多内存的子进程,则可以使用setrlimit函数。更常见的用户界面是使用ulimitshell 命令:
$ ulimit -Sv 500000 # Set ~500 mb limit
$ pdftoppm ...
Run Code Online (Sandbox Code Playgroud)
这只会限制进程的“虚拟”内存,考虑并限制被调用的进程与其他进程共享的内存,以及映射但未保留的内存(例如,Java 的大堆)。尽管如此,虚拟内存是增长非常大的进程的最接近的近似值,使得所述错误无关紧要。
如果您的程序产生子进程,并且由它们分配内存,则它变得更加复杂,您应该编写辅助脚本来在您的控制下运行进程。我在我的博客中写道,为什么以及如何。
kvz*_*kvz 76
ulimit 有一些问题。这是有关该主题的有用阅读:限制 Linux 中程序的时间和内存消耗,这导致了超时工具,它可以让您通过时间或内存消耗来控制进程(及其分支)。
超时工具需要 Perl 5+ 和已/proc挂载的文件系统。之后,您将工具复制到例如/usr/local/bin:
curl https://raw.githubusercontent.com/pshved/timeout/master/timeout | \
sudo tee /usr/local/bin/timeout && sudo chmod 755 /usr/local/bin/timeout
Run Code Online (Sandbox Code Playgroud)
之后,您可以像在您的问题中一样通过内存消耗来“控制”您的进程,如下所示:
timeout -m 500 pdftoppm Sample.pdf
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用-t <seconds>和-x <hertz>分别通过时间或 CPU 限制来限制进程。
该工具的工作方式是每秒多次检查生成的进程是否没有超额订阅其设置的边界。这意味着实际上有一个小窗口,一个进程可能会在超时通知和终止进程之前过度订阅。
因此,更正确的方法可能涉及 cgroups,但设置起来要复杂得多,即使您使用 Docker 或 runC,其中提供了对 cgroups 更用户友好的抽象。
Hi-*_*gel 64
在任何基于 systemd 的发行版上,您还可以通过 systemd-run 间接使用 cgroup。例如,对于限制pdftoppm为 500M RAM 的情况,请使用:
systemd-run --scope -p MemoryMax=500M pdftoppm
Run Code Online (Sandbox Code Playgroud)
注意:这会要求您输入密码,但应用程序会以您的用户身份启动。不要让这让您误以为命令需要sudo,因为这会导致命令在 root 下运行,这不是您的本意。
如果您不想输入密码(实际上,为什么需要密码来限制您已经拥有的内存),您可以使用--user选项,但是要使其工作,您将需要启用 cgroupsv2 支持,现在需要启动与systemd.unified_cgroup_hierarchy内核参数。
Jan*_*nis 12
我正在使用以下脚本,效果很好。它通过 更新:它现在使用来自cgmanager. cgroup-tools. 命名这个脚本limitmem并将它放在你的 $PATH 中,你可以像limitmem 100M bash. 这将限制内存和交换使用。为了仅限制内存,请删除带有memory.memsw.limit_in_bytes.
编辑:在默认的 Linux 安装中,这只会限制内存使用,而不是交换使用。要启用交换使用限制,您需要在 Linux 系统上启用交换记帐。通过设置/添加swapaccount=1来/etc/default/grub做到这一点,它看起来像
GRUB_CMDLINE_LINUX="swapaccount=1"
Run Code Online (Sandbox Code Playgroud)
然后运行sudo update-grub并重新启动。
免责声明:如果cgroup-tools将来也会中断,我不会感到惊讶。正确的解决方案是使用 systemd api 进行 cgroup 管理,但该 atm 没有命令行工具
GRUB_CMDLINE_LINUX="swapaccount=1"
Run Code Online (Sandbox Code Playgroud)
cgroupsv2 更新 (Ubuntu 22.04)
事情发生了一些变化。与https://unix.stackexchange.com/a/125024/32558相比,现在您需要:
sudo cgcreate -a $USER:$USER -g memory:myGroup -t $USER:$USER
sudo cgset -r memory.max=500M myGroup
sudo cgset -r memory.swap.max=0 myGroup
sudo chmod o+w /sys/fs/cgroup/cgroup.procs
cgexec -g memory:myGroup mycmd arg0 arg1
Run Code Online (Sandbox Code Playgroud)
该行:
sudo chmod o+w /sys/fs/cgroup/cgroup.procs
Run Code Online (Sandbox Code Playgroud)
它需要在没有以下情况下工作sudo:https://askubuntu.com/questions/1406329/how-to-run-cgexec-without-sudo-as-current-user-on-ubuntu-22-04-with-cgroups- v2/1450845#1450845否则会失败并显示:
cgroup change of group failed
Run Code Online (Sandbox Code Playgroud)
如果您不运行该命令,您还可以使用:
sudo cgexec -g memory:myGroup mycmd arg0 arg1
Run Code Online (Sandbox Code Playgroud)
但随后运行为root,您通常不希望它这样做,这可以使用 进行测试sudo cgexec -g memory:myGroup id。
与最初用于 v1 的https://unix.stackexchange.com/a/125024/32558相比:
/sys/fs/cgroup/memory/myGroup/memory.limit_in_bytes就是现在/sys/fs/cgroup/myGroup/memory.max/sys/fs/cgroup/memory/myGroup/memory.memsw.limit_in_bytes被分割了,现在你只需单独设置交换/sys/fs/cgroup/myGroup/memory.swap.max而不是总和然而,对于内存的具体情况,只需使用https://unix.stackexchange.com/a/536046/32558systemd-run中提到的方法即可,该方法刚刚起作用,并且是迄今为止最简单的方法。
测试一下
malloc_touch.c
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
size_t nbytes, step;
if (argc > 1) {
nbytes = strtoull(argv[1], NULL, 0);
} else {
nbytes = 0x10;
}
if (argc > 2) {
step = strtoull(argv[2], NULL, 0);
} else {
step = 1;
}
char *base = malloc(nbytes);
assert(base);
char *i = base;
while (i < base + nbytes) {
*i = 13;
i += step;
}
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
首先我们发现这1M大约是 C hello world 运行的最低限度:
sudo cgset -r memory.max=1M myGroup
cgexec -g memory:myGroup ./malloc_touch.out
Run Code Online (Sandbox Code Playgroud)
然后从那里开始我们可以尝试 malloc 100k:
sudo cgset -r memory.max=1M myGroup
cgexec -g memory:myGroup ./malloc_touch.out 100000
Run Code Online (Sandbox Code Playgroud)
好吧,还剩下一些。那么如果尝试 1M:
sudo cgset -r memory.max=1M myGroup
cgexec -g memory:myGroup ./malloc_touch.out 1000000
Run Code Online (Sandbox Code Playgroud)
果然被杀了。将限制增加到 10M:
sudo cgset -r memory.max=10M myGroup
cgexec -g memory:myGroup ./malloc_touch.out 1000000
Run Code Online (Sandbox Code Playgroud)
好的。Malloc 9M:
sudo cgset -r memory.max=10M myGroup
cgexec -g memory:myGroup ./malloc_touch.out 9000000
Run Code Online (Sandbox Code Playgroud)
好的。Malloc 10M:
sudo cgset -r memory.max=10M myGroup
cgexec -g memory:myGroup ./malloc_touch.out 10000000
Run Code Online (Sandbox Code Playgroud)
好的。嗯,不知道为什么,预计它会死。MiB 与 MB?尝试11M
sudo cgset -r memory.max=10M myGroup
cgexec -g memory:myGroup ./malloc_touch.out 11000000
Run Code Online (Sandbox Code Playgroud)
果然被杀了。
在 Ubuntu 22.10 上测试。
cgcreate 在 Ubuntu 21.10 上完全损坏
https://askubuntu.com/questions/1376093/is-cgroup-tools-using-cgroup-v1-or-v2
失败:
cgcreate: libcgroup initialization failed: Cgroup is not mounted
Run Code Online (Sandbox Code Playgroud)
显然他们将系统的一部分移至 v2,但没有移至另一部分。
| 归档时间: |
|
| 查看次数: |
302277 次 |
| 最近记录: |