我写了一个测试程序,它只包含一个无限循环,里面有一些计算,并且不执行 I/O 操作。我尝试启动程序的两个实例,一个具有高 niceness 值,另一个具有低 niceness 值:
sudo nice -n 19 taskset 1 ./test
sudo nice -n -20 taskset 1 ./test
taskset 命令确保两个程序在同一个内核上执行。与我的预期相反,top 报告说这两个程序都获得了大约 50% 的计算时间。这是为什么?nice 命令甚至有效果吗?
我写了一个测试程序,它只包含一个无限循环,里面有一些计算,并且不执行 I/O 操作。我尝试启动程序的两个实例,一个具有高 niceness 值,另一个具有低 niceness 值:
sudo nice -n 19 taskset 1 ./test
sudo nice -n -20 taskset 1 ./test
taskset 命令确保两个程序在同一个内核上执行。与我的预期相反,top 报告说这两个程序都获得了大约 50% 的计算时间。这是为什么?nice 命令甚至有效果吗?
您看到的行为几乎可以肯定是由于 Linux 2.6.38(2010 年)中添加的自动组功能。大概当您描述运行这两个命令时,它们是在不同的终端窗口中运行的。如果您在同一个终端窗口中运行它们,那么您应该已经看到 nice 值产生了效果。这个答案的其余部分详细说明了这个故事。
内核提供了一种称为自动分组的功能,以在面对多进程、CPU 密集型工作负载(例如使用大量并行构建进程(即make(1) -j
标志)构建 Linux 内核)时提高交互式桌面性能。
通过创建新会话时会创建一个新的自动组setsid(2)
;例如,当一个新的终端窗口启动时,就会发生这种情况。创建的新进程fork(2)
继承其父级的自动组成员身份。因此,会话中的所有进程都是同一个自动组的成员。
启用自动分组后,自动分组的所有成员都被放置在同一个内核调度程序“任务组”中。Linux 内核调度程序采用一种算法来均衡 CPU 周期在任务组之间的分布。这对于交互式桌面性能的好处可以通过以下示例进行描述。
假设有两个自动组竞争同一个 CPU(即假设一个 CPU 系统或使用taskset(1)
将所有进程限制到 SMP 系统上的同一个 CPU)。第一组包含来自内核构建的十个 CPU 绑定进程make -j10
. 另一个包含一个受 CPU 限制的进程:视频播放器。自动分组的效果是两个组将各自接收一半的 CPU 周期。也就是说,视频播放器将接收 50% 的 CPU 周期,而不是仅 9% 的周期,这可能会导致视频播放质量下降。SMP 系统上的情况更复杂,但总体效果是相同的:调度程序在任务组之间分配 CPU 周期,这样包含大量 CPU 绑定进程的自动组最终不会以牺牲 CPU 周期为代价系统上的其他作业。
不错的价值和组调度
在调度非实时进程(例如,在默认SCHED_OTHER
策略下调度的进程)时,调度器采用称为“组调度”的技术,在该技术下,线程被调度在“任务组”中。任务组是在各种情况下形成的,这里的相关案例是自动分组。
如果启用了自动分组,则(隐式)放置在自动组中的所有线程(即,由 创建的同一会话setsid(2)
)形成一个任务组。因此,每个新的自动组都是一个单独的任务组。
在组调度下,线程的 nice 值仅对与同一任务组中的其他线程相关的调度决策产生影响。就 UNIX 系统上 nice 值的传统语义而言,这会产生一些令人惊讶的后果。特别是,如果启用了自动分组(这是各种 Linux 发行版中的默认设置),则nice(1)
在进程上使用仅对相对于在同一会话中执行的其他进程(通常:同一终端窗口)中执行的其他进程的调度有影响。
相反,对于(例如)不同会话中唯一受 CPU 限制的进程的两个进程(例如,不同的终端窗口,其每个作业都绑定到不同的自动组),修改其中一个会话中进程的 nice 值对于调度程序相对于另一个会话中的进程的决策没有影响。这大概是您看到的场景,尽管您没有明确提到使用两个终端窗口。
如果您想防止自动分组干扰nice
此处描述的传统行为,您可以禁用该功能
echo 0 > /proc/sys/kernel/sched_autogroup_enabled
请注意,这也会导致禁用自动分组功能旨在提供的桌面交互性优势(见上文)。
自动分组不错的价值
可以通过文件查看进程的自动组成员身份/proc/[pid]/autogroup
:
$ cat /proc/1/autogroup
/autogroup-1 nice 0
此文件还可用于修改分配给自动组的 CPU 带宽。这是通过将“nice”范围内的数字写入文件来设置自动组的 nice 值来完成的。允许的范围是从 +19(低优先级)到 -20(高优先级)。
autogroup nice 设置与 process nice 值具有相同的含义,但适用于将 CPU 周期分配给整个 autogroup,基于其他 autogroup 的相对 nice 值。对于自动组内的进程,它接收的 CPU 周期将是自动组的 nice 值(与其他自动组相比)和进程的 nice 值(与同一自动组中的其他进程相比)的乘积。
我整理了一个 test.c ,它只是:
for(;;)
{
}
然后用你的nice运行它。我没有为每个人运行不同的 sudo,而是 sudo 了一个交互式 shell 并从那里运行它们。我用了两个&。
我得到一个 ./test 重击我的 CPU,而一个几乎没有碰到它。
自然,系统仍然感觉反应灵敏。在现代处理器上需要大量占用 CPU 的进程才能获得如此多的负载,您可以“感觉到”它。
这与 I/O 占用进程和内存占用进程形成鲜明对比。在这些情况下,单个贪婪进程可能会使系统难以使用。
我猜您的系统有一个相对独特的与优先级相关的错误(或微妙之处),或者您的方法有问题。
我在 Ubuntu 11.04 系统上进行了测试。
Process niceness (priority) 设置对 Linux 有影响! (在实践中,但前提是你给它足够的工作去做!)
在我的系统上,只要所有内核都满载,nice确实会产生影响。在 ubuntu 14.04 上,使用 nice -N 运行的进程与在不改变 nice 值的情况下运行的进程相比,运行了 0.807 ** N 次操作(假设您为每个 nice 级别运行每个核心一个实例)。
在我的情况下,我有关闭超线程的四核 i7,所以如果我运行四个或更少的进程,那么它们的好值是什么并不重要——它们每个都有一个完整的核心。如果我在 nice 级别 0 和 4 在 nice 级别 12 运行四个进程,那么级别 12 的进程通过 0.807 ^ 12,即大约 7% 的工作在 nice 级别 0 中。该比率似乎是从 nice 级别 0 到 14 的合理预测指标,之后它会波动(例如,几次运行的 nice 级别 18 处理超过 nice 16) - 运行更长时间的测试可能会使结果变得平滑。
(使用红宝石 2.1.2)
,cl 文件:
uptime
nices='-0 -6 -12 -18'
nices='-0 -18'
nices='-0 -2 -4 -6 -8 -10 -12 -14 -16 -18'
rm -f ,n-*
for i in 1 2 3 4
do
for n in $nices
do
nice $n ruby ,count_loops.rb > ,n${n}-$i &
done
done
ps -l
uptime
wait
uptime
ps -l
c=`cat ,n-0-[1234] | total`
last=$c
for n in $nices
do
echo
c2=`cat ,n${n}-[1234] | total`
echo total of `cat ,n${n}-[1234]` is $c2
echo -n "nice $n count $2, percentage: "
echo "3 k $c2 100 * $c / p" | dc
echo -n " percent of last: "
echo "3 k $c2 100 * $last / p" | dc
last=$c2
done
uptime
echo total count: `cat ,n-*-[1234] | total`
,count_loops.rb 文件
#!/usr/bin/env ruby
limit = Time.new + 70
i=0
while Time.new < limit
i += 1
j = 0
while (j += 1) < 10000
t = j
end
end
puts i
结果sh ,cl
- 初始诊断输出:
19:16:25 up 20:55, 2 users, load average: 3.58, 3.59, 2.88
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 4987 4977 0 80 0 - 7297 wait pts/3 00:00:00 bash
0 S 1000 11743 2936 0 80 0 - 2515 wait pts/3 00:00:00 rubymine.sh
0 S 1000 11808 11743 6 80 0 - 834604 futex_ pts/3 00:18:10 java
0 S 1000 11846 11808 0 80 0 - 4061 poll_s pts/3 00:00:02 fsnotifier64
0 S 1000 19613 4987 0 80 0 - 2515 wait pts/3 00:00:00 sh
0 R 1000 19616 19613 0 80 0 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19617 19613 0 82 2 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19618 19613 0 84 4 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19619 19613 0 86 6 - 7416 - pts/3 00:00:00 ruby
0 R 1000 19620 19613 0 88 8 - 6795 - pts/3 00:00:00 ruby
0 R 1000 19621 19613 0 90 10 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19622 19613 0 92 12 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19623 19613 0 94 14 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19624 19613 0 96 16 - 6078 - pts/3 00:00:00 ruby
0 R 1000 19625 19613 0 98 18 - 6012 - pts/3 00:00:00 ruby
0 R 1000 19626 19613 0 80 0 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19627 19613 0 82 2 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19628 19613 0 84 4 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19629 19613 0 86 6 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19630 19613 0 88 8 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19631 19613 0 90 10 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19632 19613 0 92 12 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19633 19613 0 94 14 - 6144 - pts/3 00:00:00 ruby
0 R 1000 19634 19613 0 96 16 - 4971 - pts/3 00:00:00 ruby
0 R 1000 19635 19613 0 98 18 - 4971 - pts/3 00:00:00 ruby
0 R 1000 19636 19613 0 80 0 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19637 19613 0 82 2 - 7449 - pts/3 00:00:00 ruby
0 R 1000 19638 19613 0 84 4 - 7344 - pts/3 00:00:00 ruby
0 R 1000 19639 19613 0 86 6 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19640 19613 0 88 8 - 7416 - pts/3 00:00:00 ruby
0 R 1000 19641 19613 0 90 10 - 6210 - pts/3 00:00:00 ruby
0 R 1000 19642 19613 0 92 12 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19643 19613 0 94 14 - 5976 - pts/3 00:00:00 ruby
0 R 1000 19644 19613 0 96 16 - 6111 - pts/3 00:00:00 ruby
0 R 1000 19645 19613 0 98 18 - 4971 - pts/3 00:00:00 ruby
0 R 1000 19646 19613 0 80 0 - 7582 - pts/3 00:00:00 ruby
0 R 1000 19647 19613 0 82 2 - 7516 - pts/3 00:00:00 ruby
0 R 1000 19648 19613 0 84 4 - 7416 - pts/3 00:00:00 ruby
0 R 1000 19649 19613 0 86 6 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19650 19613 0 88 8 - 6177 - pts/3 00:00:00 ruby
0 R 1000 19651 19613 0 90 10 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19652 19613 0 92 12 - 6078 - pts/3 00:00:00 ruby
0 R 1000 19653 19613 0 94 14 - 6247 - pts/3 00:00:00 ruby
0 R 1000 19654 19613 0 96 16 - 4971 - pts/3 00:00:00 ruby
0 R 1000 19655 19613 0 98 18 - 4971 - pts/3 00:00:00 ruby
0 R 1000 19656 19613 0 80 0 - 3908 - pts/3 00:00:00 ps
19:16:26 up 20:55, 2 users, load average: 3.58, 3.59, 2.88
19:17:37 up 20:56, 3 users, load average: 28.92, 11.25, 5.59
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 4987 4977 0 80 0 - 7297 wait pts/3 00:00:00 bash
0 S 1000 11743 2936 0 80 0 - 2515 wait pts/3 00:00:00 rubymine.sh
0 S 1000 11808 11743 6 80 0 - 834604 futex_ pts/3 00:18:10 java
0 S 1000 11846 11808 0 80 0 - 4061 poll_s pts/3 00:00:02 fsnotifier64
0 S 1000 19613 4987 0 80 0 - 2515 wait pts/3 00:00:00 sh
0 R 1000 19794 19613 0 80 0 - 3908 - pts/3 00:00:00 ps
结果sh ,cl
- 统计:(最后的百分比是此总数与最后一组进程的计数相比的百分比)
total of 99951 101725 100681 104046 is 406403
nice -0 count , percentage: 100.000
percent of last: 100.000
total of 64554 62971 64006 63462 is 254993
nice -2 count , percentage: 62.743
percent of last: 62.743
total of 42997 43041 43197 42717 is 171952
nice -4 count , percentage: 42.310
percent of last: 67.434
total of 26882 28250 27151 27244 is 109527
nice -6 count , percentage: 26.950
percent of last: 63.696
total of 17228 17189 17427 17769 is 69613
nice -8 count , percentage: 17.129
percent of last: 63.557
total of 10815 10792 11021 11307 is 43935
nice -10 count , percentage: 10.810
percent of last: 63.113
total of 7023 6923 7885 7323 is 29154
nice -12 count , percentage: 7.173
percent of last: 66.357
total of 5005 4881 4938 5159 is 19983
nice -14 count , percentage: 4.917
percent of last: 68.542
total of 3517 5537 3555 4092 is 16701
nice -16 count , percentage: 4.109
percent of last: 83.576
total of 4372 4307 5552 4527 is 18758
nice -18 count , percentage: 4.615
percent of last: 112.316
19:17:37 up 20:56, 3 users, load average: 28.92, 11.25, 5.59
total count: 1141019
(纯粹主义者会注意到我正在混合 ruby、shell 和 dc - 他们将不得不原谅我上个世纪的旧习惯;))
我假设&
命令行末尾缺少一个。否则,在第一行完成之前,第二行不会运行。
当两个进程都在运行时,使用类似的东西top
并确保它们每个都具有您分配的不错的值。
如果您仅使用启动进程taskset
,然后renice
在它们运行后调整它们的优先级,会发生什么情况?
我从APUE运行了一个示例程序并且nice
确实有效果。
示例程序主要是fork
一个孩子,父母和孩子都在i++
给定的时间(10s)内执行递增操作。通过给孩子不同的nice
价值,结果表明是否nice
有所作为。
这本书警告说我应该在单处理器 PC 上运行该程序,首先我尝试使用自己的 PC i5-7500 CPU @ 3.40GHz × 4
(4 核),给出不同的nice
值,几乎没有区别。
然后我登录到我的远程服务器1 processor 1 GB
,并获得预期的差异。
1 core processor 1 GB
测试结果:
./a.out
NZERO = 20
current nice value in parent is 0
current nice value in child is 0, adjusting by 0
now child nice value is 0
parent count = 13347219
child count = 13357561
./a.out 20 //child nice set to 20
NZERO = 20
current nice value in parent is 0
current nice value in child is 0, adjusting by 20
now child nice value is 19
parent count = 29770491
ubuntu@VM-0-2-ubuntu:~$ child count = 441330
测试程序(我做了一点修改),来自第 8.16 节,APUE:
apue.h
只是一个标头包装器
err_sys()
也是一个错误处理程序包装器,您可以printf
临时使用。
#include "apue.h"
#include <errno.h>
#include <sys/time.h>
#if defined(MACOS)
#include <sys/syslimits.h>
#elif defined(SOLARIS)
#include <limits.h>
#elif defined(BSD)
#include <sys/param.h>
#endif
unsigned long long count;
struct timeval end;
void
checktime(char *str)
{
struct timeval tv;
gettimeofday(&tv, NULL);
if (tv.tv_sec >= end.tv_sec && tv.tv_usec >= end.tv_usec) {
printf("%s count = %lld\n", str, count);
exit(0);
}
}
int
main(int argc, char *argv[])
{
pid_t pid;
char *s;
int nzero, ret;
int adj = 0;
setbuf(stdout, NULL);
#if defined(NZERO)
nzero = NZERO;
#elif defined(_SC_NZERO)
nzero = sysconf(_SC_NZERO);
#else
#error NZERO undefined
#endif
printf("NZERO = %d\n", nzero);
if (argc == 2)
adj = strtol(argv[1], NULL, 10);
gettimeofday(&end, NULL);
end.tv_sec += 10; /* run for 10 seconds */
if ((pid = fork()) < 0) {
err_sys("fork failed");
} else if (pid == 0) { /* child */
s = "child";
printf("current nice value in child is %d, adjusting by %d\n",
nice(0), adj);
errno = 0;
if ((ret = nice(adj)) == -1 && errno != 0)
err_sys("child set scheduling priority");
printf("now child nice value is %d\n", ret);
} else { /* parent */
s = "parent";
printf("current nice value in parent is %d\n", nice(0));
}
for(;;) {
if (++count == 0)
err_quit("%s counter wrap", s);
checktime(s);
}
}