1. 程式人生 > >linux核心排程演算法(3)--多核系統的負載均衡

linux核心排程演算法(3)--多核系統的負載均衡

多核CPU現在很常見,那麼問題來了,一個程式在執行時,只在一個CPU核上執行?還是交替在多個CPU核上執行呢?LINUX核心是如何在多核間排程程序的呢?又是核心又是CPU核,兩個核有點繞,下面稱CPU處理器來代替CPU核。

實際上,如果你沒有對你的程序做過特殊處理的話,LINUX核心是有可能把它放到多個CPU處理器上執行的,這是核心的負載均衡。上文說過,每個處理器上有一個runqueue佇列,表示這顆處理器上處於run狀態的程序連結串列,在多處理器的核心中,就會有多個runqueue,而如果他們的大小很不均衡,就會觸發核心的load_balance函式。這個函式會把某個CPU處理器上過多的程序移到runqueue元素相對少的CPU處理器上。

舉個例子來簡單說明這個過程吧。當我們剛fork出一個子程序時,子程序也還在當前CPU處理器的runqueue裡,它與父程序均分父程序的時間片。當然,時間片與多處理器間的負載均衡沒有關係。假設我們的系統是雙核的,父程序執行在cpu0上,那麼這個fork出來的程序也是在cpu0的runqueue中。

那麼,什麼時候會發生負載均衡呢?

1、當cpu1上的runqueue裡一個可執行程序都沒有的時候。這點很好理解,cpu1無事可作了,這時在cpu1上會呼叫load_balance,發現在cpu0上還有許多程序等待執行,那麼它會從cpu0上的可執行程序裡找到優先順序最高的程序,拿到自己的runqueue裡開始執行。

2、第1種情形不適用於執行佇列一直不為空的情況。例如,cpu0上一直有10個可執行程序,cpu1上一直有1個可執行程序,顯然,cpu0上的程序們得到了不公平的對待,它們拿到cpu的時間要小得多,第1種情形下的load_balance也一直不會呼叫。所以,實際上,每經過一個時鐘節拍,核心會呼叫scheduler_tick函式,而這個函式會做許多事,例如減少當前正在執行的程序的時間片,在函式結尾處則會呼叫rebalance_tick函式。rebalance_tick函式決定以什麼樣的頻率執行負載均衡。

static void rebalance_tick(int this_cpu, runqueue_t *this_rq,
			   enum idle_type idle)
{
	unsigned long old_load, this_load;
	unsigned long j = jiffies + CPU_OFFSET(this_cpu);
	struct sched_domain *sd;

	/* Update our load */
	old_load = this_rq->cpu_load;
	this_load = this_rq->nr_running * SCHED_LOAD_SCALE;
	/*
	 * Round up the averaging division if load is increasing. This
	 * prevents us from getting stuck on 9 if the load is 10, for
	 * example.
	 */
	if (this_load > old_load)
		old_load++;
	this_rq->cpu_load = (old_load + this_load) / 2;

	for_each_domain(this_cpu, sd) {
		unsigned long interval;

		if (!(sd->flags & SD_LOAD_BALANCE))
			continue;

		interval = sd->balance_interval;
		if (idle != SCHED_IDLE)
			interval *= sd->busy_factor;

		/* scale ms to jiffies */
		interval = msecs_to_jiffies(interval);
		if (unlikely(!interval))
			interval = 1;

		if (j - sd->last_balance >= interval) {
			if (load_balance(this_cpu, this_rq, sd, idle)) {
				/* We've pulled tasks over so no longer idle */
				idle = NOT_IDLE;
			}
			sd->last_balance += interval;
		}
	}
}

當idle標誌位是SCHED_IDLE時,表示當前CPU處理器空閒,就會以很高的頻繁來呼叫load_balance(1、2個時鐘節拍),反之表示當前CPU並不空閒,會以很低的頻繁呼叫load_balance(10-100ms)。具體的數值要看上面的interval了。

當然,多核CPU也有許多種,例如INTEL的超執行緒技術,而LINUX核心對一個INTEL超執行緒CPU會看成多個不同的CPU處理器。

上面說過,如果你沒有對你的程序做過特殊處理的話,LINUX核心是有可能把它放到多個CPU處理器上執行的,但是,有時我們如果希望我們的程序一直執行在某個CPU處理器上,可以做到嗎?核心提供了這樣的系統呼叫。系統呼叫sched_getaffinity會返回當前程序使用的cpu掩碼,而sched_setaffinity則可以設定該程序只能在哪幾顆cpu處理器上執行。當我們對某些程序有強烈的期待,或者想自己來考慮CPU間的負載均衡,可以這麼試試哈。