_sock.h
_で定義されているsock
構造体には、非常によく似た2つの属性があります。
sk_wmem_alloc
_、これは「コミットされたキューバイトの送信」として定義されますsk_wmem_queued
_、「永続キューサイズ」として定義私にとって、_sk_wmem_alloc
_は、送信キューに現在割り当てられているメモリの量です。では、_sk_wmem_queued
_とは何ですか?
wmem_queued:送信キューにキューイングされたソケット送信バッファーによって使用され、まだ送信されていないか、まだ確認されていないメモリの量。
ss
manも定義を提供しますが、これは私を実際には啓発しません(IP層がこれと何の関係があるのかわかりません):wmem_alloc:パケットの送信に使用されるメモリ(レイヤー3に送信されたもの)wmem_queued:パケットの送信に割り当てられたメモリ(レイヤー3に送信されていない)
sock_diag(7)
のマニュアルページにも、これらの属性の独自の定義があります:SK_MEMINFO_WMEM_ALLOC:送信キュー内のデータの量。 SK_MEMINFO_WMEM_QUEUED:TCPによってキューに入れられたが、まだ送信されていないデータの量。
これらの定義はすべて異なり、__alloc
_と__queued
_のバリアントがどのように異なるかを明確に説明しているものはありません。
Linuxネットワークスタックに貢献しているEricDumazetにメールを送りましたが、その答えは次のとおりです。
sk_wmem_alloc
は、キューに入れられたskbafterトランスポートスタック:qdiscレイヤーおよびNIC TXリングバッファーのバイト数を追跡します。1MBのデータがTCP書き込みキューにあり、まだ送信されていない(cwnd制限))場合、
sk_wmem_queue
は約1MBになりますが、sk_wmem_alloc
は約0になります
これらの3つのタイプのキュー(ソケットバッファー、qdiscキュー、およびデバイスキュー)が何であるかを理解するための非常に優れたドキュメントは この記事(かなり長い)記事 です。簡単に言うと、ソケットはパケットをqdiscキューに直接プッシュすることから始まり、qdiscキューはパケットをデバイスキューに転送します。 qdiscキューがいっぱいになると、ソケットは独自の書き込みキューにデータのバッファリングを開始します。
ネットワークスタックは、パケットをキューイングの分野に直接配置するか、キューがいっぱいの場合は上位層(ソケットバッファなど)にプッシュバックします。
だから基本的に: sk_wmem_queues
は、ソケットバッファによって使用されるメモリです(sock.sk_write_queue
)while sk_wmem_alloc
は、qdiscおよびデバイスキュー内のパケットによって使用されるメモリです。
TL; DR:manページを信じましょう:-)。彼らは、データがキューに入れられる可能性のある2つの異なる場所があると言います。したがって、合計メモリ使用量を知りたい場合は、2つの値を合計する必要があります。
免責事項:私の主張は十分な情報に基づいた情報源から導き出された結論ですが、私はこれをテスト済みしていません。また、これは非常に長すぎるため、おそらく読みたくないでしょう。
_sk_wmem_alloc
_をGoogleで検索すると、TCP Small Queues(TSQ)の導入に関する書き込みが返されました。
下位層のキューイング自体は、2つの異なるキューで構成されています。最初にqdisc(キューイングディシプリン)[*]があり、次にデバイス内のキューがあります。
TSQコードでは、「sk-> sk_wmem_alloc [は]指定された制限を超えて拡張することは許可されておらず、指定された時間にqdisc/devレイヤーのtcpソケットあたり最大128KB [デフォルト]を許可します。」
=> _sk_wmem_alloc
_には少なくとも qdiscレイヤーとdevレイヤーを含める必要があります。
TSQコードは大きな違いをもたらしました。 「単一の[バルク送信者]によってqdiscに4Mバイトがバックログされなくなりました」。また、「両側のソケットの自動調整で4Mバイトが使用されなくなりました」。
4MBはどこから来たのですか?ここで使用されているqdiscの制限ではありません-それははるかに大きいです。テストでは「標準」FIFO qdisc、デフォルトは1000パケットです。4MB/ 1000は4Kになりますが、これは標準のパケットサイズではありません。テストでは標準の最大化されたTSOパケットを使用しました。サイズ:64K。回答:
Linux 2.6.17には、送信側と受信側の自動調整と4MBのデフォルトの最大ウィンドウサイズがあります。
Linuxで現在実装されているのは、基本的にSemke '98で説明されているものですが、最大最小公平性はありません。
- https://wiki.geant.org/display/public/EK/TCP+Buffer+Auto+Tuning
これを調べると、自動調整は非常に大まかに「2 *帯域幅*遅延の経験則に従う」ことです。これは、「cwnd [輻輳ウィンドウ]:既存のTCP変数(単一接続の場合))を使用して実装されます。これは、使用可能な帯域幅遅延積[BDP]を推定して、保持する適切なデータ量を決定します。飛行中です。」
最後に、「飛行中」のデータの総量は、TCPバッファのサイズによって制限されます。これは、TCPが信頼できるプロトコルであるためです。パケットを物理的に送信するため、データのコピーをバッファに保持する必要があります。パケットが転送中に失われた場合に備えて、再送信できる必要があります。受信者からデータが到着したことを通知されるまで、データを保存し続ける必要があります。安全に。
これは、TSQでのさまざまな結果を説明しています。
まず、送信者はqdisc + deviceに小さなキューを作成します。 TCPウィンドウを段階的に増やして、パスの予備容量を調べます。送信量が増えるため、より大きなキューが構築されます。TCP検出されたパケットは、ドロップすると、元に戻りますが、qdiscにはまだパケットを追加する余地があるため、ドロップする理由はありません。このサイクルは、制限に達するまで続きます...
TSQを使用すると、単一のTCP送信者はqdisc +デバイスキューを128Kを超えて拡張しません。ただし、TSQがないと、4MBの制限に達する可能性があります。
=>ここでmanページがどのように意味をなすかを見てください。
TSQがなければ、_sk_wmem_queued
_は最大4MBに達します。 _sk_wmem_alloc
_は4MBに達し、残りのBDPを差し引いたものになります。
記載されている結果は、物理的な伝送遅延が非常に短いローカルテストからのものです。
遅延(または帯域幅)を増やすと、BDPが増加します。 TSQが_sk_wmem_queued
_を128Kに制限しているにもかかわらず、_sk_wmem_alloc
_は4MBに達する可能性があります。
[*]高度なqdisc機能はルーターで使用されます。異なるパケットに優先順位を付けます。ただし、すべてのLinuxシステムはqdiscレイヤーで一部のパケットをキューに入れます。元々、これは固定数のパケット( "fifo")のキューでした。最近の多くのシステムでは、デフォルトは「fq_codel」になりました。 「codel」は、送信時間によってキューイングを適応的に制限します。デバイスの速度が遅いほど、キューの拡大が許可されます。 「fq_codel」はさらに、より公平な共有を試み、非バルク送信者がバルク送信者よりも先にスキップできるようにして、応答性を向上させます。
2つの変数のうちの1つは、UDPに最も関連しているようです。もう1つは、TCPに特に関連しているようです。 sock_writeable() (_sk_wmem_alloc
_)と sk_stream_is_writeable())の使用法を調べました。 (_sk_wmem_queued
_)。
もちろん、UDPやTCPだけでなく多くのソケットプロトコルがあります。コードを見ると、違いは「信頼性」の実装に関連していることがわかります。
これは、DCCP(Datagram Congestion Control Protocol)と呼ばれるプロトコルがsk_stream_is_writeable()
:-Pと呼ばれる関数を使用している理由を説明します。 DCCPは「信頼できる」プロトコルです。 dccp_poll() を参照してください。
ドロップされたパケットの処理に関連している場合は、_AF_LOCAL
_ストリームソケットの実装のどこにもsk_stream_is_writeable()
の呼び出しが見つからなかった理由も説明されます。 _AF_LOCAL
_データグラムは転送中に失われることはありません。
TCPとUDPの違いは、特に_net/sunrpc/xprtsock.c
_で見られます。
_/**
* xs_udp_write_space - callback invoked when socket buffer space
* becomes available
* @sk: socket whose state has changed
*
* Called when more output buffer space is available for this socket.
* We try not to wake our writers until they can make "significant"
* progress, otherwise we'll waste resources thrashing kernel_sendmsg
* with a bunch of small requests.
*/
static void xs_udp_write_space(struct sock *sk)
{
read_lock_bh(&sk->sk_callback_lock);
/* from net/core/sock.c:sock_def_write_space */
if (sock_writeable(sk))
xs_write_space(sk);
read_unlock_bh(&sk->sk_callback_lock);
}
/**
* xs_tcp_write_space - callback invoked when socket buffer space
* becomes available
* @sk: socket whose state has changed
*
* Called when more output buffer space is available for this socket.
* We try not to wake our writers until they can make "significant"
* progress, otherwise we'll waste resources thrashing kernel_sendmsg
* with a bunch of small requests.
*/
static void xs_tcp_write_space(struct sock *sk)
{
read_lock_bh(&sk->sk_callback_lock);
/* from net/core/stream.c:sk_stream_write_space */
if (sk_stream_is_writeable(sk))
xs_write_space(sk);
read_unlock_bh(&sk->sk_callback_lock);
}
_
sock_writeable()
と_sk_wmem_alloc
_の定義は、「ソケットバッファ」が嘘であることを示唆しています。
上記の最初のセクションでは、qdisc + deviceキューとは異なるTCPソケットバッファを明確に識別しました。それはすべて真実です。しかし、TCPは特別な場合、信頼できる(「ストリーム」)プロトコル。
_man sendmsg
_も参照してください-
ENOBUFS
ネットワークインターフェイスの出力キューがいっぱいでした。これは通常、インターフェイスが送信を停止したことを示しますが、一時的な輻輳が原因である可能性があります。 (通常、これはLinuxでは発生しません。デバイスキューがオーバーフローすると、パケットはサイレントにドロップされます。)
ここでの正確な表現は、UDPソケットバッファについては言及していません。 DPソケット専用の送信バッファはありません。パケットを直接qdiscに詰め込むだけです。 _sk_wmem_alloc
_が「送信バッファーサイズ」を超える場合、sendmsg()
呼び出しはブロック(待機)します。ただし、qdiscに空きがない場合、パケットはサイレントに破棄されます。