Linux NO_HZ_FULL NO_HZ 框架实现分析

解决一个问题重要的是了解问题的领域知识和相关背景,本文的目的是介绍linux内核的NO_HZ_FULL相关领域知识,以便涉及相关问题时候能更容易上手。
简单的说linux NO_HZ_FULL的作用是消除Linux内核不必要的周期时钟,提高系统的性能或者节省能耗。NO_HZ需要内核高精度时钟框架的支持。NO_HZ有几种选择,NO_HZ_IDLE是在CPU IDLE的时候关掉该CPU的周期tick。 NO_HZ_FULL是在普通任务不多于一个,或者只有实时FIFO任务等情况下关掉该CPU的周期tick。

Linux tick的作用
Linux内核每隔固定周期会发出timer interrupt,HZ是用来定义每一秒有几次timer interrupts。HZ为1000,代表每秒有1000次timer interrupts。HZ的倒数称为一个节拍,也就是一个Tick。
内核会利用这个时钟中断进行管理系统,在每个tick中处理这些管理事务,具体内容包括任务调度、定时器、时间管理等。在原来的Linux设计中这个tick是系统管理的基础,不可或缺。

NO_HZ提出的背景
NO_HZ_FULL是linux内核的一项功能,它提出的背景驱动如下:
提高HPC性能,CPU上只运行一个任务时候应该最大化的利用CPU。HZ=1000的周期tick会带来大约1%的开销。NO_HZ_FULL能在这种情况能提高系统0.5%-1.0%的性能。
实时任务希望减少延迟,一些关键任务希望抖动尽可能小。内核引起的抖动源就是周期tick。
随着cpu和核数的增加,一个cpu上执行一个任务变得越来越普遍,这个特性可以在桌面和移动领域发挥作用。

NO_HZ增加的开销
NO_HZ带来的开销主要在CPU进出NO_HZ模式时,定时器需要重设置,会导致处理时间变得稍微长些。
用户态和内核态切换的时候,会有一点开销,主要用于通知类似RCU的内核子系统模式切换了。
POSIX CPU timers会妨碍CPU进入NO_HZ_FULL,当实时任务需要采用其他方案代替使用POSIX CPU timers。
如果pending的perf events 超过硬件的限制,通常采用循环的方法来处理,不过NO_HZ_FULL会阻碍这种情况,目前只能通过限制pending perf events的数目来解决。

NO_HZ设计框架
既然NO_HZ是停掉Linux内核的周期时钟,那么首先需要解决的问题是,什么情况下可以停掉,如何停掉。这个问题解决了,当然就是是什么时候恢复,如何恢复的问题。只要搞清楚了上面2个问题,整个NO_HZ的设计框架就清楚了。下面以这些问题为主线简单说明一下:

什么时候可以停掉:
内核已经封装了can_stop_full_tick函数来解决这个问题,这个函数在tick-sched.c文件中。
static bool can_stop_full_tick(void)
{
WARN_ON_ONCE(!irqs_disabled());

if (!sched_can_stop_tick()) {
    trace_tick_stop(0, "more than 1 task in runqueue\n");
    return false;
}

if (!posix_cpu_timers_can_stop_tick(current)) {
    trace_tick_stop(0, "posix timers running\n");
    return false;
}

if (!perf_event_can_stop_tick()) {
    trace_tick_stop(0, "perf events running\n");
    return false;
}

/* sched_clock_tick() needs us? */

ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK
/*
* TODO: kick full dynticks CPUs when
* sched_clock_stable is set.
*/
if (!sched_clock_stable()) {
trace_tick_stop(0, “unstable sched clock\n”);
/*
* Don’t allow the user to think they can get
* full NO_HZ with this machine.
*/
WARN_ONCE(tick_nohz_full_running,
“NO_HZ FULL will not work with unstable sched clock”);
return false;
}
endif

return true;

}

简单说下这个函数,要停掉周期tick,首先需要满足即内核任务调度器这个时候必须不依赖于tick,什么时候调度器不依赖于tick呢,运行实时FIFO任务的时候,只有一个RR实时任务的时候,或者可以运行的普通任务不超过一个。
其次需要满足这时候当前任务没有posix 定时器,主要看用户态程序是否使用了。
最后这时候需要没有perf事件,这个通常没有做性能分析的时候都满足。
另外,如果系统时钟是不稳定的,那么也不满足条件。稳定的系统时钟可以通过启动参数指定,也可以在系统运行时动态指定。

如何停掉周期tick进入NO_HZ:
内核在每个中断处理完成退出的时候都会检查是否应该停掉周期tick进入NO_HZ。注意,这里不仅是检查是否进入NO_HZ的地方,也是检查是否退出NO_HZ的地方!
具体调用链如下:
do_IRQ
exiting_irq
irq_exit
tick_irq_exit
tick_nohz_irq_exit
tick_nohz_full_update_tick
tick_nohz_stop_sched_tick 或者tick_nohz_restart_sched_tick
在检查的时候,发现符合进入NO_HZ的条件,就调用tick_nohz_stop_sched_tick停掉周期tick进入NO_HZ。
tick_nohz_stop_sched_tick这个函数这里就不进行详细分析了,简单说下一个关键的地方,tick_nohz_stop_sched_tick会判断周期tick停止多久,注意下面的判断
static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
ktime_t now, int cpu)
{

……
if (rcu_needs_cpu(basemono, &next_rcu) ||
arch_needs_cpu() || irq_work_needs_cpu()) {
next_tick = basemono + TICK_NSEC;
} else {

}

……
}
如果rcu子系统在当前CPU存在回调,或者在当前CPU有irq_work链表的工作要做(这些会在IRQ中被执行),那么只停TICK_NSEC这么一小段时间。因此如果要想NO_HZ效果好,那么必须对这些需要进入NO_HZ的CPU设置合理的RCU回调配置(没有rcu回调 ,或者cpu配置了CONFIG_RCU_NOCB_CPU_ALL),并且不要在这些CPU上添加irq_work。

如何退出NO_HZ:
什么时候判断一个CPU应该退出NO_HZ的好时机?
答案是:当有新任务添加到NO_HZ的CPU上,或者有新的定时器添加到NO_HZ的CPU上。wake_up_nohz_cpu会调用ick_nohz_full_kick_cpu让指定CPU退出NO_HZ。

timer加入的no_hz active的cpu上时候
add_timer_on
internal_add_timer
wake_up_nohz_cpu

在no_hz cpu上启动一个timer的时候
hrtimer_start_range_ns
wake_up_nohz_cpu

往运行队列中增加的时候
add_nr_running
tick_nohz_full_kick_cpu

下面是tick_nohz_full_kick_cpu的调用链:
wake_up_nohz_cpu
tick_nohz_full_kick_cpu
irq_work_queue_on(&per_cpu(nohz_full_kick_work, cpu), cpu); —会向指定cpu 发送IPI
arch_send_call_function_single_ipi(cpu);
tick_nohz_irq_exit –收到ipi之后
tick_nohz_full_update_tick
tick_nohz_restart_sched_tick —-这时候restart
上面调用链很清楚了,退出的时候只需要向对应的CPU发送一个IPI,这个中断什么也不做,只是触发中断。这个空中断处理函数退出后,会走irq_exit,然后就通过irq退出时候的判断逻辑执行退出NO_HZ的代码,当然在发送IPI之前,需要设置好退出NO_HZ的标志。

小结:
NO_HZ的进入和退出都是在中断退出的时候进行。进入NO_HZ需要满足特定条件。在有新进程加入运行队列或者新定时器的时候,系统检测是否需要退出NO_HZ,如果需要发送IPI到指定CPU,然后该CPU在IPI中断退出的时候退出NO_HZ。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值