6

Linuxカーネルのマルチプロセッサシステムでロードバランサがどのように機能するかを理解しようとしています.

Linux スケジューラは基本的に runques を使用して、次に実行する必要があるタスクを保存します。現在、マルチプロセッサ システムの状況を考慮して、load_balancer() が実装されている方法について説明します。Robert Loves の本 Linux Kernel Development 2nd edition に記載されています。

最初に、load_balance() は find_busiest_queue() を呼び出して、最もビジーなランキューを決定します。つまり、これは最も多くのプロセスを含むランキューです。現在より 25% 以上のプロセスを持つランキューがない場合、find_busiest_queue() は NULL を返し、load_balance() は戻ります。それ以外の場合は、最もビジーなランキューが返されます。

2 番目に、load_balance() は、最もビジーなランキューのどの優先順位の配列から取得するかを決定します。これらのタスクは比較的長い間実行されておらず、プロセッサのキャッシュにない可能性が高い (つまり、キャッシュがホットではない) ため、期限切れの配列が優先されます。期限切れの優先度配列が空の場合、アクティブな配列が唯一の選択肢です。

次に、優先度の高いタスクを優先度の低いタスクよりも公平に分配することが重要であるため、load_balance() は優先度が最も高い (最小値の) タスクのリストを見つけます。

指定された優先度の各タスクが分析され、実行されていないタスク、プロセッサ アフィニティによって移行が妨げられていないタスク、キャッシュ ホットではないタスクが検出されます。タスクがこの基準を満たす場合、pull_task() が呼び出されて、最もビジーなランキューから現在のランキューにタスクがプルされます。

ランキューが不均衡なままである限り、前の 2 つのステップが繰り返され、より多くのタスクが最もビジーなランキューから現在のランキューに引き出されます。最後に、不均衡が解決されると、現在のランキューのロックが解除され、load_balance() が返されます。

コードは次のとおりです

static int load_balance(int this_cpu, runqueue_t *this_rq,
                        struct sched_domain *sd, enum idle_type idle)
{
        struct sched_group *group;
        runqueue_t *busiest;
        unsigned long imbalance;
        int nr_moved;

        spin_lock(&this_rq->lock);

        group = find_busiest_group(sd, this_cpu, &imbalance, idle);
        if (!group)
                goto out_balanced;

        busiest = find_busiest_queue(group);
        if (!busiest)
                goto out_balanced;

        nr_moved = 0;
        if (busiest->nr_running > 1) {
                double_lock_balance(this_rq, busiest);
                nr_moved = move_tasks(this_rq, this_cpu, busiest,
                                      imbalance, sd, idle);
                spin_unlock(&busiest->lock);
        }
        spin_unlock(&this_rq->lock);

        if (!nr_moved) {
                sd->nr_balance_failed++;

                if (unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2)) {
                        int wake = 0;

                        spin_lock(&busiest->lock);
                        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;
                }
        } else
                sd->nr_balance_failed = 0;

        sd->balance_interval = sd->min_interval;

        return nr_moved;

out_balanced:
        spin_unlock(&this_rq->lock);

        if (sd->balance_interval < sd->max_interval)
                sd->balance_interval *= 2;

        return 0; 
}

私が明確ではないのは、上記のコード struct sched_domain *sd の構造です。私がチェックしたこの構造は、 http: //lxr.linux.no/linux+v3.7.1/include/ linux/sched.h#L895 これは大きな構造なので、わかりやすくするためにリンクを示しました。私が知りたいのは、上記のコードで struct sched_domain の使用は何ですか?

load_balancer() が呼び出されたときにこれが使用されるのはなぜですか? この構造体は何を表していますか?

おそらく http://www.kernel.org/doc/Documentation/scheduler/sched-domains.txtにいくつかの情報が記載され ています。CPU にスケジューリング ドメインが必要なのはなぜですか? これらのドメインは何の略ですか?

4

1 に答える 1

15

スケジューリング ドメインとスケジューラ グループ/CPU グループは、次のようなタスクのスケジューリング プロセスを容易にするのに役立ちます。

  1. CPU 間でタスクを負荷分散します。
  2. 新しいタスクを実行するための CPU の選択。
  3. スリープ状態のタスクが起動したときに実行する CPU を選択します。

これには 2 つの利点があります。

  1. システム内のCPUをグループと階層に非常にうまく編成します。

  2. L2キャッシュを共有するすべての CPU は 1 つのドメインに属し
    ます。L3 キャッシュを共有するすべての CPU は、L2 キャッシュを共有
    するすべてのドメインを含む上位レベルのドメインに属します

ツリーのようなデータ構造で見られる利点は、スケジューラ ドメインおよびグループの利点と同様です。

次の図を参照してください

     _________sd1________
    /                    \
    ----------------------
         l3 cache
    ----------------------
    ---------   ----------
    l2 cache    l2 cache
    ---------   ----------
    cpu0 cpu1   cpu2 cpu3
    \_______/   \________/
      sd0          sd0

 ________sd1_________
/                    \
----------------------
      l3 cache
----------------------
---------   ----------
l2 cache    l2 cache
---------   ----------
cpu4 cpu5   cpu6 cpu7
\_______/   \________/
  sd0          sd0

上に表示されているのは、スケジューラ ドメイン階層です。sd1 は、たまたま sd1 のスケジューラ グループである sd0 を含みます。すべての CPU には、それに関連付けられたスケジューラ ドメイン階層があります。
cpu0->sd=sd0; sd0->parent=sd1.このようにリンク リストを使用して、CPU が属するすべてのスケジューラ ドメインを反復処理できます。

これはどのように役立ちますか?

1. ロード バランシング: cpu0 がアイドル状態で、負荷のかかっている他の CPU を解放するためにタスクをプルする準備ができているとします。 load.ここでは、cpu1.cpu1 からタスクを取得する場合は、より高いレベルのドメイン sd1 に移動します.cpu1 からタスクを移行することを選択した場合は、キャッシュの内容を利用できるため、これが最善の方法です;共有キャッシュ.これが最初の利点です。スケジュールされたドメインは、ハードウェアが提供する利点に基づいて形成されます。

sd1 に行く場合、sd1 の「グループ」、sd0 の両方をプローブします。ここに次の利点があります。sched グループだけに関する情報が必要であり、その中の個々の cpu については気にしません。load(sd0[ cpu2,cpu3]) > load(sd0[cpu0,cpu1]) これが true の場合にのみ、cpu2/3 の負荷が高いかどうかを確認します。スケジューラ ドメインまたはグループがない場合は、状態を確認する必要があります。現在行っているように 1 回の反復ではなく、2 回の反復で cpu2 と cpu3 を実行します。

この問題と解決策を 128 cpu にスケーリングします。どのCPUが負荷を軽減するのに最適かを伝えるものが何もなかったら、それがどれほど混乱していたか想像してみてください。最悪の場合、128個のCPUすべてを反復処理する必要があります。

しかし、スケジューラ ドメインまたはグループを使用すると、たとえば 128 CPU を 16 CPU のグループに分割すると、8 つのグループができます。どれが最も忙しいかを確認すると、8 回の繰り返しになります。次に、最も忙しいグループがわかります。さらに16回の反復。最悪の場合

8+16 = 24 回の反復。この減少は、1 レベルのスケジュール ドメインでのみ発生します。より多くのレベルがあれば、反復回数をさらに減らすことができると想像してください。

要するに、スケジューラーのドメインとグループは、関連するものをスケジュールするための「分割して征服しますが、より有用なものを可能な限り征服する」ソリューションです。

将来誰かが読みたいと思うかもしれない場合に備えて投稿しました。

于 2013-01-10T15:11:50.720 に答える