如何从linux调度程序屏蔽cpu(防止它将线程调度到该cpu)?

时间:2012-06-20 01:56:49

标签: linux performance real-time scheduling

可以使用sched_setaffinity将线程固定到cpu上,从而提高性能(在某些情况下)

来自linux手册页:

  

限制进程在单个CPU上运行也可以避免   a时发生的缓存失效导致的性能开销   进程停止在一个CPU上执行,然后重新开始执行   一个不同的CPU

此外,如果我想要更实时的响应,我可以将该线程的调度程序策略更改为SCHED_FIFO,并将优先级更高为某个高值(最多sched_get_priority_max),这意味着有问题的线程应该总是抢占其cpu上运行的任何其他线程。

但是,此时,在实时线程刚刚抢占的cpu上运行的线程可能已经驱逐了大部分实时线程的一级缓存条目。

我的问题如下:

  1. 是否可以阻止调度程序将任何线程调度到给定的cpu上? (例如:要么完全从调度程序中隐藏cpu,要么以其他方式隐藏)
  2. 是否有一些绝对必须能够在该cpu上运行的线程? (例如:内核线程/中断线程)
  3. 如果我需要在该cpu上运行内核线程,那么使用什么是合理的最大优先级值,这样我就不会匮乏内核线程?

4 个答案:

答案 0 :(得分:35)

答案是使用cpusetspython cpuset utility可以轻松配置它们。

基本概念

3 cpusets

  • root:出现在所有配置中并包含所有cpus(非屏蔽
  • system:包含用于系统任务的cpus - 需要运行但不“重要”的(非屏蔽
  • user:包含用于“重要”任务的cpus - 我们想要以“实时”模式运行的任务(屏蔽

shield命令管理这3个cpusets。

在设置过程中,它将所有可移动任务移动到未屏蔽的cpuset(system)中,并在拆解过程中将所有可移动任务移动到root cpuset中。 设置完成后,子命令允许您将任务移动到 shield user)cpuset中,此外,还可以将特殊任务(内核线程)从root移动到{{1} }(因此超出system cpuset)。

<强>命令:

首先我们创造一个盾牌。当然,屏蔽的布局将取决于机器/任务。例如,假设我们有一个4核非NUMA机器:我们希望将 3个内核专用于屏蔽,并将 1个内核用于不重要的任务;因为它是非NUMA,我们不需要指定任何内存节点参数,我们让内核线程在user cpuset中运行(即:跨所有cpu)

root

某些内核线程(未绑定到特定cpus的线程)可以移动到$ cset shield --cpu 1-3 cpuset中。 (通常,移动已绑定到特定cpu的内核线程并不是一个好主意)

system

现在让我们列出盾牌($ cset shield --kthread on )或未屏蔽(user)cpusets中运行的内容:( system代表详细信息,列出进程名称)(添加第二个{ {1}}显示超过80个字符)

-v

如果我们想要停止盾牌(拆解)

-v

现在让我们在屏蔽中执行一个进程($ cset shield --shield -v $ cset shield --unshield -v -v 之后的命令被传递给要执行的命令,而不是$ cset shield --reset

'--'

如果我们已经有一个正在运行的进程,我们想要进入屏蔽(注意我们可以通过传递逗号分隔列表或范围来移动多个进程(范围内的任何进程都将移动,即使存在间隙) )

cset

高级概念

$ cset shield --exec mycommand -- -arg1 -arg2 - 这些可以让你更好地控制cpusets

设置

创建,调整,重命名,移动和销毁cpusets

命令

使用cpus 1-3创建一个cpuset,使用NUMA节点1并将其命名为“my_cpuset1”

$ cset shield --shield --pid 1234
$ cset shield --shield --pid 1234,1236
$ cset shield --shield --pid 1234,1237,1238-1240

将“my_cpuset1”更改为仅使用cpus 1和3

cset set/proc

销毁cpuset

$ cset set --cpu=1-3 --mem=1 --set=my_cpuset1

重命名现有的cpuset

$ cset set --cpu=1,3 --mem=1 --set=my_cpuset1

创建分层cpuset

$ cset set --destroy --set=my_cpuset1

列出现有的cpusets(级别1的深度)

$ cset set --set=my_cpuset1 --newname=your_cpuset1

列出现有的cpuset及其子级

$ cset set --cpu=3 --mem=1 --set=my_cpuset1/my_subset1

列出所有现有的cpusets

$ cset set --list

<强> PROC

管理线程和流程

命令

列出在cpuset中运行的任务

$ cset set --list --set=my_cpuset1

在cpuset中执行任务

$ cset set --list --recurse

移动任务

$ cset proc --list --set=my_cpuset1 --verbose

移动任务及其所有兄弟姐妹

$ cset proc --set=my_cpuset1 --exec myApp -- --arg1 --arg2

将所有任务从一个cpuset移动到另一个cpuset

$ cset proc --toset=my_cpuset1 --move --pid 1234
$ cset proc --toset=my_cpuset1 --move --pid 1234,1236
$ cset proc --toset=my_cpuset1 --move --pid 1238-1340

将未固定的内核线程移动到cpuset

$ cset proc --move --toset=my_cpuset1 --pid 1234 --threads

强行将内核线程(包括那些固定到特定cpu的线程)移动到cpuset中(注意:这可能会给系统带来可怕的后果 - 确保你知道你在做什么)

$ cset proc --move --fromset=my_cpuset1 --toset=system

层次结构示例

我们可以使用分层cpusets来创建优先级分组

  1. 使用1 cpu(0)
  2. 创建$ cset proc --kthread --fromset=root --toset=system cpuset
  3. 使用1 cpu(1)
  4. 创建$ cset proc --kthread --fromset=root --toset=system --force cpuset
  5. 使用2 cpus(1-2)
  6. 创建system cpuset
  7. 使用3 cpus(1-3)
  8. 创建prio_low cpuset
  9. 使用全部4个cpus(0-3)创建一个prio_met cpuset(注意这与root相同;保持与root分离是个好习惯)
  10. 要实现上述目的,请创建prio_all,然后在prio_all等下创建子集prio_high

    prio_high

答案 1 :(得分:5)

我还有另外两种方法可以考虑这样做(虽然不如cset那么优雅,但似乎没有Redhat的支持程度):

1)任务集包括PID 1在内的所有内容 - 简单易行(但是,alledgly - 我自己从未见过任何问题 - 可能会导致调度程序效率低下)。下面的脚本(必须以root身份运行)在所有正在运行的进程上运行taskset,包括init(pid 1);这会将所有正在运行的进程固定到一个或多个“垃圾核心”,并且通过固定init,它将确保任何未来的进程也在“垃圾核心”列表中启动:

#!/bin/bash

if [[ -z $1 ]]; then
  printf "Usage: %s '<csv list of cores to set as junk in double quotes>'", $0
  exit -1;
fi

for i in `ps -eLfad |awk '{ print $4 } '|grep -v PID | xargs echo `; do 
   taskset -pc $1 $i;
done

2)使用isolcpus内核参数(这里是https://www.kernel.org/doc/Documentation/kernel-parameters.txt的文档):

isolcpus=   [KNL,SMP] Isolate CPUs from the general scheduler.
            Format:
            <cpu number>,...,<cpu number>
            or
            <cpu number>-<cpu number>
            (must be a positive range in ascending order)
            or a mixture
            <cpu number>,...,<cpu number>-<cpu number>

        This option can be used to specify one or more CPUs
        to isolate from the general SMP balancing and scheduling
        algorithms. You can move a process onto or off an
        "isolated" CPU via the CPU affinity syscalls or cpuset.
        <cpu number> begins at 0 and the maximum value is
        "number of CPUs in system - 1".

        This option is the preferred way to isolate CPUs. The
        alternative -- manually setting the CPU mask of all
        tasks in the system -- can cause problems and
        suboptimal load balancer performance.

我已经将这两个加上cset机制用于几个项目(顺便说一句,请原谅公然的自我推销:-)),我刚刚为一个名为Pontus Vision ThreadManager的工具申请了专利任何给定x86平台对任何给定软件工作负载的最佳锁定策略;在客户站点测试之后,我得到了非常好的结果(峰值延迟减少了270%),因此非常值得进行固定和CPU隔离。

答案 2 :(得分:0)

这是使用cgroups的老式方法。我有一台Fedora 28机器,而RedHat / Fedora希望您使用systemd-run,但是我无法在其中找到此功能。如果有人愿意启发我,我很想知道如何使用systemd-run来做到这一点。

假设我要从调度中排除我的第四个CPU(CPU 0-3),并将所有现有进程移至CPU 0-2。然后,我想单独将一个进程放在CPU 3上。

sudo su -
cgcreate -g cpuset:not_cpu_3
echo 0-2 > /sys/fs/cgroup/cpuset/not_cpu_3/cpuset.cpus
# This "0" is the memory node. See https://utcc.utoronto.ca/~cks/space/blog/linux/NUMAMemoryInfo
# for more information *
echo 0 > /sys/fs/cgroup/cpuset/not_cpu_3/cpuset.mems
  • 具体来说,您需要在计算机上查看/proc/zoneinfo/sys/devices/system/node的层次结构。留给读者正确的节点信息。

现在我们有了cgroup,我们需要创建隔离的CPU 3 cgroup:

cgcreate -g cpuset:cpu_3
echo 3 > /sys/fs/cgroup/cpuset/cpu_3/cpuset.cpus
# Again, the memory node(s) you want to specify.
echo 0 > /sys/fs/cgroup/cpuset/cpu_3/cpuset.mems

将所有进程/线程放在not_cpu_3 cgroup上:

for pid in $(ps -eLo pid) ; do cgclassify -g cpuset:not_cpu_3 $pid; done

评论:

ps -eL k psr o psr,pid,tid,args | sort | cut -c -80

注意!当前处于睡眠状态的进程将不会移动。必须将它们唤醒,以便调度程序将它们放置在其他CPU上。要看到这一点,请在上面的列表中选择您喜欢的睡眠过程-例如Web浏览器,您认为该过程应该在CPU 0-2上,但仍在3上。使用上面列表中的线程ID,执行: / p>

kill -CONT <thread_id>

示例

kill -CONT 9812

重新运行ps命令,并注意它已移至另一个CPU。

请注意!某些内核线程无法并且不会移动!例如,您可能会注意到,每个CPU都有一个内核线程[kthreadd]。将进程分配给cgroup仅适用于用户空间进程,不适用于内核线程。这就是多任务世界中的生活。

现在将一个进程及其所有子进程移至控制组cpu_3:

pid=12566 # for example
cgclassify -g cpuset:cpu_3 $pid
taskset -c -p 3 $pid

同样,如果$pid处于睡眠状态,则需要唤醒它以使CPU移动真正发生。

要撤消所有这些操作,只需删除您创建的cgroup。每个人都会回到根cgroup中:

cgdelete -r cpuset:cpu_3
cgdelete -r cpuset:not_cpu_3

无需重启。

(对不起,我不明白原始海报中的第三个问题。我无法对此发表评论。)

答案 3 :(得分:0)

如果您使用的是rhel实例,则可以为此使用Tuna(也可以用于其他Linux发行版,但不确定如何)。可以从yum命令轻松安装。金枪鱼可用于隔离CPU核心,它可以将在该特定CPU中运行的进程动态移至相邻的CPU。隔离CPU核心的命令如下,

company_good_id

您可以使用# tuna --cpus=CPU-LIST --isolate来查看金枪鱼如何实时隔离CPU核心。