宅男
分类: Android平台
2017-07-11 15:31:29
我们知道内核有个全局变量叫着jiffies,同时有个跟它有关的宏定义叫着HZ。Jiffies依赖于CPU的定时器,每一个时钟tick,jiffies数值加1.一般HZ是一个整数,HZ个jiffies为1s钟。一般我们设置HZ为100,时钟中断的间隔为10ms,也就意味着jiffies的最小精确度为10ms。Scheduler tick也是在这个时钟中断中完成的。
Timer tick带来了好处,同时也有一些不如人意的地方。比如如果没有task可运行,CPU进入idle进程,并最终进入power collapse状态。时钟中断会定期的将CPU从power collapse状态唤醒,然后又重新进入power collapse状态。这是很没有必要的。
所以在linux kernel里面引入了NOHZ feature,通过CONFIG_NO_HZ_COMMON来控制。Nohz就是关闭idle CPU上的时钟中断。这样不会存在定期的时钟中断。在必要的时候通过IPI_RESCHEDULE让idle的cpu退出power collapse。
在trigger_load_balance函数中。
#ifdef CONFIG_NO_HZ_COMMON
if (nohz_kick_needed(rq))
nohz_balancer_kick();
#endif
nohz_kick_needed用于检查是否需要kick nohz load balance,就是检查当前CPU是否负载过高等等情况,是否需要唤醒一个idle CPU让其pull 一些进程过去,分担一下压力。
void scheduler_ipi(void)
{
…
irq_enter();
sched_ttwu_pending();
/*
* Check if someone kicked us for doing the nohz idle load balance.
*/
if (unlikely(got_nohz_idle_kick())) {
this_rq()->idle_balance = 1;
raise_softirq_irqoff(SCHED_SOFTIRQ);
}
irq_exit();
首先调用sched_ttwu_pending();将pending的一些在当前rq的wake_list的tasks激活。
然后调用raise_softirq_irqoff(SCHED_SOFTIRQ);发起一个软中断。关于SCHED_SOFTIRQ在前面已经讲到了。
open_softirq(SCHED_SOFTIRQ, run_rebalance_domains)
再次回到run_rebalance_domains函数。
static void run_rebalance_domains(struct softirq_action *h)
{
struct rq *this_rq = this_rq();
enum cpu_idle_type idle = this_rq->idle_balance ?
CPU_IDLE : CPU_NOT_IDLE;
/*
* If this cpu has a pending nohz_balance_kick, then do the
* balancing on behalf of the other idle cpus whose ticks are
* stopped. Do nohz_idle_balance *before* rebalance_domains to
* give the idle cpus a chance to load balance. Else we may
* load balance only within the local sched_domain hierarchy
* and abort nohz_idle_balance altogether if we pull some load.
*/
nohz_idle_balance(this_rq, idle);
rebalance_domains(this_rq, idle);
}
这一次主要看第一个函数nohz_idle_balance。其中rebalance_domains为当前cpu做idle load balance。nohz_idle_balance检查this rq之外的idle cpu是否需要做idle load balance。
关于rebalance_domains(rq, CPU_IDLE)在上一篇load balance中已经详细讲解了。
这边讲一下如何wake up一个nohz 的idle cpu。
在load_balance里面attach_tasks用于将需要migrate的tasks插入到remote cpu相应的rq里面。
static void attach_task(struct rq *rq, struct task_struct *p)
{
lockdep_assert_held(&rq->lock);
BUG_ON(task_rq(p) != rq);
p->on_rq = TASK_ON_RQ_QUEUED;
activate_task(rq, p, 0);
check_preempt_curr(rq, p, 0);
}
check_preempt_curr用于检查是否需要抢占rq上的curr task。