我正在运行pdftoppm,将用户提供的PDF转换为300DPI图像。这非常有效,除非用户提供的PDF页面尺寸很大。 pdftoppm将分配足够的内存以在内存中保存该大小的300DPI图像,对于100平方英寸的页面而言,它是100 * 300 * 100 * 300 *每个像素4个字节= 3.5GB。恶意用户可能只会给我一个笨拙的PDF并引起各种问题。即将运行-如果进程试图分配超过500MB的内存,那就让进程终止。

我认为ulimit不能用于此,但是有一个等效的单进程吗?

评论

也许是码头工人?

另请参见:无法在GCE Debian Jessie实例上启用cgroup_enable = memory swapaccount = 1

unix.stackexchange.com/questions/28678/…

#1 楼

ulimit存在一些问题。这是关于此主题的有用读物:限制Linux中程序的时间和内存消耗,这会导致超时工具,该工具使您可以按时间或内存消耗来笼罩进程(及其分支)。

超时工具需要安装Perl 5+和/proc文件系统。之后,您将该工具复制到/usr/local/bin像这样:

或者,您也可以使用-t <seconds>-x <hertz>分别通过时间或CPU限制来限制进程。

此工具的工作方式是每秒检查多次(如果生成的进程)尚未超额认购其设定的界限。这意味着实际上存在一个很小的窗口,在此窗口中,进程可能会在超时通知和终止进程之前超额预订。 ,即使您使用的是Docker或runC,它们在cgroup周围提供了更加用户友好的抽象。

评论


似乎现在为我工作(再次?),但这是Google缓存版本:webcache.googleusercontent.com/…

–kvz
17年4月27日在12:32

我们可以将超时与任务集一起使用吗(我们需要限制内存和内核)?

– ransh
17-10-24在12:47

应该注意的是,这个答案不是指同名的linux标准coreutils实用程序!因此,如果您的系统上的任何地方,某个软件包的脚本都期望Linux标准的coreutils软件包超时,则答案可能很危险!我不知道此工具已被打包用于debian等发行版。

–user1404316
18年4月8日在7:03



-t 约束会在几秒钟后杀死进程吗?

–xxxxxx562
18年11月26日在2:05

-m接受千字节也可能会有帮助。上面的示例建议使用MB。

–丹尼尔(Daniel)
19年11月4日在13:34

#2 楼

限制此情况的另一种方法是使用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

将创建一个名为myGroup的控制组,用myGroup限制在memory.limit_in_bytes下运行的进程集,最多500 MB物理内存,以及与memory.memsw.limit_in_bytes一起最多5000 MB的物理和交换内存。
有关这些选项的更多信息,可以在这里找到:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-memory
要运行一个过程在控制组下:
cgexec -g memory:myGroup pdftoppm

请注意,在此现代发行版中,此示例需要安装cgroup-bin软件包并编辑/etc/default/grub,将GRUB_CMDLINE_LINUX_DEFAULT更改为:
GRUB_CMDLINE_LINUX_DEFAULT="cgroup_enable=memory swapaccount=1"

,然后运行sudo update-grub和重新启动以使用新的内核启动参数启动。

评论


firejail程序还可以让您启动一个具有内存限制的进程(使用cgroups和名称空间限制的不仅仅是内存)。在我的系统上,我无需更改内核命令行即可运行!

– Ned64
18年2月15日在12:20

您是否需要GRUB_CMDLINE_LINUX_DEFAULT修改才能使设置持久化?我找到了使它持久化的另一种方法。

–stason
18年8月5日在18:36



另请参阅:GRUB_CMDLINE_LINUX_DEFAULT中的swapaccount = 1有什么作用?

–马丁·托马
19年7月22日在11:04

在此答案中指出,在某些发行版(例如Ubuntu)上,sg是cgcreate所必需的,以后的命令也是必需的,除非已将权限授予当前用户。这将使读者不必在其他地方(例如askubuntu.com/questions/345055)找到此信息。我建议对此进行编辑,但被拒绝。

–stewbasic
19年7月23日在5:28

似乎现代的ubuntu需要cgroup-tools而不是cgroup-bin

– Ferroao
20 Jan 26 '17:38



#3 楼

如果您的进程没有产生更多消耗最多内存的子代,则可以使用setrlimit函数。为此,更常见的用户界面是使用Shell的ulimit命令:被调用的进程与其他进程共享的内存,以及映射但不保留的内存(例如Java的大堆)。尽管如此,虚拟内存仍然是非常大的进程的最接近近似值,因此上述错误无关紧要。

如果您的程序产生了子代,并且由它们来分配内存,则子代会变得更加复杂,因此您应该编写辅助脚本以在您的控制下运行进程。我在博客中写道,为什么以及如何。

评论


为什么setrlimit对于更多的孩子来说比较复杂? man setrlimit告诉我,“通过fork(2)创建的子进程继承了其父级资源限制。资源限制在execve(2)中保留”

– akira
2011-2-13在8:13

因为内核不对所有子进程的虚拟机大小求和;如果这样做的话,反正还是会得到错误的答案。限制是每个进程,是虚拟地址空间,而不是内存使用率。内存使用情况更难衡量。

– MarkR
2011-2-13在8:17

如果我正确理解了这个问题,那么OP每个子进程(子进程)的限制是多少。

– akira
2011-2-13在8:21

只是想说谢谢-这种ulimit方法帮助我解决了Firefox的错误622816-加载大图像可能会“冻结” firefox或使系统崩溃;在USB引导(从RAM)引导下,这往往会冻结操作系统,需要重新启动;现在,至少firefox崩溃了,操作系统还活着……干杯!

– sdaau
2013年4月4日15:51



软限制和硬限制是什么?

–李
16年8月29日在17:16

#4 楼

在任何基于systemd的发行版上,您还可以通过systemd-run间接使用cgroup。例如。对于将pdftoppm限制为500M RAM的情况,请使用:
 systemd-run --scope -p MemoryMax=500M pdftoppm
 

注意:这将要求您输入密码,但是应用会以您的用户身份启动。不要让这个让您误以为该命令需要sudo,因为这将导致该命令在root用户下运行,这不是您的本意。您是否需要密码来限制已经拥有的内存),可以使用--user选项,但是要实现此目的,您需要启用cgroupsv2支持,现在需要使用systemd.unified_cgroup_hierarchy内核参数启动。

评论


谢谢,让我开心

– Geradlus_RU
19-10-14在16:07

简短而甜美。在我尝试使用firejail之前,但这似乎过于适度,而且有太多的副作用,只能限制内存消耗。谢谢!

–安德烈·辛尼森(Andrei Sinitson)
19年12月13日在7:31

另一个注意事项:这是在后台使用cgroups,我认为用此命令执行此操作比在其他答案中建议的手动操作cgroups更有意义。这应该有更多的支持。

–安德烈·辛尼森(Andrei Sinitson)
19/12/13在7:33

这是IMO最简单,最明智的方法,谢谢

–skrat
20年4月7日在18:23

很好的答案。您是否知道有没有办法在超出限制后终止进程,而是限制最大内存使用量?

– EdiD
20年7月10日在11:52

#5 楼

我正在使用以下脚本,效果很好。它通过cgmanager使用cgroups。更新:现在使用来自cgroup-tools的命令。将此脚本命名为limitmem并将其放在$ PATH中,就可以像limitmem 100M bash一样使用它。这将限制内存和交换使用。要仅限制内存,请使用memory.memsw.limit_in_bytes删除该行。要启用交换使用限制,您需要在Linux系统上启用交换记帐。通过在swapaccount=1中设置/添加/etc/default/grub来做到这一点,使其看起来像

GRUB_CMDLINE_LINUX="swapaccount=1"


然后运行sudo update-grub并重新启动。如果cgroup-tools将来也会出现故障,请不要感到惊讶。正确的解决方案是使用systemd api进行cgroup管理,但没有用于该atm的命令行工具。

 #!/bin/sh

# This script uses commands from the cgroup-tools package. The cgroup-tools commands access the cgroup filesystem directly which is against the (new-ish) kernel's requirement that cgroups are managed by a single entity (which usually will be systemd). Additionally there is a v2 cgroup api in development which will probably replace the existing api at some point. So expect this script to break in the future. The correct way forward would be to use systemd's apis to create the cgroups, but afaik systemd currently (feb 2018) only exposes dbus apis for which there are no command line tools yet, and I didn't feel like writing those.

# strict mode: error if commands fail or if unset variables are used
set -eu

if [ "$#" -lt 2 ]
then
    echo Usage: `basename q4312079q` "<limit> <command>..."
    echo or: `basename q4312079q` "<memlimit> -s <swaplimit> <command>..."
    exit 1
fi

cgname="limitmem_$$"

# parse command line args and find limits

limit=""
swaplimit="$limit"
shift

if [ "" = "-s" ]
then
    shift
    swaplimit=""
    shift
fi

if [ "" = -- ]
then
    shift
fi

if [ "$limit" = "$swaplimit" ]
then
    memsw=0
    echo "limiting memory to $limit (cgroup $cgname) for command $@" >&2
else
    memsw=1
    echo "limiting memory to $limit and total virtual memory to $swaplimit (cgroup $cgname) for command $@" >&2
fi

# create cgroup
sudo cgcreate -g "memory:$cgname"
sudo cgset -r memory.limit_in_bytes="$limit" "$cgname"
bytes_limit=`cgget -g "memory:$cgname" | grep memory.limit_in_bytes | cut -d\  -f2`

# try also limiting swap usage, but this fails if the system has no swap
if sudo cgset -r memory.memsw.limit_in_bytes="$swaplimit" "$cgname"
then
    bytes_swap_limit=`cgget -g "memory:$cgname" | grep memory.memsw.limit_in_bytes | cut -d\  -f2`
else
    echo "failed to limit swap"
    memsw=0
fi

# create a waiting sudo'd process that will delete the cgroup once we're done. This prevents the user needing to enter their password to sudo again after the main command exists, which may take longer than sudo's timeout.
tmpdir=${XDG_RUNTIME_DIR:-$TMPDIR}
tmpdir=${tmpdir:-/tmp}
fifo="$tmpdir/limitmem_$$_cgroup_closer"
mkfifo --mode=u=rw,go= "$fifo"
sudo -b sh -c "head -c1 '$fifo' >/dev/null ; cgdelete -g 'memory:$cgname'"

# spawn subshell to run in the cgroup. If the command fails we still want to remove the cgroup so unset '-e'.
set +e
(
set -e
# move subshell into cgroup
sudo cgclassify -g "memory:$cgname" --sticky `sh -c 'echo $PPID'`  # $$ returns the main shell's pid, not this subshell's.
exec "$@"
)

# grab exit code 
exitcode=$?

set -e

# show memory usage summary

peak_mem=`cgget -g "memory:$cgname" | grep memory.max_usage_in_bytes | cut -d\  -f2`
failcount=`cgget -g "memory:$cgname" | grep memory.failcnt | cut -d\  -f2`
percent=`expr "$peak_mem" / \( "$bytes_limit" / 100 \)`

echo "peak memory used: $peak_mem ($percent%); exceeded limit $failcount times" >&2

if [ "$memsw" = 1 ]
then
    peak_swap=`cgget -g "memory:$cgname" | grep memory.memsw.max_usage_in_bytes | cut -d\  -f2`
    swap_failcount=`cgget -g "memory:$cgname" |grep memory.memsw.failcnt | cut -d\  -f2`
    swap_percent=`expr "$peak_swap" / \( "$bytes_swap_limit" / 100 \)`

    echo "peak virtual memory used: $peak_swap ($swap_percent%); exceeded limit $swap_failcount times" >&2
fi

# remove cgroup by sending a byte through the pipe
echo 1 > "$fifo"
rm "$fifo"

exit $exitcode
 


评论


调用cgmanager_create_sync失败:我尝试使用limitmem 100M进程名运行的每个进程的请求无效。我使用的是Xubuntu 16.04 LTS,该软件包已安装。

–亚伦·弗兰克(Aaron Franke)
17 Mar 12 '17 at 9:58



Ups,我收到此错误消息:$ limitmem 400M rstudio将命令rstudio的内存限制为400M(cgroup limitmem_24575)错误org.freedesktop.DBus.Error.InvalidArgs:无效的请求有什么主意?

– R Kiselev
18-2-15在7:19



@RKiselev cgmanager现在已弃用,甚至在Ubuntu 17.10中也不可用。它使用的systemd api在某些时候已更改,因此这可能是原因。我已经更新了脚本以使用cgroup-tools命令。

– JanKanis
18-2-15在11:39



如果百分比计算结果为零,则expr状态代码为1,并且此脚本过早退出。建议将行更改为:percent = $((“” peakpeak_mem“ / $((” $ bytes_limit“ / 100))))))(ref:unix.stackexchange.com/questions/63166/…)

–威廉·巴伦丁(Willi Ballenthin)
18年5月23日在22:46

如果我超出限制,如何配置cgroup以终止进程?

– d9ngle
19年5月1日在18:29

#6 楼

除了Mark Johnson建议的daemontools中的工具之外,您还可以考虑在chpst中找到的runit。 Runit本身捆绑在busybox中,因此您可能已经安装了它。

chpst的手册页显示了以下选项:


-m字节
限制内存。将数据段,堆栈段,锁定的物理页以及每个进程的所有段的总数限制为每个字节



#7 楼

我正在运行Ubuntu 18.04.2 LTS,JanKanis脚本对我不起作用,正如他所建议的那样。运行limitmem 100M script限制了100MB的RAM,并且没有无限交换。 />
# create cgroup
sudo cgcreate -g "memory:$cgname"
sudo cgset -r memory.limit_in_bytes="$limit" "$cgname"
sudo cgset -r memory.swappiness=0 "$cgname"
bytes_limit=`cgget -g "memory:$cgname" | grep memory.limit_in_bytes | cut -d\  -f2`


评论


@sourcejedi添加了它:)

– d9ngle
19年5月2日,12:59

是的,我编辑了答案。要启用交换限制,您需要在系统上启用交换记帐。这样做的运行时开销很小,因此默认情况下在Ubuntu上未启用它。看到我的编辑。

– JanKanis
19年5月2日在13:18

#8 楼

并不是所提出问题的真正答案,但是:

您可以检查文件大小以防止在尝试处理pdf之前出现问题吗?这样就可以消除“大得可笑”的问题。如何使用python /读取pdf文件),从而可以将pdf拆分为更易于管理的块。或两者都做:如果文件大小合理,则进行处理;否则(其他)将其拆分为所需的碎片,然后进行处理。然后可以重新组合输出。为了防止出现“边界”问题,各部分之间可能需要重叠一些。