多核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間的負載平衡,可以這麼試試哈。