日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux进程网络均衡,linux多CPU进程负载均衡解析

發布時間:2024/10/14 linux 112 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux进程网络均衡,linux多CPU进程负载均衡解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在linux中,支持對稱smp的處理器模型,在多處理器的情況下,每個處理器都有自己的一個運行隊列,這樣就存在著分配不均的情況,有的cpu運行隊列很多進程,導致一直很忙,有的cpu運行隊列可能很少的進程甚至沒有任何運行進程,導致cpu經常處于空轉的狀態,因此我們需要一種機制,來均衡各個cpu上運行隊列的進程數。

1數據結構

為了支持多種多處理器模型,linux提出了調用域及組的概念,一個調用域可以包含其它的調用域或者多個組,一個組通常包含一個或者多個cpu,組的數據結構是:

struct sched_group {

struct sched_group *next;//一個調用域可能會包含多個組,該next用于將//sched_group串到調用域的鏈表上面

cpumask_t cpumask; //每個group中可能會包含一個或者多個cpu,這里的mask表//示了該group所包含的cpu

unsigned long cpu_power;//通常是cpu的個數

};

下面是調用域的數據結構:

struct sched_domain {

struct sched_domain *parent;//調用域可以被別的調用域所包含,parent指向父調用域

struct sched_group *groups;//該調用域所包含的組

cpumask_t span;

unsigned long min_interval;//最小的時間間隔,用于檢查進行負載均衡操作的時機是否到了

unsigned long max_interval; //同上

unsigned int busy_factor; ? //當處理器在不空閑的狀態下時,進行負載均衡操作的時間間隔一般也長很多,該factor為其乘數銀子

unsigned int imbalance_pct;

unsigned long long cache_hot_time;

unsigned int cache_nice_tries;

unsigned int per_cpu_gain;

int flags;

unsigned long last_balance;

unsigned int balance_interval; ?//負載均衡進行的時間間隔

unsigned int nr_balance_failed; //負載均衡遷移進程失敗的次數

};

下圖表現出了調用域和組之間的關系,這里我們關注2-cpu的smp和8-cpu的numa;2-cpu的SMP有一個調用域,調用域含兩個組,每個組含有一個cpu;8-cpu的numa中包含兩個調用域,最底層的調用域代表一個節點,每個最底層的調用域包含四個組,每個組有1個cpu,上層調用域包含兩個基礎的調用域。

2進行cpu負載均衡的時機

每經過一次時鐘中斷,scheduler_tick()就調用rebalance_tick()函數,rebalance_tick()函數會去觸發cpu運行隊列的負載均衡操作。該函數從最底層的調度域開始,一直到最上層的調度域進行檢查,對于每個調度域都去查看是否到了調用load_balance()函數的時間,該函數會進行cpu的負載均衡操作。由當前cpu的idle狀態和sched_domain的參數來確定調用load_balance()的時間間隔。

3 2-cpu smp和8-cpu numa cpu模型的調用域初始化

對調用域的初始化部分的代碼在linux2.6.11版本里面是在arch_init_sched_domains()函數中,被sched_init_smp()函數所調用。

在sched.c中定義了一個數組static struct sched_group sched_group_phys[NR_CPUS];每個數組元素代表一個cpu組。另外定義了一個每cpu變量Sched.c (kernel):static DEFINE_PER_CPU(struct sched_domain, phys_domains);,系統為每個物理cpu都生成了一個調度域數據結構。

對于2-cpu smp的情形來說,其初始化后,調度域和各個組之間的關系是:

在這個里面雖然每個cpu都有個調度域的數據結構,但調度域的groups鏈表指向的都是同一個group鏈表

8-cpu numa的組和調度域關系:

4cpu負載均衡的源碼解析

4.1rebalance_tick()

staticvoidrebalance_tick(intthis_cpu,?runqueue_t?*this_rq,

enumidle_type?idle)

{

unsigned?longold_load,?this_load;

unsigned?longj?=?jiffies?+?CPU_OFFSET(this_cpu);

structsched_domain?*sd;

//當前運行隊列中可運行的進程數決定了當前運行隊列的

//cpu_load參數

old_load?=?this_rq->cpu_load;

this_load?=?this_rq->nr_running?*?SCHED_LOAD_SCALE;

if(this_load?>?old_load)

old_load++;

//將運行隊列的cpu?load值設定為上一次的cpu_load和本次cpu_load的平均值

this_rq->cpu_load?=?(old_load?+?this_load)?/?2;

//從該cpu所屬的調度域開始,依次遍歷各個更高級的調用域

for_each_domain(this_cpu,?sd)?{

unsigned?longinterval;

//在該調度域上不需要做負載均衡

if(!(sd->flags?&?SD_LOAD_BALANCE))

continue;

//若當前cpu不處于空閑狀態的話,其調用load_balance的時間間隔會比較長

interval?=?sd->balance_interval;

if(idle?!=?SCHED_IDLE)

interval?*=?sd->busy_factor;

//將時間間隔ms轉換成jiffies

interval?=?msecs_to_jiffies(interval);

if(unlikely(!interval))

interval?=?1;

//當前的時間戳和上次balance的時間大于其間隔的話,調用load_balance進行負載的均衡

if(j?-?sd->last_balance?>=?interval)?{

//load_balance會去尋找最繁忙的cpu組中的最繁忙的cpu,將其進程遷移過來一部分

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;

}

}

}

4.2load_balance()

staticintload_balance(intthis_cpu,?runqueue_t?*this_rq,

structsched_domain?*sd,enumidle_type?idle)

{

structsched_group?*group;

runqueue_t?*busiest;

unsigned?longimbalance;

intnr_moved;

spin_lock(&this_rq->lock);

schedstat_inc(sd,?lb_cnt[idle]);

//查找最繁忙的cpu組

group?=?find_busiest_group(sd,?this_cpu,?&imbalance,?idle);

//所有的組都是平衡的,不需要做均衡

if(!group)?{

schedstat_inc(sd,?lb_nobusyg[idle]);

gotoout_balanced;

}

//找到最繁忙的組中最繁忙的運行隊列

busiest?=?find_busiest_queue(group);

if(!busiest)?{

schedstat_inc(sd,?lb_nobusyq[idle]);

gotoout_balanced;

}

//最繁忙的運行隊列是當前cpu的運行隊列,不需要做均衡

if(unlikely(busiest?==?this_rq))?{

WARN_ON(1);

gotoout_balanced;

}

schedstat_add(sd,?lb_imbalance[idle],?imbalance);

nr_moved?=?0;

if(busiest->nr_running?>?1)?{

double_lock_balance(this_rq,?busiest);

//將imbalance個進程從最繁忙的運行隊列上遷移到當前的cpu運行隊列上面

nr_moved?=?move_tasks(this_rq,?this_cpu,?busiest,

imbalance,?sd,?idle);

spin_unlock(&busiest->lock);

}

spin_unlock(&this_rq->lock);

//nr_moved?==?0,表示沒有遷移成功

if(!nr_moved)?{

schedstat_inc(sd,?lb_failed[idle]);

sd->nr_balance_failed++;

if(unlikely(sd->nr_balance_failed?>?sd->cache_nice_tries+2))?{

intwake?=?0;

spin_lock(&busiest->lock);

//active_balance表明該運行隊列是否喚醒遷移線程來進行負載均衡,

//push_cpu記錄了由哪個cpu來喚醒了其遷移線程

if(!busiest->active_balance)?{

busiest->active_balance?=?1;

busiest->push_cpu?=?this_cpu;

wake?=?1;

}

spin_unlock(&busiest->lock);

//喚醒最繁忙運行隊列上的遷移內核線程,對進程進行遷移

if(wake)

wake_up_process(busiest->migration_thread);

sd->nr_balance_failed?=?sd->cache_nice_tries;

}

if(sd->balance_interval?max_interval)

sd->balance_interval++;

}?else{

sd->nr_balance_failed?=?0;

//進程遷移成功,重置調用域的balance_interval參數

sd->balance_interval?=?sd->min_interval;

}

returnnr_moved;

out_balanced:

spin_unlock(&this_rq->lock);

/*?tune?up?the?balancing?interval?*/

//不需要進行進程的遷移,適當的加大負載均衡的間隔時間,說明

//當前的負載均衡做的比較好

if(sd->balance_interval?max_interval)

sd->balance_interval?*=?2;

return0;

}

4.3move_tasks()

staticintmove_tasks(runqueue_t?*this_rq,intthis_cpu,?runqueue_t?*busiest,

unsigned?longmax_nr_move,structsched_domain?*sd,

enumidle_type?idle)

{

prio_array_t?*array,?*dst_array;

structlist_head?*head,?*curr;

intidx,?pulled?=?0;

task_t?*tmp;

if(max_nr_move?<=?0?||?busiest->nr_running?<=?1)

gotoout;

//先從過期隊列上進行進程的遷移,這樣對硬件cache的

//影響比較小

if(busiest->expired->nr_active)?{

array?=?busiest->expired;

dst_array?=?this_rq->expired;

}?else{

array?=?busiest->active;

dst_array?=?this_rq->active;

}

new_array:

idx?=?0;

skip_bitmap:

//從優先級最高的可運行進程開始進行遷移

if(!idx)

idx?=?sched_find_first_bit(array->bitmap);

else

idx?=?find_next_bit(array->bitmap,?MAX_PRIO,?idx);

if(idx?>=?MAX_PRIO)?{

if(array?==?busiest->expired?&&?busiest->active->nr_active)?{

array?=?busiest->active;

dst_array?=?this_rq->active;

gotonew_array;

}

gotoout;

}

//找到對應優先級隊列的隊列末尾的進程,該進程應該是被

//放入的最早的一個進程了

head?=?array->queue?+?idx;

curr?=?head->prev;

skip_queue:

tmp?=?list_entry(curr,?task_t,?run_list);

curr?=?curr->prev;

//判斷該進程能否進行遷移

if(!can_migrate_task(tmp,?busiest,?this_cpu,?sd,?idle))?{

//該優先級別的隊列是否遍歷完畢

if(curr?!=?head)

gotoskip_queue;

idx++;

//該優先級別的任務隊列遍歷完畢,去遍歷下一個優先級的任務隊列

gotoskip_bitmap;

}

schedstat_inc(this_rq,?pt_gained[idle]);

schedstat_inc(busiest,?pt_lost[idle]);

//將隊列遷移到本地的任務隊列

pull_task(busiest,?array,?tmp,?this_rq,?dst_array,?this_cpu);

pulled++;

if(pulled

//該優先級別的隊列是否遍歷完畢

if(curr?!=?head)

gotoskip_queue;

idx++;

//該優先級別的任務隊列遍歷完畢,去遍歷下一個優先級的任務隊列

gotoskip_bitmap;

}

out:

returnpulled;

}

總結

以上是生活随笔為你收集整理的linux进程网络均衡,linux多CPU进程负载均衡解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。