Contents

限制UNIX中的进程消耗资源

1. 概述

在我们的 Linux 系统上运行多个进程需要它们共享 CPU、RAM 和磁盘空间等资源。

为了避免资源匮乏的进程导致系统冻结,我们可能希望限制其他进程消耗的资源。

在本教程中,我们将学习如何添加有关 CPU、RAM 和磁盘空间的约束。我们将使用ulimitcgroupssystemd-runionicenice等命令。在某些情况下,这些命令需要超级用户权限。

2. 使用systemd运行

systemd-run是大多数 Linux 发行版中直接可用的系统和服务管理器。它处理启动和运行进程、服务和守护进程。

该工具允许我们直接向要运行的特定进程添加限制

例如,我们可以启动一个具有 1GB RAM 限制的进程:

$ systemd-run --scope -p MemoryLimit=1000M ./myProcess.sh

此处,myProcess.sh已由systemd-run启动,只能访问指定的 RAM。

我们还可以使用它来强制进程仅使用最大百分比的 CPU:

$ systemd-run --scope -p CPUQuota=10% ./myProcess.sh

systemd-run允许我们结合 CPU 和内存限制:

$ systemd-run --scope -p MemoryLimit=1000M -p CPUQuota=10% ./myProcess.sh

3. 使用ulimit

ulimit允许我们覆盖或创建对 RAM 和磁盘空间的限制。它是 bash shell 的内置函数。

3.1. 硬限制和软限制

系统中的所有用户都有一个硬性限制,由管理员设置。然后,有一个软限制,每个用户都可以自己设置。这些限制在更改之前一直有效。

让我们为当前用户设置 1GB RAM 软限制:

$ ulimit -Sv 1000000

现在,我们运行的任何进程都只能使用 1GB 的 RAM。此限制将影响我们启动的所有未来进程,但如果我们愿意,我们可以在运行下一个进程之前更改它。

使用 ulimit,我们还可以对进程创建的最大文件大小添加限制,从而限制使用的磁盘空间:

$ ulimit -Sf 2000

现在,我们的用户启动的所有进程将只能创建或复制最大为 2MB 的文件。我们应该注意,此限制是基于每个文件的。

我们还可以组合限制:

$ ulimit -Sv 1000000 -Sf 2000 && ./myProcess.sh

在这里,我们设置了 RAM 和文件大小的软限制,并立即启动了myProcess.sh

3.2. 如何恢复使用ulimit设置的限制

任何用户都可以更改他们的软限制。不过,总体硬限制仍然优先。但是,只有管理员可以更改硬限制。

通常,我们以数字方式提供ulimit的限制。但是,要重置ulimit限制,我们可以使用关键字unlimited。假设我们设置了一些限制:

$ ulimit -Sv 1000000 -Sf 2000
$ ulimit -a
...
file size               (blocks, -f) 2000
...
virtual memory          (kbytes, -v) 1000000
...

要删除它们,我们设置一个无限制的限制:

$ ulimit -Sv unlimited -Sf unlimited 
$ ulimit -a 
...
file size (blocks, -f) unlimited 
...
virtual memory (kbytes, -v) unlimited 
...

4. 使用cpulimit

cpulimit 可以限制新进程或现有进程的 CPU 使用率。这个工具通常在 Ubuntu 中可用。如果我们使用不同的发行版,我们可能需要获取它:

$ sudo apt-get install cpulimit

4.1. 如何在新流程中使用它

我们可以在应用 CPU 限制的情况下直接启动新进程:

$ cpulimit -l 20 firefox
Process 5202 detected

在这里,我们启动了一个新进程——Firefox 浏览器——CPU 限制为 20%。

4.2. 如何在现有流程中使用它

要限制已在运行的进程,我们必须提供进程的名称以供cpulimit查找:

$ firefox &
$ cpulimit -e firefox -l 30
Process 2666 detected

这里 cpulimit检测到PID为2666的firefox进程,将CPU百分比限制在30%。

5. 使用ionice

**ionice 允许我们限制何时允许程序使用系统磁盘 IO。**目前, ionice的调度只有四种类型或类别:0、1、2 和 3。要启动一个空闲进程,这意味着它不会使用磁盘 IO,除非自上次以来已经过了一定的宽限期磁盘IO请求,我们使用class 3:

$ ionice --class 3 ./myProcess.sh

使用 class 1,我们可以设置一个进程在磁盘 IO 上具有最高优先级:

$ ionice --class 1 ./myProcess.sh

**我们在使用 1 类启动进程时需要小心,**因为它没有考虑启动时磁盘上发生的事情。那时我们可能会遇到其他正在使用磁盘 IO 的进程的一些问题。

6. 使用cgroups

控制组(也称为cgroups)是我们可以用来应用于我们的进程的限制组。我们可以将它们视为我们的流程必须通过的资源限制过滤器。

当我们通过cgroup运行进程时,将应用该组的所有限制。

6.1. 我们如何得到它?

有可能某些cgroups命令在我们的发行版中不可用,因此我们可能首先需要安装它们:

$ apt-get install cgroup-tools

6.2. 创建群组

我们现在将使用*cgcreate 命令创建两个cgroups*,我们将使用它们来运行一些进程:

$ sudo cgcreate -t $USER:$USER -a $USER:$USER -g cpu:/cpunormalusage
$ sudo cgcreate -t $USER:$USER -a $USER:$USER -g cpu:/cpulowusage

在这里,我们创建了两个cgroup分配给我们的用户。为简单起见,我们将*$USER*用作用户和组,但如果我们愿意,可以将第二次出现的值替换为组。

现在,我们需要为我们的cgroup添加限制:

$ cgset -r cpu.shares=512 cpulowusage

在这里,我们使用cgsetcpulowusage组的共享数设置 为 512 。当我们创建一个cgroup时,它默认有 1024 个共享。一个组拥有的份额越多,它可以从 CPU 获得的资源就越多。

6.3. 启动进程

要在cgroup中启动进程,我们使用cgexec 命令:

$ cgexec -g cpu:cpulowusage firefox &

运行上述命令将在后台启动控制组中的firefox 。因为它没有任何其他cgroup可以与之竞争,所以它可以访问所有 CPU。如果我们在控制组中启动另一个进程,它将通过份额数量与它竞争。

6.4. 限制内存

到目前为止,我们已经使用cgroups来限制 CPU。我们还可以限制 RAM 使用。为此,我们将创建一个新的cgroup并覆盖一个名为memory.limit_in_bytes的属性。

然后,我们将结合使用echotee来为该组写入一个值到属性中:

$ sudo cgcreate -g memory:/memoryLimitGroup
$ echo $((1000000000)) | sudo tee /sys/fs/cgroup/memory/memoryLimitGroup/memory.limit_in_bytes

在我们的例子中,我们将限制设置为 1GB。 如果我们也想限制交换空间,我们覆盖memory.memsw.limit_in_bytes属性:

$ echo $((1000000000)) | sudo tee /sys/fs/cgroup/memory/memoryLimitGroup/memory.memsw.limit_in_bytes

这会为我们的限制组增加 1GB 的交换空间。 现在,我们可以在该控制组的后台启动一个进程:

$ cgexec -g memory:memoryLimitGroup firefox &

7. 进程调度操作

7.1. 使用nice

niceness的*范围从 -20 到 19,显示了一个进程被 CPU 选择运行的潜力。我们可以在启动进程之前设置这个值:

$ nice -n 19 ./myProcess.sh

在这里,我们将niceness设置为 19 并启动了myProcessniceness越高,调度程序对进程的需求越少,因此当其他进程需要 CPU 时,该进程将获得较少的 CPU 时间。

7.2. 使用renice

要在进程运行时更改它的 niceness 值,我们使用一个名为renice的工具:

$ ./myProcess.sh && ps aux | grep -i 'myProcess'
blogdemo-reader       20935  0.0  0.0   6376  2420 pts/0    S+   10:16   0:00 grep --color=auto -i myProcess
$ renice -n 19 20935

在上面的示例中,我们启动了一个名为myProcess 的进程,获取了它的 PID,并将其niceness值更改为 19。