既に述べたように、キューイング規則を用いると、 データの送信のされ方を変更できます。 クラスのない (クラスレスな) キューイング規則とは、 おおむねデータを受信し、それを再スケジュール・遅延・破棄するようなものです。
これを用いると、再分割 (subdivision) を用いずに、 インターフェース全体のトラフィックを絞れます。 クラスのある (クラスフルな) 「qdisc を含む qdisc」に進む前に、 この部分を理解しておくことはとても大切です。
これまでのところ、最も広く用いられている規則は pfifo_fast qdisc です (これがデフォルトです)。これは、 なぜこれらの先進的な機能の信頼性がこれほど高いのかも説明してくれます。 これらは「ひとつ余分のキュー」以上のものではないのです。
これらのキューには、それぞれ長所と短所があります。 これらのすべてが、完全にテストされているというわけではありません。
このキューは、名前からわかるとおり、「先入れ先出し (First In, First Out)」 で、つまりどの受信パケットも特殊な扱いは (少なくともそれほどは) されません。 このキューには 3 つのいわゆる「バンド」があります。 各バンドには FIFO のルールが適用されます。 しかし、バンド 0 に待ちパケットがあると、バンド 1 は処理されません。 バンド 1 と バンド 2 も同じ関係にあります。
カーネルは、パケットのいわゆる「サービスのタイプ (Type of Service)」フラグを 尊重し、「最小遅延 (minimum delay)」パケットをバンド 0 に入れるよう計らいます。
このクラスレスかつシンプルな qdisc を、 クラスフルな PRIO qdisc と混同しないこと。 両者は同じように動作しますが、pfifo_fast はクラスレスですから、 tc コマンドで他の qdisc を追加することはできません。
pfifo_fast qdisc は出来合いのデフォルトですから、 ユーザによる設定はできません。 ここではデフォルトでどのように設定されているかを示します。
パケットの優先具合が、カーネルによって、どのバンドに対応付けられるかを決めます。 対応付けはパケットの TOS に従って決まります。TOS は以下のようなものです。
0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | | | | | PRECEDENCE | TOS | MBZ | | | | | +-----+-----+-----+-----+-----+-----+-----+-----+ |
TOS の 4 ビット (「TOS フィールド」) は次のように定義されています。
Binary Decimcal Meaning ----------------------------------------- 1000 8 Minimize delay (md) 0100 4 Maximize throughput (mt) 0010 2 Maximize reliability (mr) 0001 1 Minimize monetary cost (mmc) 0000 0 Normal Service |
この 4 ビットの右にはもう 1 ビットありますから、 TOS フィールドの実際の値はこれらの TOS ビットから決まる値の 2 倍になります。 tcpdump -v -v としたときは、この 4 ビット列の値ではなく、 TOS フィールド全体の値を表示します。 これは、次の表の第一列にある値になります。
TOS Bits Means Linux Priority Band ------------------------------------------------------------ 0x0 0 Normal Service 0 Best Effort 1 0x2 1 Minimize Monetary Cost 1 Filler 2 0x4 2 Maximize Reliability 0 Best Effort 1 0x6 3 mmc+mr 0 Best Effort 1 0x8 4 Maximize Throughput 2 Bulk 2 0xa 5 mmc+mt 2 Bulk 2 0xc 6 mr+mt 2 Bulk 2 0xe 7 mmc+mr+mt 2 Bulk 2 0x10 8 Minimize Delay 6 Interactive 0 0x12 9 mmc+md 6 Interactive 0 0x14 10 mr+md 6 Interactive 0 0x16 11 mmc+mr+md 6 Interactive 0 0x18 12 mt+md 4 Int. Bulk 1 0x1a 13 mmc+mt+md 4 Int. Bulk 1 0x1c 14 mr+mt+md 4 Int. Bulk 1 0x1e 15 mmc+mr+mt+md 4 Int. Bulk 1 |
たくさん数字が並んでいますね。第二列は、 これらに対応する 4 つの TOS ビット列の値です。 続いてそれらの意味する内容が示されています。 例えば 15 は、このパケットは Minimal Monetary Cost, Maximum Reliability, Maximum Throughput, Minimum Delay のすべてを期待しているパケットを意味しています。 私はこれを「強欲なパケット (Dutch Packet)」と呼びたいと思います。 [訳注: 著者の Hubert さんもオランダ人 :-)]
第 4 列は Linux カーネルによる TOS ビット列の解釈です。 どの優先度にマップされるかを示しています。
最後の列は、デフォルトの優先度マップ (priomap) の結果です。 コマンドラインでは、デフォルトの優先度マップは次のようになります。
1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 |
つまり例えば優先度 4 のパケットは、バンド番号 1 へとマップされます (訳注: 優先度は 0 から始まることに注意)。 優先度マップでは高い優先度 (> 7) をリストすることもできます。 これらは TOS マッピングには対応していませんが、 他の方法によって設定できます。
この表は RFC1349 (詳細は直接こちらを) から取ったもので、 各アプリケーションがどのように TOS ビットを設定すると良いかについて示してあります。
TELNET 1000 (minimize delay) FTP Control 1000 (minimize delay) Data 0100 (maximize throughput) TFTP 1000 (minimize delay) SMTP Command phase 1000 (minimize delay) DATA phase 0100 (maximize throughput) Domain Name Service UDP Query 1000 (minimize delay) TCP Query 0000 Zone Transfer 0100 (maximize throughput) NNTP 0001 (minimize monetary cost) ICMP Errors 0000 Requests 0000 (mostly) Responses <same as request> (mostly) |
このキューの長さはインターフェースの設定から決まります。 インターフェース設定は ifconfig や ip で閲覧・設定できます。 キューの長さを 10 にするには、 "ifconfig eth0 txqueuelen 10" と実行します。
このパラメータは tc では設定できません。
トークンバケツフィルタ (Token Bucket Filter: TBF) は 単純な qdisc で、管理者が設定した速度を越えない範囲で到着パケットを通します。 ただし短い時間の突発的なものなら、この値を越えることを許す可能性があります。
TBF は非常に正確で、ネットワークとプロセッサへの負荷も軽いです。 単純にインターフェースの速度を落としたいと思ったときには、 まず最初にこの利用を考えてみるべきです。
TBF の実装はバッファ (バケツ) であり、 これは定期的に仮想的な情報の断片 (トークンと呼ばれます) によって、 一定の割合 (トークン速度) で満たされていきます。 バケツの最も重要なパラメータはサイズで、 保持できるトークンの数を意味します。
バケツに入った各トークンは、 それぞれひとつの受信データパケットをデータキューから拾い、 そしてバケツからは削除されます。 この 2 つの流れ (トークンとデータ) からなるアルゴリズムには、 3 つのシナリオが考えられます。
トークンと「同じ」割合で、TBF にデータが到着する。 この場合各受信パケットは、それぞれ対応するトークンがあるので、 遅延することなしにキューを通過する。
トークンの速度よりも「遅い」割合で、TBF にデータが到着する。 キューに入った受信データパケットの出力に応じて 削除されるトークンは一部分のみなので、 トークンはバケツサイズ一杯にまで溜まっていく。 使われなかったトークンは、 突発的なデータのバーストが起こったような場合に利用でき、 トークンの標準流入速度を越えたデータが送信できる。
トークンの速度よりも「大きい」割合で、TBF にデータが到着する。 この場合、バケツのトークンはすぐに空になってしまい、 TBF はしばらくの間入力を絞る。 これは「過負荷状態 (overlimit situation)」と呼ばれる。 パケットがそのまま入り続けてくる場合には、 パケットは破棄され始める。
後ろの 2 シナリオがとても重要です。 これらは、このフィルタを通過するデータのバンド幅を、 管理者が制御できることを意味しているからです。
トークンが溜まっていくと、制限を越えたデータバーストも、 短いものならロスなしに通過できますが、 過負荷状態が続くとパケットはだんだん遅延していき、ついには破棄されます。
ただし実際の実装では、トークンが対応しているのはバイトであり、 パケットではありません。
ほとんど変更の必要はないでしょうが、 TBF にも調整つまみはついています。 まず、つねに指定できるパラメータです:
limit はトークン待ち状態でキューに入れるバイト数の制限値です。 これは latency パラメータ (パケットが TBF に留まれる時間の最大値) を設定することによっても指定できます。 こちらを計算する際には、バケツのサイズ、トークンの追加速度、 および (指定されていれば) ピーク速度を考慮します。
バイト単位のバケツのサイズです。 これはある瞬間に利用できるトークンの最大値 (バイト数) です。 一般に、絞りたい通信速度が大きい場合には、大きなバッファがいります。 Intel で 10mbit/s を使う場合、 この設定速度にするには少なくとも 10kbyte のバッファが必要です。
バッファが小さすぎると、 時間単位あたりに到着するトークンだけでバケツが溢れてしまうので、 パケットが破棄されてしまうかもしれません。
サイズが 0 のパケットも、使うバンド幅は 0 ではありません。 イーサネットでは、64 バイト以下のパケットはありません。 最小パケット単位 (Minimum Packet Unit) は、 ひとつのパケットが用いるトークンの最小値を定めます。
スピードつまみです。制限については上記を参照のこと。
バケツにトークンが入っていて、これを空にすることが許されるとき、 デフォルトではこの作業は無限の速度で行われます。 これがまずい場合には、以下のパラメータを使って下さい。
トークンが使える状態でパケットが到着すると、 デフォルトではそのパケットはすぐさま、いうならば「光速」で送り出されます。 これは、特に大きなバケツを使っている場合には、 望ましいことではないかもしれません。
peakrate は、バケツを空ける際に許される速さを決めるために用います。 すべてが本に書いてある通りなら、 この動作は「パケットを送って、十分長い間待って、そして次のパケットを送る」 ことによって実現されています。 待ち時間は、ちょうど peakrate で送られるように計算してあります。
しかしながら、Unix でのデフォルトの時間単位は 10ms なので、 パケットサイズの平均を 10.000 ビットとすると、 peakrate は 1mbit/s にしかできません!
1mbit/s の peakrate は、これが通常の速度より遅ければ、 あまり便利とは言えません。 時間単位ごとに送れるパケットの数を増やせば、 より peakrate は大きくできます。 これは実質的には 2 番目のバケツを作るのと同じことです。
この 2 番目のバケツは、デフォルトでは 1 つのパケットでしかなく、 つまり実際にはバケツではありません。
peakrate の最大許容値を計算するには、 mtu の設定値に 100 (あるいは正確には HZ。これは Intel では 100, Alpha では 1024) をかけてください。
シンプルですが、とっても便利な設定を示します。
# tc qdisc add dev ppp0 root tbf rate 220kbit latency 50ms burst 1540 |
はい、なぜ便利なのか説明しましょう。 DSL モデムやケーブルモデムのように、 キューの大きなネットワークデバイスがあるとして、 そのデバイスには (イーサネットインターフェースのような) 高速なデバイスで通信しているとしましょう。 するとアップロードの際に、対話通信が完全にダメになってしまいます。
この理由は、アップロードがモデムのキューを一杯にしてしまうからです。 このキューはおそらく非常に大きく、 実際これによってアップロードのスループットが向上しています。 しかしこれは望ましいことではないでしょう。 キューをあまり大きくせずにおいて、 データの送信時にも他のことができるよう、 対話通信を可能としておきたいことが多いはずです。
上記の実行例では、モデムのキューが必要ない程度にまで、 送信速度を落としています。これでキューは Linux 上にあり、 これに対して制限を課すことができます。
220kbit は、あなたの環境の「実際の」速度 (マイナス数パーセント) にしてください。手持ちのモデムが非常に高速なら、'burst' を少々増やしてください。
確率的不偏キューイング (Stochastic Fairness Queueing: SFQ) は、不偏キューイングのアルゴリズムをシンプルに実装したものです。 これは他のものほど正確ではありませんが、 あまり計算量が必要なく、それでいて非常に公平です。
SFQ でのキーワードは「会話 (または流れ)」で、 これは TCP セッションや UDP ストリームとほとんど同じ意味です。 トラフィックはかなり多い数の FIFO キューに分割され、 各キューがそれぞれの会話に対応します。 そしてトラフィックはラウンドロビン的に送られます。 すなわちデータを送る機会が各セッションに順番に与えられます。
これは非常に公平な動作につながり、 ある特定の会話が残りをかき消してしまうようなことはなくなります。 SFQ には「確率的」と名前がついていますが、 これは SFQ が実際には各セッションごとにキューを割り当てるのではなく、 トラフィックをある限られた数のキューに、 ハッシュアルゴリズムを使って分割するからです。
このハッシュのため、複数のセッションが同じバケツに入ることもあり得ます。 各セッションがパケットを送信する機会を握っているのはこのバケツなので、 実効的な速度は半分になってしまいます。 この状況が目立つのを防ぐために、 SFQ はハッシュアルゴリズムを極めて頻繁に変更し、 あるセッション同士の衝突が、短い時間しか続かないようにしています。
これは重要なポイントですが、 SFQ が有効なのは、実際の出力インターフェースが、 本当に一杯一杯になっている場合に限られます。 そうでなければ、あなたの linux マシンのキューはほぼ空っぽで、 よって何の効果も持ちません。 SFQ を他の qdisc と組み合わせ、 「両方にいい顔をしたい」状況を実現するための方法については後ほど議論します。
特に、ケーブルモデムや DSL ルータに向かうイーサネットに対して SFQ を設定するのは、他の制限を行わないならば的外れです。
SFQ はほとんどチューニングの必要がありません。
ハッシュコードの再設定を行う時間間隔。 設定しないとハッシュは変更されませんが、これはお勧めできません。 たぶん 10 秒で良いでしょう。
次のキューに順番を回すまえに、 あるストリームのキューから吐き出すデータのバイト量。 デフォルトは最も大きなサイズ (MTU サイズ) のパケット 1 つ分です。 MTU よりも小さな値にはしないように!
この SFQ がキューイングするパケットの総数 (これを越えるとパケットを落とし始めます)。
接続速度と実効速度とが同じデバイス (電話線のモデム) に対しては、 この設定を行うと公平性の向上が期待できます:
# tc qdisc add dev ppp0 root sfq perturb 10 # tc -s -d qdisc ls qdisc sfq 800c: dev ppp0 quantum 1514b limit 128p flows 128/1024 perturb 10sec Sent 4812 bytes 62 pkts (dropped 0, overlimits 0) |
800c: というのは、自動的に割り当てられるハンドル番号です。 limit は、このキューでは 128 個のパケットが待機できることを意味しています。 1024 個のハッシュバケツが割り振りに利用でき、 そのうち同時にアクティブになれるのは 128 個です (これ以上の数はキューに入れません!)。 10 秒ごとにハッシュが再設定されます。