探索Linux超線程感知的調度算法

     在linux培訓教程中曾有一段提到過超線程,那我們來探索一下linux超線程告知的調度算法。

     什麼是超線程?

   超線程是英特爾所研發的一種技術,於2002年發佈。超線程的英文是HT技術,全名爲Hyper-Threading,中文又名超線程。超線程技術原先只應用於Xeon處理器中,當時稱爲Super-Threading。之後陸續應用在Pentium 4中,將技術主流化。早期代號爲Jackson。

      超線程工作原理是什麼?

 超線程是通過採用特殊的硬件指令,可以把兩個邏輯內核模擬成兩個物理超線程芯片,在單處理器中實現線程級的並行計算,同時在相應的軟硬件的支持下大幅度的提高運行效能,從而實現在單處理器上模擬雙處理器的效能。其實,從實質上說,超線程是一種可以將CPU內部暫時閒置處理資源充分調動起來的技術。

    超線程感知調度優化算法

(1)共享運行隊列
  在對稱多處理SMP(Symmetrical Multi-Processing)環境中,)調度器爲每個CPU分配了一個運行隊列,避免了多CPU共用一個運行隊列帶來的資源競爭。Linux會將超線程CPU中的兩個邏輯CPU視爲SMP的兩個獨立CPU,各維持一個運行隊列。但是這兩個邏輯CPU共享cache等資源,沒有體現超線程CPU的特性。因此引入了共享運行隊列的概念。HT-aware scheduler patch在運行隊列struct runqueue結構中增加了nr_cpucpu兩個屬性,nr_cpu記錄物理CPU中的邏輯CPU數目,CPU則指向同屬CPU(同一個物理CPU上的另一個邏輯CPU)的運行隊列,Linux中通過調用sched_map_runqueue( )函數實現兩個邏輯CPU的運行隊列的合併。sched_map_runqueue( )首先會查詢系統的CPU隊列,通過phys_proc_id(記錄邏輯CPU所屬的物理CPUID)判斷當前CPU的同屬邏輯CPU。如果找到同屬邏輯CPU,則將當前CPU運行隊列的cpu屬性指向同屬邏輯CPU的運行隊列。
  (2)支持被動的負載均衡
  用中斷驅動的均衡操作必須針對各個物理CPU,而不是各個邏輯CPU。現有的調度程序不會將這種情形認爲是失衡的。在調度程序看來,似乎是第一個物理處理器上的兩個CPU運行1-1任務,而第二個物理處理器上的兩個CPU運行0-0任務。
  在2.6.0版之前,Linux只有通過load_balance( )函數才能進行CPU之間負載均衡。當某個CPU負載過輕而另一個CPU負載較重時,系統會調用load_balance( )函數從重載CPU上遷移線程到負載較輕的CPU上。只有系統最繁忙的CPU的負載超過當前CPU負載的25% 時才進行負載平衡。找到最繁忙的CPU(CPU)之後,確定需要遷移的線程數爲源CPU負載與本CPU負載之差的一半,然後按照從expired 隊列到active 隊列、從低優先級線程到高優先級線程的順序進行遷移。
  在超線程系統中進行負載均衡時,如果也是將邏輯CPU等同於SMP環境中的單個CPU進行調度,則可能會將線程遷移到同一個物理CPU的兩個邏輯CPU上,從而導致物理CPU的負載過重。
  在2.6.0版之後,Linux開始支持NUMA(Non-Uniform Memory Access Architecture)體系結構。進行負載均衡時除了要考慮單個CPU的負載,還要考慮NUMA下各個節點的負載情況。
  Linux的超線程調度借鑑NUMA的算法,將物理CPU當作NUMA中的一個節點,並且將物理CPU中的邏輯CPU映射到該節點,通過運行隊列中的node_nr_running屬性記錄當前物理CPU的負載情況。
  Linux通過balance_node( )函數進行物理CPU之間的負載均衡。物理CPU間的負載平衡作爲rebalance_tick( )函數中的一部分在load_balance( )之前啓動,避免了出現一個物理CPU運行1-1任務,而第二個物理CPU運行0-0任務的情況。balance_node( )函數首先調用find_
  busiest_node( )找到系統中最繁忙的節點,然後在該節點和當前CPU組成的CPU集合中進行load_balance( ),把最繁忙的物理CPU中的線程遷移到當前CPU上。之後rebalance_tick( )函數再調用load_balance(工作集爲當前的物理CPU中的所有邏輯CPU)進行邏輯CPU之間的負載均衡。
  (3)支持主動的負載均衡
  當一個邏輯CPU 變成空閒時,可能造成一個物理CPU的負載失衡。例如:系統中有兩個物理CPU,一個物理CPU上運行一個任務並且剛剛結束,另一個物理CPU上正在運行兩個任務,此時出現了一個物理CPU空閒而另一個物理CPU忙的現象。
  Linux中通過active_load_balance( )函數進行主動的負載均衡,active_load_balance( )函數用於在所有的邏輯CPU中查詢該CPU的忙閒情況。如果發現由於超線程引起的負載不平衡(一個物理CPU的兩個邏輯CPU都空閒,另一個物理CPU的兩個邏輯CPU都在運行兩個線程),則喚醒一個需要遷移的線程,將它從一個忙的物理CPU遷移到一個空閒的物理CPU上。
  active_load_balance( )通過調用cpu_rq( )函數得到每一個邏輯CPU上的運行隊列。如果運行隊列上的當前運行線程爲idle線程,則說明當前邏輯CPU爲空閒;如果發現一個物理CPU兩個邏輯CPU都爲空閒,而另一個物理CPU中的兩個邏輯CPU的運行隊列爲繁忙的情況,則說明存在超線程引起的負載不均衡。這時當前CPU會喚醒遷移服務線程(migration_thread)來完成負載均衡的線程遷移。
  (4)支持超線程感知的任務挑選
  在超線程處理器中,由於cache資源爲兩個邏輯處理器共享,因此調度器在選取一個新任務時,必須確保同組的任務儘量共享一個物理CPU,從而減少cache失效的開銷,提高系統的性能。而傳統的調度器只是簡單地爲邏輯CPU選取一個任務,沒有考慮物理CPU的影響。
  嵌入式進行線程切換時會調用schedule( )函數進行具體的操作。如果沒有找到合適的任務schedule()函數,則會調度idle線程在當前CPU上運行。在超線程環境中Linux調度idle線程運行之前會查詢其同屬CPU的忙閒狀況。如果同屬CPU上有等待運行的線程,則會調用一次load_balance( )函數在兩個同屬CPU之間作一次負載均衡,將等待運行的線程遷移到當前CPU上,保證優先運行同屬CPU上的任務。
  (5)支持超線程感知的CPU喚醒
  傳統的調度器只知道當前CPU,而不知道同屬的邏輯CPU。在超線程環境下,一個邏輯CPU正在執行任務時,其上的一個線程被喚醒了,此時,如果它的同屬邏輯CPU是空閒的,則應該在同屬邏輯CPU上運行剛剛喚醒的任務。
    嵌入式通過wake_up_cpu( )函數實現CPU喚醒,在try_o_wakeuppull_taskmove_task_away加入了wake_up_cpu( )函數的相應調用點。wake_up_cpu()首先查詢當前CPU是不是空閒的,如果當前CPU爲空閒,則調用resched_cpu( )函數啓動調度器,將喚醒的線程調度到當前CPU執行;否則查找其同屬邏輯CPU。如果同屬邏輯CPU是空閒的,則將喚醒的線程調度到同屬邏輯CPU上執行;否則比較喚醒的線程和當前CPU上運行的線程的優先級。如果喚醒的線程的優先級高,或者優先級相等但是時間片多,則進行線程切換,在當前CPU上調度執行喚醒的線程。如果上述條件都不滿足,最後比較喚醒的線程和當前CPU的同屬邏輯CPU上運行的線程的優先級,如果喚醒的線程的優先級高,或者優先級相等但是時間片多,則在同屬邏輯CPU上調度執行喚醒的線程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章