気になったことのあれこれ。2.6.11のコードリーディングをしていく。CPUのアーキテクチャは書籍に沿ってIntelのx86とする。今回はプロセススケジューリングにおけるロードバラシングについて見ていく。マルチプロセッサ環境でのランキューにおけるロードバランシングLinuxでは対称型マルチプロセッサ(SMP)モデルを採用しており、基本的には全てのCPUが平等である。一般的なマルチプロセッサシステムで、全てのCPUはRAMを共有する。ハイパースレッディング対応のCPUは複数のスレッドを同時に実行することが可能で、単一のコア内に複数のレジスタの組みを保持しており、それらを高速に切り替えることでハイパースレッディングを実現している。メモリへアクセスする際の待ち時間効率的に使用することができ、単一のハイパースレッディング対応の物理CPUはLinuxから複数の論理CPUとして認識されるため、例えば2つの物理CPUを搭載している場合カーネルからは4つの論理CPUとして認識される。CPUとRAMをセットにしノードとして分割する(通常単一のCPUと複数のRAMチップで構成される)。通常のSMPではメモリ調停回路(複数のCPUから行われるメモリアクセスの順序性を担保する回路)に処理が集中するが、NUMAではノード内のローカル(CPUとセットになっている)RAMにアクセスする際には競合はほとんど場合発生しない(逆にノード外のRAMへのアクセスは非常に遅くなる)。http://xtreview.com/review12.htm上記に挙げたSMPの性質は組み合わさっていることも多い。全ての実行可能なプロセスはある単一CPUに対応するランキュー上に存在しており、複数CPUのランキュー上にまたがって存在することはない。これはCPUのハードウェアキャッシュを考えた時に非常に効率的でシステム性能の向上に大きく影響する。多数のプロセスが単一のCPUに集中してしまうこともあるためカーネルでは定期的に複数のCPU間でロードバランシング処理を行い。プロセスを別のCPUに対応するランキューに移し替えるような処理を行う。カーネルは「スケジューリングドメイン」という概念を用いてCPU間の負荷分散を行いマルチコアプロセッサにも対応している。スケジューリングドメインスケジューリングドメインはカーネルが負荷を均等に保つべきCPUの集合である。ドメインは複数のグループから構成されており、カーネルはそのドメイン内のグループ間に負荷の偏りが発生した場合、負荷分散を行う。(1)は2つの物理CPUから成る古典的なマルチプロセッサアーキテクチャで、単一のスケジュールドメインの中に2つのグループが存在し、各グループは単一のCPUに対応している。(2)はハイパースレッディング対応の2つの物理CPU(4つの論理CPU)から成る構成で、スケジューリングドメインは2階層になる。親スケジューリングドメインは2つの子スケジューリングドメインから構成されており、子スケジューリングドメインは単一の物理CPU(2つの論理CPU)を保持する。子スケジューリングは2つのグループを保持しており、各グループが単一の論理CPUと対応している。(3)は2つのノードから成り各ノードが4つの物理CPUを保持するNUMAアーキテクチャで、これもスケジューリングドメインは複数階層になる。上位スケジューリングドメインは2つの下位スケジューリングドメインから構成されており、これはNUMAアーキテクチャのノードに対応する。下位スケジューリングドメインは4つグループを保持しており、各グループは単一の物理CPUに対応している。スケジューリングドメインはsched_domain構造体で表現され、以下のように定義されている。ttwu_wake_balance;#endif};parentメンバは上位スケジューリングドメインが存在しない場合にはnullが設定される。gruopsはドメインが保持するグループで以下のように定義されており、sched_domain構造体のメンバであるgroupsはグループリストの先頭であることがわかる。cpu_power;};sched_domainは物理CPU毎に存在する変数であるphys_domainsが保持しており以下のように定義されている。これは最下層のドメインになる。phys_domains);ハイパースレッディング対応CPUを搭載するハードウェアの場合は、以下のようにcpu_domainsという変数で定義されており、これが最下層ドメインとなる。cpu_domains);rebalance_tick()システムに存在するランキュー間のロードバラシングを行うために、全てのCPU内でtick毎にschedule_tick()関数内から呼び出される。rebalance_tick()は以下のように定義されている。ロードバランシングの処理を呼び出す。タスクを他から取得したためアイドル状態ではなくなる。}}load_balance()上記のrebalance_tick()関数内で呼び出されるこの関数はスケジューリングドメイン間に大きな負荷の偏りがないか確認し、偏りがあれば最も負荷の高いグループからローカルCPUのランキューにプロセスを移動することでロードバランシングを行う。load_balance()関数は以下のように定義されている。当該フラグを見てmitigationカーネルスレッドはロードバラシングを走らせる。次のバランシング処理のインターバルを大きくする。0;}ちなみにmitigationカーネルスレッドは以下のように定義されており、上記のactive_balanceフラグを参照しているのがわかる。...}move_tasks()上記のload_balance()関数内で呼び出され、実際に別のランキューからローカルのキュー(両ランキューともロックする必要がある)にタスクを移動し移動したタスク数を戻り値として返す。当該関数は以下のように定義されている。移動したプロセス数}スケジューリング関連のシステムコールスケジューリングに関するシステムコールは基本的に優先度を下げることは可能だが、優先度をあげる場合には特権が必要となる。nice()nice()システムコールはnice値を変更する。現在はsetpriority()に置き換えられており後方互換性のために残っている(setpriority()システムコールの方が処理は遅い)。0;}上記で呼び出されているset_user_nice()の定義は以下。setpriority()nice()システムコールはカレントプロセス、すなわち呼び出したプロセスの優先度を変更するが、getpriority()及びsetpriority()は指定したプロセスグループの優先度を変更する。getpriority()は指定したプロセスグループ内に存在する全てのプロセスのうち、最も小さい値(最も高い優先度値)を戻り値として返す。setpriority()は指定したプロセスグループ内の全てのプロセスに指定の優先度値を設定する。setpriority()は以下のように定義されている。sched_setaffinity()sched_getaffinity()システムコールはプロセスのCPUアフィニティマスク(プロセスの実行を許可するCPUのマスク)であるcpus_allowedメンバの値の取得を行い、sched_setaffinity()システムコールはCPUアフィニティマスクの設定を行う。sched_getaffinity()は以下のように定義されている。retval;}上記のsched_setaffinity()では各IDやケーパビリティのチェックのみを行う。sched_setaffinity()内で呼び出されているset_cpus_allowed()は以下のように定義されている。»Profile低レイヤ好きのソフトウェアエンジニア。(1)はてなブログをはじめよう!k--onishiさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。