CFSスケジューラー(CFSスケジューラー)

以前、O(n)およびO(1)スケジューラーの実装原理を共有し、各スケジューラーが直面する欠点と問題についても学びました。一般に、O(1)スケジューラーはO(n)スケジューラーでは解決できない問題を解決するように見え、サーバー上のLinux2.4カーネルでO(1)スケジューラーの変形は可能ですが、Linux2です。 4日以降、モバイルデバイスの普及に伴い、ケイトンの問題の顔が徐々に明らかになり、CFSスケジューラーが登場しました。

 

このセクションでは、CFSスケジューラーの実装に焦点を当てます。CFSコードを学ぶ前に、まずCFSの実装原理を見て、そのインとアウトを理解し、CFSスケジューラーをこのように設計する必要がある理由は、基本的にCFSスケジューラーを習得することです。 。

 

CFSの紹介

Complete Fair Scheduler(CFS)は、2017年にLinux 2.6.23バージョンに初めて統合され、これまでシステムのデフォルトスケジューラでした。カーネルの記事のsched-design-CFS.txtドキュメントには、CFSスケジューラの簡単な紹介があります。

CFSの設計の80%は1文で要約できます。CFSは基本
的に、実際のハードウェアで「理想的で正確なマルチタスクCPU」をモデル化しています。

この文の意味は、CFSの設計の80%が「実際のハードウェアで公平で正確なマルチタスクCPUを実現する」という文にまとめられることです。

「理想的で正確なマルチタスクCPU」という言葉はどういう意味ですか?どうやってそれを理解していますか?例を通して説明しましょう

「理想的なマルチタスクCPU」とは、(存在しない:-))CPUであり、100%の物理パワーを持ち、各タスクを正確に等しい速度で並列に、それぞれ1 / nr_running速度で実行できます。例:2つのタスクが実行されている場合、
それぞれが50%の物理パワーで実行されます-つまり、実際には並列です。

カーネルのドキュメントにはそう書かれています。「理想的なマルチタスクCPU」とは、各タスクが1 / nr_running_speedで同時に実行されることです。つまり、各プロセスがCPUを同時に分割する時間は同じです。たとえば、2つのプロセスが実行されている場合、各プロセスはCPU時間の50%を占めます。

例を挙げます:

2つのバッチプロセスは、合計で10ミリ秒しか実行できません。

実際の状況:各プロセスは5ミリ秒実行され、100%のCPU使用率を占有します

理想的な状況:各プロセスは10ミリ秒実行され、CPU使用率の50%を占有します。

理想的な状況は、CFSが言及する「理想的なマルチタスクCPU」です。

上記の例は、シングルコアCPUとプロセッシングコアが1つしかないCPUでは一度に1つのプロセスしか実行できず、別のプロセスが待機する必要があるため、完全に不可能です。そしてCFSは完全な公正なスケジューリングを達成することです、それは何をすべきですか?

 

完全な公正なスケジューリングを実現する方法

O(n)スケジューラーとO(1)スケジューラーでは、対応するタイムスライス、つまりタイムスライスが優先順位によって割り当てられています。そして、これらのタイムスライスは固定されています。たとえば、O(n)スケジューラのnice0に対応するタイムスライスは60ミリ秒です。CFSスケジューラには、タイムスライスの概念はありません。代わりに、プロセスの実行可能時間は、現在のシステムで実行可能なプロセスの総数に基づいて計算されます。

O(n)スケジューラーおよびO(1)スケジューラーでは、通常のプロセスが対応するタイムスライスをnice値で取得します。nice値が大きいほど、取得されるタイムスライスが多くなり、実行される可能性が高くなります。CFSスケジューラーでは、重みの概念が導入され、nice値が対応する重みに変換されます。優先度が高いほど、対応する重みが大きくなり、より多くのCPU時間を取得できることを意味します。

                                   次に、CPU時間=プロセスの重量/実行可能なプロセスの総重量プロセスのCPU時間= プロセスの重量/実行可能なプロセスの総重量

CFSは、一定の時間実行することでプロセスが公平になるようにすることです。プロセスが占める時間は、実行可能なプロセス全体の合計重量に対するプロセスの重みの比率です。

例:合計10msの時間、シングルコアCPU

  • プロセスの優先順位は同じです:

2つのプロセスの優先度が同じで、対応する重みが同じ場合、各プロセスのCPU時間は5msです。5つのプロセスがある場合、各プロセスのCPU時間は2msです。合計で10プロセスの場合、各プロセス1msのCPU時間。

  • プロセスの優先順位は異なります。

2つのプロセスの優先度が異なる場合、たとえば、Aプロセスのnice値は0で、Bのnice値は1です。Aプロセスの優先度は高く、重みは大きくなります。Aの重みが6で、Bの重みが4であるとします。次に、AはCPU時間の2/3を使用し、BはCPU時間の1/3を使用します。

このようにして、公平性が達成され、各プロセスは、各子の重量比率の下でCPU時間の異なるシェアを占有します。

人生と組み合わせて例を見てみましょう:

同社は年末賞を発行しており、一般的に部門の総パッケージ(CPU時間)は決まっている。公平を期すために、ボスは全員に同じボーナスを与えることはありません。これは不公平です。これは、会社のパフォーマンス、仕事の深刻な態度(プロセスの重み)で測定されます。たとえば、Zhang XXは非常に難しく、残業が多いことが多く、プロセスが移動し、年末のボーナス(プロセスがCPU時間を消費する)がより頻繁になります。Liu XXは遅れることが多く、誰も仕事を休んでいないため、年末のボーナス(プロセスがCPU時間を消費する)はめったに送信されません。これは公平に思えます。

 

CFSスケジューラーがプロセスを選択する方法

CFSの目標は、各プロセスが一定期間にわたって公平性を実現できるようにすることです。これは、プロセスの重みに従ってCPU時間を分割することです。重みが大きいほど、より多くのCPU時間が分割され、割り当てられるCPU時間が多くなるほど、CPUを取得する可能性が高くなります。

CFSスケジューリングは、プロセスの仮想時間vruntimeを実行するプロセスを選択することです。vruntimeの計算式は以下のとおりです。

vruntime = (wall_time * NICE0_TO_weight) / weight

その中で、wall_timeはプロセスの実際の実行時間を表し、NICE0_TO_Weightは0に等しいniceの値に対応する重みを表し、weightはプロセスの対応する重みです。vruntimeの値は、実際の実行時間にnice0に対応する重みを掛け、プロセスの重みで割ったものであることがわかります。

/*
 * Nice levels are multiplicative, with a gentle 10% change for every
 * nice level changed. I.e. when a CPU-bound task goes from nice 0 to
 * nice 1, it will get ~10% less CPU time than another CPU-bound task
 * that remained on nice 0.
 *
 * The "10% effect" is relative and cumulative: from _any_ nice level,
 * if you go up 1 level, it's -10% CPU usage, if you go down 1 level
 * it's +10% CPU usage. (to achieve that we use a multiplier of 1.25.
 * If a task goes up by ~10% and another task goes down by ~10% then
 * the relative distance between them is ~25%.)
 */
const int sched_prio_to_weight[40] = {
 /* -20 */     88761,     71755,     56483,     46273,     36291,
 /* -15 */     29154,     23254,     18705,     14949,     11916,
 /* -10 */      9548,      7620,      6100,      4904,      3906,
 /*  -5 */      3121,      2501,      1991,      1586,      1277,
 /*   0 */      1024,       820,       655,       526,       423,
 /*   5 */       335,       272,       215,       172,       137,
 /*  10 */       110,        87,        70,        56,        45,
 /*  15 */        36,        29,        23,        18,        15,
};

このテーブルはnice値と重みの変換です。このテーブルは計算されています。コードでvruntimeを計算する必要がある場合は、nice値に基づいてテーブルをチェックするだけで済みます。

ノートからわかるように、ニースがステップを増やすと、CPU時間は10%減少し、ニースがステップずつ減少すると、10%のCPU時間を獲得します。

上記のvruntimeの計算式から、niceが0に等しいプロセスの仮想時間は物理時間に等しいと結論付けることができます。プロセスの重みが大きい場合、対応するプロセスのvruntimeは小さくなり、プロセスの重みが小さい場合、対応するvruntimeは大きくなります。

CFSスケジューリングの原則は、常に最小のvruntimeを持つプロセスをスケジューリングに選択することです。最小のvruntimeを持つプロセスの重みが大きく、優先順位が高いほど、CPU時間は長くなります。

 

:合計6ミリ秒で、3つのプロセスがあり、1つのプロセスAのウェイトは1024、別のプロセスBのウェイトは335、プロセスCのウェイトは3121です。

プロセスA vruntime =(2ms * 1024)/ 1024 = 2ms、CPU使用率= 1024 /(1024 + 335 + 3121)= 22%

プロセスB vruntime =(2ms * 1024)/ 335 = 6ms、CPU使用率= 335 /(1024 + 335 + 3121)= 7%

プロセスC vruntime =(2ms * 1024)/ 3121 = 0.65ms、CPU使用率= 3121 /(1024 + 335 + 3121)= 70%

見える

  1. 各CPU使用率は50%異なります。nice値が1ステップ増えるたびに、CPU使用率には10%の差があるためです
  2. プロセスの重みが大きいほど、分母が大きくなり、vruntimeが小さくなり、次にプロセスが選択されるときに優先順位が高くなります
  3. nice0 = 1024の重みを持つプロセスの仮想時間は、物理時間と同じです
  4. 重みが大きいほど仮想時間が短くなり、対応する仮想タイムラインの実行が速くなることが理解できます。
  5. 重みが小さいほど、仮想時間が長くなり、対応する仮想タイムラインの実行が遅くなります

 

スケジュール期間(sched_period)

プロセスが占有するCPU時間は、システムで実行できるプロセスの総重量に対するプロセスの重量の比率に基づいていると前に述べました。

CPU時間=プロセスの重量/実行可能なプロセスの総重量プロセスの                    CPU時間= プロセスの重量/実行可能なプロセスの総重量

たとえば、同じ優先度の2つのプロセスの合計時間は10ミリ秒で、各プロセスには5ミリ秒かかります。システムで実行できるプロセスの数が徐々に増えると、各プロセスが占有するCPU時間はますます小さくなり、ゼロに近づきます。これにより、プロセスの前にコンテキストの切り替えが頻繁に発生し、CPU時間のほとんどがプロセスのコンテキストの切り替えの処理に使用されるため、システムの効率が低下します。

この問題のために、CFSにスケジューリングサイクルが導入されています。スケジューリングサイクルの計算は次のとおりです。

/*
 * The idea is to set a period in which each task runs once.
 *
 * When there are too many tasks (sched_nr_latency) we have to stretch
 * this period because otherwise the slices get too small.
 *
 * p = (nr <= nl) ? l : l*nr/nl
 */
static u64 __sched_period(unsigned long nr_running)
{
	if (unlikely(nr_running > sched_nr_latency))
		return nr_running * sysctl_sched_min_granularity;
	else
		return sysctl_sched_latency;
}

static unsigned int sched_nr_latency = 8;
unsigned int sysctl_sched_latency			= 6000000ULL;
unsigned int sysctl_sched_min_granularity			= 750000ULL;

コメントから判断すると、この関数の目的は、各プロセスを1回実行できるようにすることです。システム内のプロセス数が徐々に増加する場合は、スケジュール期間を長くする必要があります。

プロセス数が8未満の場合、スケジューリング期間はスケジューリング遅延と等しく、6msと等しくなります。システム内のプロセス数が8より大きい場合、スケジューラーサイクルはプロセス数に0.75msを掛けたものに等しくなります。sysctl_sched_min_granularityは、スケジューリングサイクルで少なくとも0.75msの実行を保証するプロセスとして理解できます。

 

CFSの概要:

  • O(n)とO(1)の両方のスケジューラで、固定タイムスライスにはnice値が割り当てられます。CFSにはタイムスライスの概念はありません。
  • プロセスの重みは、CFSスケジューラでのプロセスの静的優先度によって計算されます。プロセスの重みは、プロセスが取得する必要があるCPU時間の割合を表します
  • プロセスの重みとプロセスの実際の実行時間によって、プロセスのvruntime仮想時間を計算します。
  • プロセスが実行キューに追加されると、スケジューラは常にプロセスのvruntimeを更新して公平性を実現します
  • スケジューラーがスケジュールするたびに、実行キュー内の仮想時間が最小のプロセスのみが選択されます。このプロセスが一定期間実行されると、vruntimeは大きくなります
  • このとき、スケジュールする必要がある場合は、実行する新しい最小vruntimeプロセスを再選択する必要があります。最後にスケジュールされたプロセスは、vrumtimeの値に従って実行キュー内の位置を選択する必要があります。
187件の元の記事を公開 108 件を獲得 37万回表示

おすすめ

転載: blog.csdn.net/longwang155069/article/details/104512696