Linux SMP负载均衡

周期性调度器scheduler_tick周期性调度器由中断实现,系统定时产生一个中断,然后启动周期性调度器,周期性调度器执行过程中要关闭中断, 周期性调度器执行完毕后再打开中断。当周期性调度器的相关统计量rq->next_balance到时触发负载均衡。在trigger_load_balance中可以发现idle再也不idle了,它也有了自己的任务,就是负载均衡。主要上面的倒数第二个和第三个if判断,如果该cpu不是idle状态,那么也可以做laod_balance,这几代内核将laod_balance作为softirq来执行了,效率自然有些不错了,以往都是在时钟中断或者schedule调度器中执行的,很延迟的,那么系统中就会有idle balance和non-idle balance两种负载均衡,后者是传统的负载均衡,而前者是应对nohz(将时钟中断停掉)的负载均衡。有人提出一个补丁,说是将timer迁移到ilb所在的cpu上,这样就可以通过将timer的到期和ilb的周期唤醒的时间重合来减少在别的cpu上timer的唤醒,从而可以最小限度的影响nohz的状态,达到节能,这个想法是不错的,大动timer的代码不现实,于是就专门写了一个迁移timer的函数来执行就可以了,这样本来需要在5秒后由于timer到期而被唤醒的nohz状态的cpu的这个timer可以被迁移到这个ilb所在的cpu上,从而那个cpu不用被唤醒了,由于timer的精度小,这个cpu恰好周期执行的时候它到期,非常不错,不过按照上面的那个函数,如果这个ilb需要执行timer了,那么是需要再选出一个cpu作为ilb的执行者呢,还是让这个cpu执行完timer之后继续作为ilb的cpu。在触发负载平衡后触发软中断(SCHED_SOFTIRQ),软中断的处理函数为run_rebalance_domains,首先自下而上遍历cpu所属的sched domain,对其进行负载均衡.如果本cpu负责idle load balance,代替停用周期时钟的cpu执行load balance。
//	部分参考:http://blog.csdn.net/dog250/article/details/5303561
//	负载均衡
//		当rq->next_balance到时,触发负载均衡	
//	调用路径:scheduler_tick->trigger_load_balance
//	注:
//		nohz.cpu_mask中的cpu表示停用了周期时钟
//	函数任务:
//		1.如果进入tick的时候rq变得有事可做,并且之前由本cpu执行idle load balance
//			1.1 不再做idle load balance
//			1.2 nohz.cpu_mask中选择一个cpu负责idle load balance
//			1.3 通过ipi通知彼cpu负责ilb
//		2.如果所有cpu均处于idle状态,之前由本cpu做idle load balance
//			2.1 没有必要再做idb,通知本cpu停止idle load balance
//		3.如果本cpu处于idle状态,其他cpu做idle load balance
//			3.1 ilb的cpu会代此cpu执行load balance,不需要raise SCHED_SOFTIRQ
//		4.本cpu没有加入到任何domain,则不需要在domain间load balance
//		5.如果到达执行load balance时间点
//			5.1 raise SCHED_SOFTIRQ
1.1 static inline void trigger_load_balance(struct rq *rq, int cpu)
{
#ifdef CONFIG_NO_HZ
	//	rq->idle_at_tick = 0表示rq上运行的非idle进程
	//	rq->in_nohz_recently表示最近关闭了周期时钟
	if (rq->in_nohz_recently && !rq->idle_at_tick) {
		rq->in_nohz_recently = 0;
		//如果之前由本cpu执行idle load balance
		if (atomic_read(&nohz.load_balancer) == cpu) {
			//nohz.cpu_mask中的cpu表示停用了周期时钟,在select_nohz_load_balancer中被加入
			cpumask_clear_cpu(cpu, nohz.cpu_mask);
			//进入tick的时候rq变得有事可做,则不再做idle load balance
			atomic_set(&nohz.load_balancer, -1);
		}
		//从nohz.cpu_mask中选择一个cpu做idle load balance 
		if (atomic_read(&nohz.load_balancer) == -1) {
			int ilb = find_new_ilb(cpu);
			if (ilb < nr_cpu_ids)
			{
				//向该cpu发送ipi
				resched_cpu(ilb);
			}
		}
	}
	//如果所有cpu均处于idle状态,本cpu做idle load balance,则通知本cpu停止idle load balance
	if (rq->idle_at_tick && atomic_read(&nohz.load_balancer) == cpu &&
	    cpumask_weight(nohz.cpu_mask) == num_online_cpus()) {
		resched_cpu(cpu);
		return;
	}
	//如果本cpu处于idle状态,其他cpu做idle load balance,则不需要raise其SCHED_SOFTIRQ
	if (rq->idle_at_tick && atomic_read(&nohz.load_balancer) != cpu &&
	    cpumask_test_cpu(cpu, nohz.cpu_mask))
		return;
#endif
	//本cpu没有加入到任何domain,则不需要raise其SCHED_SOFTIRQ
	if (time_after_eq(jiffies, rq->next_balance) &&
	    likely(!on_null_domain(cpu)))
	{
		//raise其SCHED_SOFTIRQ
		raise_softirq(SCHED_SOFTIRQ);
	}
}
//	负载均衡软中断
//		由trigger_load_balance函数触发
//	函数任务:
//		1.自下而上遍历cpu所属的sched domain,对其进行负载均衡
//		2.如果本cpu负责idle load balance,代替停用周期时钟的cpu执行load balance
//			2.1 遍历nohz.cpu_mask中所有的idle cpu
//				2.2.1 代其执行步骤1
//				2.2.2 如果这这段时间内本cpu有非idle进程就绪,退出ilb,下一次负载均衡时发生时再ilb
//				2.2.3 如果idle cpu下一次进行负载均衡的时间戳大于本cpu
//					2.2.3.1 更新idle cpu下一次负载均衡的时间为本cpu进行负载均衡的时间戳
2.1 static void run_rebalance_domains(struct softirq_action *h)
{
	int this_cpu = smp_processor_id();
	struct rq *this_rq = cpu_rq(this_cpu);
	//cpu当前状态
	//	如果rq上当前运行的为idle task则cpu为idle状态
	enum cpu_idle_type idle = this_rq->idle_at_tick ?
						CPU_IDLE : CPU_NOT_IDLE;
	//为cpu在同一个domain内执行load balance
	rebalance_domains(this_cpu, idle);
#ifdef CONFIG_NO_HZ
	//如果本cpu负责idle load balance,代替停用周期时钟的cpu执行load balance
	if (this_rq->idle_at_tick &&
	    atomic_read(&nohz.load_balancer) == this_cpu) {
		struct rq *rq;
		int balance_cpu;
		for_each_cpu(balance_cpu, nohz.cpu_mask) {
			if (balance_cpu == this_cpu)
				continue;
			//非idle进程就绪,不在执行idle load balance,下一次load balance发生时再ilb
			if (need_resched())
				break;
			//代idle的cpu执行load balance
			rebalance_domains(balance_cpu, CPU_IDLE);
			//更新本rq下一次load balance的时间为所有被代理rq执行load balance中最早的
			rq = cpu_rq(balance_cpu);
			if (time_after(this_rq->next_balance, rq->next_balance))
				this_rq->next_balance = rq->next_balance;
		}
	}
#endif
}
//	负载均衡
//		在cpu所属的sched domain层次结构上执行load balance
//	调用路径:run_rebalance_domains->rebalance_domains
//	函数参数:
//		idle,cpu处于的状态
//			CPU_IDLE,cpu上运行的idle task
//			CPU_NOT_IDLE,cpu上运行的非idle task
//	函数任务:
//		1.自上而下遍历rq所属的所有sched domain
//			1.1 如果此domain不需要执行load balance(没有设置SD_LOAD_BALANCE),则跳过
//			1.2 计算domain执行load balance的时间间隔
//				1.2.1 由domain的balance_interval指定load balance在domain上执行的时间间隔
/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值