CPU 实践 隔离 (cpu实验)

CPU 实践 隔离 (cpu实验)

SUSE Labs 团队探索了Kernel CPU 隔离及其核心组件之一:Full Dynticks(或Nohz Full),并撰写了本系列文章:

1.CPU 隔离– 简介

2.CPU 隔离– Full Dynticks 深探

3.CPU 隔离– Nohz_full

4.CPU 隔离– 管理和权衡

5.CPU 隔离– 实践

本文是第五篇。

我们通过前面四篇文章初步掌握了理论概念,现在终于到了实践阶段。本实践操作共配置了 8 个 CPU,将以完全隔离的方式在第 8 个 CPU 上运行一个无意义的用户空间循环,即:不受任何干扰。

内核配置要求

如果您运行的是 SUSE Linux Enterprise Server 15 SP3 ( 或更高版本,则无需担心这一问题;否则,一定要保证:

CONFIG_NO_HZ_FULL=y

CONFIG_CPUSETS=y

CONFIG_TRACING=y

第一条为在运行一个任务时停止Tick 提供支持。第二条使任务绑定设置更容易。第三个选项支持对 CPU 隔离进行调试的跟踪能力。

引导要求

使用“nohz_full=”引导参数,可以在运行单个任务时关闭计时器 Tick,并且大多数非内核负载也会迁移到隔离范围之外的 CPU。由于计划隔离第 8 个 CPU,我们需要通过以下信息引导内核:

nohz_full=7

CPU 编号从 0 开始,所以第 8 个 CPU 的编号为 7。此外,无需设置“rcu_nocbs=”引导参数,如示例中通常显示的那样,nohz_full 可自动调节该参数。

任务绑定

有多种方法可以在隔离的任务和系统其余部分之间划分 CPU,首选方法是使用 cpuset ( 。对于有特殊需求的人,还有其他解决方案可用。

一旦内核启动,为了确保无关任务不会干扰 CPU 7,我们创建两个 cpusets 分区。名为“isolation”的目录包含我们隔离的 CPU,它将来会运行隔离任务。另一个名为“housekeeping”的目录承担常规负载。我们强制禁用“isolation”分区的负载平衡,以确保任何任务都不能迁移进/出 CPU 7,除非手工移动。

在本例中,我们在SUSE Linux Enterprise Server 15 SP3 上使用默认的 cpuset 挂载点(。

cd /sys/fs/cgroup/cpuset

mkdir housekeeping

mkdir isolated

echo 0-6 > housekeeping/cpuset.cpus

echo 0 > housekeeping/cpuset.mems

echo 7 > isolated/cpuset.cpus

echo 0 > isolated/cpuset.memse

cho 0 > cpuset.sched_load_balance

echo 0 > isolated/cpuset.sched_load_balance

while read P

echo $P > housekeeping/cgroup.procs

done < cgroup.procs

对housekeeping/cgroup.procs 的一些写入操作可能会失败,因为内核线程 pid 无法移出根 cpuset 分区。然而,未绑定的内核线程会自动强制绑定 nohz_full 范围之外的 CPU,因此可以安全地忽略这些故障。

您还可以使用“isolcpus=” ( 内核引导参数实现与以上 cpuset 设置相同的设置。但我们不建议使用这种解决方案,因为以后无法在运行时更改隔离配置。因此,尽管“isolcpus” 仍在使用,但一般“不推荐”。对于尚未支持 cpusets/cgroups 的专用或嵌入式内核,它可能仍然可用。

Taskset, sched_setaffinity(), …

从底层来讲,还可以使用 taskset(等工具或者依赖 sched_setaffinity() (这样的 API,将每个任务绑定到所需的 CPU 集合。在不支持 cpuset 的系统中,其优点是允许在运行时更改绑定关系,这与“isolcpus”不同;缺点是它需要更精细的工作。

IRQ绑定

我们已经进行了任务绑定,但是硬件中断仍然可以在隔离的 CPU 上触发,并干扰其独占负载。所幸,我们可以通过 procfs ( 提供的接口安排在内务管理集上触发这些中断:

# Migrate irqs to CPU 0-6 (exclude CPU 7)

for I in $(ls /proc/irq)

if [[ -d "/proc/irq/$I" ]]

echo "Affining vector $I to CPUs 0-6"

echo 0-6 > /proc/irq/$I/smp_affinity_list

您可能会在其中一个中断向量上遇到 I/O 错误,例如 x86-64 机器上的数字 0,因为这是每个 CPU 上的计时器向量表,由于其本地性的特质,无法将其移开。然而,这个问题大可放心忽略,因为“nohz_full” 就是专门为解决这个问题而设计的。

防止其他干扰

在本例中,我们处理的是基于调度程序和中断的直接干扰。更高阶的话题将在后续文章中介绍,例如防止页面错误等异常(。

实际测试

现在,大部分内务管理工作负载应在 CPU 0-6 上运行。CPU 7 将在无干扰的情况下运行用户空间代码。我们用启动器做一个无意义的循环。

虚拟用户空间循环

以下代码将当前任务绑定到隔离的 cpuset(即 cpu7),并始终执行一个死循环。它在启动并运行 10 秒后,最终被单独的启动器启关闭。

int main(void)

// Move the current task to the isolated cgroup (bind to CPU 7)

int fd = open("/sys/fs/cgroup/cpuset/isolated/cgroup.procs", O_WRONLY);

perror("Can't open cpuset file...\n");

write(fd, "0\n", 2);

close(fd);

// Run an endless dummy loop until the launcher kills us

将该代码包写在名为“user_loop.c”的文件中,并编译:

$ gcc user_loop.c -o user_loop

启动器

除了在隔离的 CPU 7 上运行无意义的循环 10 秒之外,启动器的作用是跟踪可能对敏感工作负载有潜在干扰的事件。在本例中,我们使用 SUSE Linux Enterprise Server 15 SP3 上默认的跟踪 debugfs 装载点:

TRACING=/sys/kernel/debug/tracing/

# Make sure tracing is off for now

echo 0 > $TRACING/tracing_on

# Flush previous traces

echo > $TRACING/trace

# Record disturbance from other tasks

echo 1 > $TRACING/events/sched/sched_switch/enable

# Record disturbance from interrupts

echo 1 > $TRACING/events/irq_vectors/enable

# Now we can start tracingecho 1 > $TRACING/tracing_on

# Run the dummy user_loop for 10 seconds on CPU 7

./user_loop &

USER_LOOP_PID=$!

kill $USER_LOOP_PID

# Disable tracing and save traces from CPU 7 in a file

echo 0 > $TRACING/tracing_on

cat $TRACING/per_cpu/cpu7/trace > trace.7

这里跟踪到两个有意思的底层事件:

上述代码可以写入名为“launch” 的文件中,该文件与“user_loop”位于相同的目录中。

完美世界的理想结果

在以 root 身份运行上述“launch”之后,如果一切顺利,可以在“trace.7”文件中找到以下内容:

<idle>-0 [007] d..2. 1980.976624: sched_switch: prev_comm=swapper/7 prev_pid=0

prev_prio=120 prev_state=R ==> next_comm=user_loop next_pid=1553

next_prio=120

user_loop-1553 [007] d.h.. 1990.946593: reschedule_entry: vector=253

user_loop-1553 [007] d.h.. 1990.946593: reschedule_exit: vector=253

在这里,user_loop任务从时间戳 1980 秒处开始第一次跟踪,从 swapper(空闲任务)进行调度。然后,在 10 秒钟内(1990 – 1980),直到任务最终中断之前,任何事情都没有发生,这样做是为了处理发射器发送的关闭信号。这表明在这段时间内没有任务或中断干扰我们的 user_loop。

请注意:只有在完美世界中的完美机器上进行理想设置时,才会出现这种结果。

声明:本文来自用户分享和网络收集,仅供学习与参考,测试请备份。