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

问题描述 投票:22回答:3

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

从linux手册页:

限制在单个CPU上运行的进程还可以避免在进程停止在一个CPU上执行然后重新开始在另一个CPU上执行时发生的高速缓存失效导致的性能成本

此外,如果我想要更实时的响应,我可以将该线程的调度程序策略更改为SCHED_FIFO,并将优先级更改为某个高值(最高为sched_get_priority_max),这意味着所讨论的线程应始终优先于其他任何其他线程线程在准备就绪时在其cpu上运行。

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

我的问题如下:

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

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

基本概念

3个cpusets

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

shield命令管理这3个cpusets。

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

命令:

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

$ cset shield --cpu 1-3

某些内核线程(未绑定到特定cpus的线程)可以移动到system cpuset中。 (一般来说,移动已绑定到特定cpu的内核线程并不是一个好主意)

$ cset shield --kthread on

现在让我们列出盾牌(user)或非屏蔽(system)cpusets中运行的内容:(-v for verbose,将列出进程名称)(添加第二个-v以显示超过80个字符)

$ cset shield --shield -v
$ cset shield --unshield -v -v

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

$ cset shield --reset

现在让我们在屏蔽中执行一个进程(将'--'之后的命令传递给要执行的命令,而不是cset

$ cset shield --exec mycommand -- -arg1 -arg2

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

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

先进的理念

cset set/proc - 这些可以让你更好地控制cpusets

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

命令

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

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

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

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

销毁cpuset

$ cset set --destroy --set=my_cpuset1

重命名现有的cpuset

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

创建分层cpuset

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

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

$ cset set --list

列出现有的cpuset及其子代

$ cset set --list --set=my_cpuset1

列出所有现有的cpusets

$ cset set --list --recurse

PROC

管理线程和流程

命令

列出在cpuset中运行的任务

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

在cpuset中执行任务

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

移动任务

$ 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

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

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

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

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

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

$ cset proc --kthread --fromset=root --toset=system

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

$ cset proc --kthread --fromset=root --toset=system --force

层次结构示例

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

  1. 用1 cpu(0)创建一个system cpuset
  2. 用1 cpu创建一个prio_low cpuset(1)
  3. 用2 cpus创建一个prio_met cpuset(1-2)
  4. 用3 cpus创建一个prio_high cpuset(1-3)
  5. 创建一个包含全部4个cpus(0-3)的prio_all cpuset(注意这与root相同;保持与root分离是个好习惯)

要实现上述目的,请创建prio_all,然后在prio_all等下创建子集prio_high

$ cset set --cpu=0 --set=system
$ cset set --cpu=0-3 --set=prio_all
$ cset set --cpu=1-3 --set=/prio_all/prio_high
$ cset set --cpu=1-2 --set=/prio_all/prio_high/prio_med
$ cset set --cpu=1 --set=/prio_all/prio_high/prio_med/prio_low

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隔离。


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 heirarchy。获取正确的节点信息留给读者练习。

现在我们有了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上。要看到这一点,请在上面的列表中选择您最喜欢的休眠过程 - 您认为应该在CPU 0-2上的过程,例如Web浏览器,但它仍然在3上。使用上面列表中的线程ID,执行:

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个问题。我不能对此发表评论。)

© www.soinside.com 2019 - 2024. All rights reserved.