Chinaunix首页 | 论坛 | 博客
  • 博客访问: 630927
  • 博文数量: 79
  • 博客积分: 2671
  • 博客等级: 少校
  • 技术积分: 1247
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-02 15:26
个人简介

宅男

文章分类

全部博文(79)

文章存档

2017年(11)

2016年(12)

2015年(6)

2012年(10)

2011年(33)

2010年(7)

分类: Android平台

2017-07-11 15:31:29


我们知道内核有个全局变量叫着jiffies,同时有个跟它有关的宏定义叫着HZJiffies依赖于CPU的定时器,每一个时钟tickjiffies数值加1.一般HZ是一个整数,HZjiffies1s钟。一般我们设置HZ100,时钟中断的间隔为10ms,也就意味着jiffies的最小精确度为10msScheduler tick也是在这个时钟中断中完成的。

Timer tick带来了好处,同时也有一些不如人意的地方。比如如果没有task可运行,CPU进入idle进程,并最终进入power collapse状态。时钟中断会定期的将CPUpower collapse状态唤醒,然后又重新进入power collapse状态。这是很没有必要的。

所以在linux kernel里面引入了NOHZ feature,通过CONFIG_NO_HZ_COMMON来控制。Nohz就是关闭idle CPU上的时钟中断。这样不会存在定期的时钟中断。在必要的时候通过IPI_RESCHEDULEidlecpu退出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 一些进程过去,分担一下压力。


  1. static void nohz_balancer_kick(void)
  2. {
  3.         int ilb_cpu;

  4.         nohz.next_balance++;

  5.         ilb_cpu = find_new_ilb();
  6.         
  7.         if (ilb_cpu >= nr_cpu_ids)
  8.                 return;

  9.         if (test_and_set_bit(NOHZ_BALANCE_KICK, nohz_flags(ilb_cpu)))
  10.                 return;
  11.         /*
  12.          * Use smp_send_reschedule() instead of resched_cpu().
  13.          * This way we generate a sched IPI on the target cpu which
  14.          * is idle. And the softirq performing nohz idle load balance
  15.          * will be run before returning from the IPI.
  16.          */
  17.         smp_send_reschedule(ilb_cpu);
  18.         return;
  19. }
首先通过find_new_ilb查找一个idlecpu。如果该cpu已经在做nohz load balance的话,直接返回。否则通过smp_send_reschedule发送IPItargetilde cpu让其做nohzload balance



  1. void handle_IPI(int ipinr, struct pt_regs *regs)
  2. {
  3.         unsigned int cpu = smp_processor_id();
  4.         struct pt_regs *old_regs = set_irq_regs(regs);

  5.         if ((unsigned)ipinr < NR_IPI) {
  6.                 trace_ipi_entry_rcuidle(ipi_types[ipinr]);
  7.                 __inc_irq_stat(cpu, ipi_irqs[ipinr]);
  8.         }

  9.         switch (ipinr) {
  10.         case IPI_RESCHEDULE:
  11.                 scheduler_ipi();
  12.                 break;
关于IPI的原理在这里不准备详细介绍。


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的一些在当前rqwake_listtasks激活。

然后调用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为当前cpuidle load balancenohz_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用于将需要migratetasks插入到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


  1. void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags)
  2. {
  3.         const struct sched_class *class;
  4.         
  5.         if (p->sched_class == rq->curr->sched_class) {
  6.                 rq->curr->sched_class->check_preempt_curr(rq, p, flags);
  7.         } else {
  8.                 for_each_class(class) {
  9.                         if (class == rq->curr->sched_class)
  10.                                 break;
  11.                         if (class == p->sched_class) {
  12.                                 resched_curr(rq);
  13.                                 break;
  14.                         }
  15.                 }
  16.         }
最终调用resched_curr发起ipi,来唤醒remote cpu进行re-schedule



  1. void resched_curr(struct rq *rq)
  2. {
  3.         struct task_struct *curr = rq->curr;
  4.         int cpu;
  5.                                 
  6.         lockdep_assert_held(&rq->lock);
  7.                         
  8.         if (test_tsk_need_resched(curr))
  9.                 return;

  10.         cpu = cpu_of(rq);

  11.         if (cpu == smp_processor_id()) {
  12.                 set_tsk_need_resched(curr);
  13.                 set_preempt_need_resched();
  14.                 return;
  15.         }

  16.         if (set_nr_and_not_polling(curr))
  17.                 smp_send_reschedule(cpu);
  18.         else
  19.                 trace_sched_wake_idle_without_ipi(cpu);
  20. }
最终又回到了上面提到的scheduler_ipi函数。


 


阅读(2484) | 评论(0) | 转发(0) |
0

上一篇:linux调度器(五)- load balance(1)

下一篇:没有了

给主人留下些什么吧!~~