您的位置:首页 > 运维架构 > Linux

linux kernel 负载均衡总结

2015-10-13 09:59 1356 查看
Linux操作系统支持SMP,并且同时会运行多个进程,这些进程是如何在多个CPU之间调度的,如何进行负载均衡?

进程在如下时机决定在哪个CPU运行

1.进程被唤醒的时候,比如进程收到网络数据包,或者完成磁盘IO操作

2.进程刚被fork出来

Linux通过select_task_rq函数决定进程在哪一个CPU上进行运行。该函数在./kernel/sched/core.c中实现。

int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags)

select_task_rq的调用时机:

wake_up_new_task: 进程刚被fork出来

try_to_wake_up:进程被唤醒的时候

select_task_rq函数在决定进程在哪个CPU上运行会考虑哪些因素?

static int select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags)

{

int cpu = smp_processor_id();

for_each_domain(cpu, tmp) {

if (!(tmp->flags & SD_LOAD_BALANCE))

break; //如果调度域不支持SD_LOAD_BALANCE,直接退出。

/*

* If both cpu and prev_cpu are part of this domain,

* cpu is a valid SD_WAKE_AFFINE target.

*/

if (want_affine && (tmp->flags & SD_WAKE_AFFINE) &&

cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) {

affine_sd = tmp;

break;

} //找到prev_cpu和cpu共同所在的最近的调度域

if (tmp->flags & sd_flag)

sd = tmp; //根据sd_flag找到满足sd_flag最大对应的调度域

else if (!want_affine)

break;

}

if (affine_sd) { //如果找到affine_sd,则使用当前cpu作为调度的CPU

sd = NULL; /* Prefer wake_affine over balance flags */

if (cpu != prev_cpu && wake_affine(affine_sd, p, sync))

new_cpu = cpu;

}

if (!sd) { //如果找不到满足sd_flags的调度域,则找附近的空闲CPU。

if (sd_flag & SD_BALANCE_WAKE) /* XXX always ? */

new_cpu = select_idle_sibling(p, new_cpu);

} else while (sd) {

struct sched_group *group;

int weight;

if (!(sd->flags & sd_flag)) {

sd = sd->child;

continue;

}

//然后找sd中负载最小的cpu作为调度cpu

new_cpu = find_idlest_cpu(group, p, cpu);

}

}

wake_flags参数的含义:

/*

* wake flags

*/

#define WF_SYNC 0x01 /* waker goes to sleep after wakeup */

#define WF_FORK 0x02 /* child wakeup after fork */

#define WF_MIGRATED 0x4 /* internal use, task got migrated */

sd_flag的含义:

#define SD_LOAD_BALANCE 0x0001/* Do load balancing on this domain. */

#define SD_BALANCE_NEWIDLE 0x0002/* Balance when about to become idle */

#define SD_BALANCE_EXEC 0x0004/* Balance on exec */

#define SD_BALANCE_FORK 0x0008/* Balance on fork, clone */

#define SD_BALANCE_WAKE 0x0010 /* Balance on wakeup */

#define SD_WAKE_AFFINE 0x0020/* Wake task to waking CPU */

#define SD_PREFER_LOCAL 0x0040 /* Prefer to keep tasks local to this domain */

#define SD_SHARE_CPUPOWER 0x0080/* Domain members share cpu power */

#define SD_POWERSAVINGS_BALANCE 0x0100/* Balance for power savings */

#define SD_SHARE_PKG_RESOURCES 0x0200/* Domain members share cpu pkg resources */

#define SD_SERIALIZE 0x0400/* Only a single load balancing instance */

#define SD_ASYM_PACKING 0x0800 /* Place busy groups earlier in the domain */

#define SD_PREFER_SIBLING 0x1000/* Prefer to place tasks in a sibling domain */

SD_LOAD_BALANCE的含义:如果对应的调度域支持SD_LOAD_BALANCE,则进行重新选择运行队列。

SD_SHARE_PKG_RESOURCES:select_idle_sibling在判断空闲core的时候,是否查找所在doman的其他cpu是否空闲。

select_task_rq_fair需要gdb调试一下,看看domain的组织结构,查找空闲cpu的时候什么时间停止?

除了进程在唤醒或者刚fork完成,还有哪些机制会导致进程运行在哪个CPU上?

如果进程只有有唤醒的时候决定在哪个CPU上,难免会导致CPU负荷不均,仅此有必要进行负载均衡。

负载均衡的调用时机:

1.周期性的调用,保证负载均衡

open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);

trigger_load_balance<-scheduler_tick<-周期性的时钟中断。

在每一个CPU上通过周期性的定时中断进行触发调用。

负载均衡有两种,一种是在CPU空闲触发,一种是CPU忙时触发。

rq->idle_balance = idle_cpu(cpu);

trigger_load_balance(rq);

作为参数传递给负载均衡函数。

static void rebalance_domains(struct rq *rq, enum cpu_idle_type idle)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: