限制UNIX中的进程消耗资源
1. 概述
在我们的 Linux 系统上运行多个进程需要它们共享 CPU、RAM 和磁盘空间等资源。
为了避免资源匮乏的进程导致系统冻结,我们可能希望限制其他进程消耗的资源。
在本教程中,我们将学习如何添加有关 CPU、RAM 和磁盘空间的约束。我们将使用ulimit、cgroups 、systemd-run 、ionice和nice等命令。在某些情况下,这些命令需要超级用户权限。
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
在这里,我们使用cgset 将cpulowusage组的共享数设置 为 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的属性。
然后,我们将结合使用echo和tee来为该组写入一个值到属性中:
$ 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 并启动了myProcess。niceness越高,调度程序对进程的需求越少,因此当其他进程需要 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。