如何屏蔽从Linux调度程序的CPU(防止调度到该CPU的线程)?

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

从Linux手册页:

限制进程在单个CPU上运行也可以避免由于某个进程在一个CPU上停止执行而发生高速caching失效而导致的性能开销,然后重新开始在不同的CPU上执行

此外,如果我希望获得更实时的响应,我可以将该线程的调度策略更改为SCHED_FIFO ,并将优先级更改为某个较高的值(最高为sched_get_priority_max ),这意味着所讨论的线程应始终sched_get_priority_max其他线程线程运行在它的CPU上,当它准备好了。

但是,此时,实时线程刚刚被抢占的cpu上运行的线程可能已经驱逐了实时线程的一级caching条目。

我的问题如下:

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

答案是使用cpusets 。 python cpuset实用程序可以很容易地配置它们。

基本概念

3 cpusets

  • root :存在于所有配置中,并包含所有cpus(未屏蔽
  • system :包含用于系统任务的cpus – 需要运行但不“重要”(未屏蔽
  • user :包含用于“重要”任务的cpus – 我们希望以“实时”模式运行( 屏蔽

shield命令管理这3个cpusets。

在安装过程中,它将所有可移动任务移动到未屏蔽的cpuset( system )中,在拆卸过程中,它将所有可移动任务移动到root cpuset中。 在设置之后,子命令可以让你将任务移动到shielduser )cpuset中,另外还可以将特殊任务(内核线程)从rootsystem (因此移出user cpuset)。

命令:

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

 $ 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

命令

创建一个cpuset,使用cpu 1-3,使用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移到另一个

 $ 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(1-2)创建一个prio_met cpuset
  4. 用3 cpus(1-3)创建一个prio_high cpuset
  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 

还有两种方法可以考虑这样做(虽然不像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的文档)&#xFF1A;

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