进程友好性(优先级)设置对 Linux 没有影响



我写了一个测试程序,它只包含一个无限循环,其中一些内部计算,并且不执行I/O 操作。我尝试启动该程序的两个实例,其中一个实例很高友好值,另一个具有低友好值:

sudo nice -n 19 taskset 1 ./test
sudo nice -n -20 taskset 1 ./test

任务集命令确保两个程序在同一内核上执行。与我的预期相反,顶级报道称这两个程序都获得了大约 50% 的计算时间。为什么?漂亮的命令甚至有效果吗?

您看到的行为几乎可以肯定是因为 Linux 2.6.38(2010 年)中添加的自动分组功能。大概当您描述运行这两个命令时,它们是在不同的终端窗口中运行的。如果您在同一终端窗口中运行它们,那么您应该已经看到不错的值产生了效果。这个答案的其余部分详细阐述了这个故事。

内核提供一种称为自动分组的功能,以在面对多进程、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)创建)组成任务组。每个新的自动组都是因此是一个单独的任务组。

在组调度下,线程的良好值对仅相对于同一线程中的其他线程调度决策任务组。这在传统语义在 UNIX 系统上很有价值。特别是,如果启用了自动分组(这是各种 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(高优先级)。

自动分组 nice 设置与进程具有相同的含义不错的值,但适用于将 CPU 周期分配给自动分组作为一个整体,基于其他的相对不错的值自动组。对于自动组内的进程,CPU 循环接收将是自动组的良好价值的乘积(比较到其他自动组)和流程的良好价值(与同一自动组中的其他进程)。

我整理了一个test.c,它只是:

for(;;)
   {
   }

然后用你的好人跑它。 我没有为每个运行不同的 sudo,而是运行一个交互式 shell,然后从那里运行它们。 我用了两个&。

我得到了一个 ./test 重击我的 CPU,一个几乎没有碰到它。

当然,该系统仍然感觉响应迅速;现代处理器上需要大量的CPU占用进程才能获得如此多的负载,你可以"感觉到"它。

这与I/O占用进程和内存占用进程形成鲜明对比;在这些情况下,单个贪婪进程会使系统难以使用。

我猜要么你的系统有一个相对独特的优先级相关错误(或微妙),要么你的方法有问题。

我在 Ubuntu 11.04 系统上运行了测试。

我假设命令行末尾缺少一个&。 否则,在第一行完成之前,第二行不会运行。

当两个进程都运行时,请使用类似 top 的内容,并确保它们都具有您分配的良好值。

如果仅使用taskset启动进程,然后在进程运行后使用 renice 调整其优先级,会发生什么情况?

进程的美观性(优先级)设置对Linux有影响! (在实践中,但前提是你给它足够的工作去做!)

在我的系统上,只要所有内核都满载,那么 nice 确实会产生影响。在 ubuntu 14.04 上,与运行的进程相比,使用 nice -N 运行的进程可以通过 0.807 ** N 个操作而不改变 nice 值(假设您为每个 nice 级别为每个内核运行一个实例)。

就我而言,我关闭了超线程的四核 i7,因此如果我运行四个或更少的进程,那么它们的好值是什么并不重要 - 它们每个都得到一个完整的核心。如果我在不错的级别 0 和在不错的级别 12 运行 4 个进程,那么级别 12 的进程通过 0.807 ^ 12,即大约 7% 的工作是零级的。该比率似乎是从良好级别 0 到 14 的合理预测因子,之后它会波动(例如,一些运行具有不错的级别 18 处理量大于良好级别 16) - 运行测试的时间更长可能会使结果平滑。

(使用 Ruby 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

(纯粹主义者会注意到我正在混合红宝石、贝壳和 dc - 他们将不得不原谅我上个世纪的旧习惯

,通过;)

我从APUE运行了一个示例程序,nice确实有效果。

示例程序主要fork子程序,父程序和子程序在给定时间(10s)内执行i++增量操作。通过给孩子不同的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

测试程序(我做了一些修改),来自 APUE 第 8.16 节:

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 = %lldn", 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 = %dn", 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 %dn",
          nice(0), adj);
        errno = 0;
        if ((ret = nice(adj)) == -1 && errno != 0)
            err_sys("child set scheduling priority");
        printf("now child nice value is %dn", ret);
    } else {        /* parent */
        s = "parent";
        printf("current nice value in parent is %dn", nice(0));
    }
    for(;;) {
        if (++count == 0)
            err_quit("%s counter wrap", s);
        checktime(s);
    }
}

完整的源代码链接:https://wandbox.org/permlink/8iryAZ48sIbaq27y

最新更新