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

Ste*_*mer 22 linux performance scheduling real-time

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

从linux手册页:

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

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

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

我的问题如下:

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

Ste*_*mer 37

答案是使用cpusets.该蟒蛇cpuset工具可以很容易地配置它们.

基本概念

3个cpusets

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

shield命令管理这3个cpusets.

在设置过程中,它将所有可移动任务移动到未屏蔽的cpuset(system)中,并在拆解过程中将所有可移动任务移动到rootcpuset中.设置完成后,子命令允许您将任务移动到shield(user)cpuset中,此外,还可以将特殊任务(内核线程)移动rootsystem(因此也不会移出usercpuset).

命令:

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

$ cset shield --cpu 1-3
Run Code Online (Sandbox Code Playgroud)

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

$ cset shield --kthread on
Run Code Online (Sandbox Code Playgroud)

现在让我们列出在shield(user)或unshielded(system)cpusets中运行的内容:( -v对于详细列出进程名称的详细信息)(添加第二个-v以显示超过80个字符)

$ cset shield --shield -v
$ cset shield --unshield -v -v
Run Code Online (Sandbox Code Playgroud)

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

$ cset shield --reset
Run Code Online (Sandbox Code Playgroud)

现在,让我们在屏蔽执行的处理(下面的命令'--'被传递到命令被执行,而不是cset)

$ cset shield --exec mycommand -- -arg1 -arg2
Run Code Online (Sandbox Code Playgroud)

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

$ cset shield --shield --pid 1234
$ cset shield --shield --pid 1234,1236
$ cset shield --shield --pid 1234,1237,1238-1240
Run Code Online (Sandbox Code Playgroud)

先进的理念

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

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

命令

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

$ cset set --cpu=1-3 --mem=1 --set=my_cpuset1
Run Code Online (Sandbox Code Playgroud)

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

$ cset set --cpu=1,3 --mem=1 --set=my_cpuset1
Run Code Online (Sandbox Code Playgroud)

销毁cpuset

$ cset set --destroy --set=my_cpuset1
Run Code Online (Sandbox Code Playgroud)

重命名现有的cpuset

$ cset set --set=my_cpuset1 --newname=your_cpuset1
Run Code Online (Sandbox Code Playgroud)

创建分层cpuset

$ cset set --cpu=3 --mem=1 --set=my_cpuset1/my_subset1
Run Code Online (Sandbox Code Playgroud)

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

$ cset set --list
Run Code Online (Sandbox Code Playgroud)

列出现有的cpuset及其子代

$ cset set --list --set=my_cpuset1
Run Code Online (Sandbox Code Playgroud)

列出所有现有的cpusets

$ cset set --list --recurse
Run Code Online (Sandbox Code Playgroud)

PROC

管理线程和流程

命令

列出在cpuset中运行的任务

$ cset proc --list --set=my_cpuset1 --verbose
Run Code Online (Sandbox Code Playgroud)

在cpuset中执行任务

$ cset proc --set=my_cpuset1 --exec myApp -- --arg1 --arg2
Run Code Online (Sandbox Code Playgroud)

移动任务

$ 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
Run Code Online (Sandbox Code Playgroud)

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

$ cset proc --move --toset=my_cpuset1 --pid 1234 --threads
Run Code Online (Sandbox Code Playgroud)

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

$ cset proc --move --fromset=my_cpuset1 --toset=system
Run Code Online (Sandbox Code Playgroud)

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

$ cset proc --kthread --fromset=root --toset=system
Run Code Online (Sandbox Code Playgroud)

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

$ cset proc --kthread --fromset=root --toset=system --force
Run Code Online (Sandbox Code Playgroud)

层次结构示例

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

  1. system用1 cpu(0)创建一个cpuset
  2. prio_low用1个cpu 创建一个cpuset(1)
  3. prio_met用2 cpus 创建一个cpuset(1-2)
  4. 创建一个prio_high3 cpu的cpuset(1-3)
  5. 创建一个prio_all包含所有4 个cpu(0-3)的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
Run Code Online (Sandbox Code Playgroud)


Leo*_*Leo 6

还有另外两种方法我可以想到这样做(虽然不像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
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

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