sock
で定義されている構造にはsock.h
非常によく似ている2つの属性があります。
sk_wmem_alloc
、「コミットされた転送キューバイト」として定義されていますsk_wmem_queued
、「持続キューサイズ」として定義
私にとって、これはsk_wmem_alloc
現在転送キューに割り当てられているメモリ量です。しかし、何ですかsk_wmem_queued
?
引用する
- ~によるとこのStackOverflowの答え:
wmem_queued: 転送キューで待機中で、まだ転送または承認されていないソケット転送バッファが使用するメモリ量。
- その
ss
人も正義を下しましたが、私にはあまり理解できませんでした。 (IP層がこれとどのように関連しているのか理解できません。)wmem_allocation:パケット転送に使用されるメモリ(レイヤ3に転送) wmem_queued:パケット転送用に割り当てられたメモリ(まだレイヤ3に転送されていません)
- もう誰かがいます。LKMLでも同様の質問が出ました。しかし、答えはありません。
- マニュアル
sock_diag(7)
ページには、次の属性の独自の定義もあります。SK_MEMINFO_WMEM_ALLOC:送信キュー内のデータ量。 SK_MEMINFO_WMEM_QUEUED:TCPで待機しているがまだ送信されていないデータ量。
これらの定義はすべて異なり、変形がどのように_alloc
異なるかを明確に説明する定義はありません。_queued
答え1
Linuxネットワーキングスタックの寄稿者であるEric Dumazetに電子メールを送信しましたが、答えは次のとおりです。
sk_wmem_alloc
skb がキューに入れたバイト数を追跡します。後ろにトランスポートスタック:qdisc層とNIC TXリングバッファ。まだ送信されていないTCP書き込みキューに1MBのデータがある場合(cwnd制限)、
sk_wmem_queue
これは約1MBですが、sk_wmem_alloc
約0です。
これら3つのタイプのキュー(ソケットバッファ、qdiscキュー、デバイスキュー)を理解するのに非常に良いドキュメントは次のとおりです。この記事(かなり長いです)。つまり、ソケットは最初にパケットをqdiscキューに直接プッシュし、次にデバイスキューに転送します。 qdisc キューがいっぱいになると、ソケットは自己書き込みキューでデータのバッファリングを開始します。
ネットワークスタックは、パケットをキューイングルールに直接配置するか、キューがいっぱいになった場合にパケットを上位層(たとえばソケットバッファ)に戻します。
したがって、デフォルトではソケットバッファ()で使用されるメモリですが、sk_wmem_queues
qdiscおよびデバイスキューのパケットで使用されるメモリです。sock.sk_write_queue
sk_wmem_alloc
答え2
要約:マニュアルページを信じてください:-)。彼らは、データが2つの異なる場所で待機できると言います。したがって、合計メモリ使用量を知りたい場合は、2つの値を追加する必要があります。
免責事項:私の主張は情報豊富な情報源からのものですが、そうではありません。テスト済みです。これ。しかも長すぎて読みたくないかもしれません。
Googleの検索sk_wmem_alloc
結果は、TSQ(TCP Small Queue)の導入に合わせて機能します。
- TCP小さなキュー、LWN.net 2012。
- tcp: TCP 小さなキュー、カーネルコミット(コード変更+説明)。
サブキュー自体は、2 つの異なるキューで構成されます。まず、qdisc(キューイングルール)[*]が出て、その後にデバイス内部のキューが続きます。
TSQコードでは、「sk->sk_wmem_allocは[許可されていません]指定された制限を超えて増加します。qdisc / dev層の各tcpソケットは、[デフォルト]与えられた時間に〜128KBを超えないように許可されています。」
=>sk_wmem_alloc
必ず含める必要があります少なくともqdiscとdevレイヤー。
TSQコードは多くの役に立ちます。 「もはや単一の[バルク送信者]によるqdiscに4MBのバックログがありません。"また、「両側ソケットの自動サイズ変更は4MBを使用しなくなりました。」
4MBはどこから来たか。これはここで使用されるqdiscの制限ではありません。はるかに大きいです。このテストでは、デフォルト値が1000パケットの「標準」FIFO qdiscを使用します。 4MB/1000は4Kですが、標準のパケットサイズではありません。このテストでは、標準の最大TSOパケットサイズである64Kを使用します。答え:
Linux 2.6.17 には、発信者と受信者の自動サイズ変更機能と、4 MB のデフォルトの最大ウィンドウサイズ機能があります。
Linuxの現在の実装は基本的にSemke '98で説明されていますが、最大-最小公正な共有はありません。
-https://wiki.geant.org/display/public/EK/TCP+Buffer+Auto+Tuning
自動拡張はおよそ「2*帯域幅*待ち時間」の法則に従います。これは、「cwnd [輻輳ウィンドウ]:転送を続けるために適切なデータ量を決定するために使用可能な帯域幅遅延積(BDP)を推定する既存のTCP変数(単一接続用)」を使用して実装されます。
最後に、「転送中」のデータの総量はTCPバッファサイズによって制限されます。これは、TCPが信頼できるプロトコルであるためです。物理的にパケットを送信した後も、バッファにデータのコピーを保持する必要があります。送信中にパケットが失われた場合に備えて再送信できる必要があります。私たちは、受信者がデータが安全に到着したと言うまでデータを保存し続けなければなりません。
これはTSQのさまざまな結果を説明しています。
まず、送信者はqdisc + deviceに小さなキューを作成します。 TCP は、パスの空き容量を検出するためにウィンドウを徐々に増やします。より多く送信するので、より大きなキューが構築されます。 TCP は、パケットがドロップされたことを検出するとバックオフします。しかし、qdiscにはまだより多くのパケットを格納するスペースがあるため、パケットをドロップする理由はありません。このサイクルは限界に達するまで続きます。
TSQでは、単一のTCP送信者はqdisc +デバイスキューを128K以上に増やしません。ただし、TSQがない場合は4MBの制限に達する可能性があります。
=>こちらのマニュアルページの内容を確認してください。
TSQがない場合、sk_wmem_queued
最大値は4MBです。 sk_wmem_alloc
残りのBDPを除くと4MBになります。
指定された結果は、物理的な伝送遅延が非常に短いローカルテストからのものです。
待ち時間(または帯域幅)を増やすと、BDPが増加します。 TSQは128Kに制限されていますが、sk_wmem_queued
まだ4MBに達することができます。sk_wmem_alloc
[*]さまざまなパケット優先順位の指定など、ルータの高度なqdisc機能を使用してください。ただし、すべての Linux システムは、qdisc レイヤーの一部のパケットをキューに入れます。最初は固定数のパケットキュー(「fifo」)です。多くの最新システムでは、デフォルトは「fq_codel」です。 「codel」は、送信時間に応じてキューを適応的に制限する。デバイスの速度が遅いほど、許容されるキューのサイズは小さくなります。 「fq_codel」はまた、より公平な共有を提供し、一括送信者ではない人が一括送信者をスキップできるようにすることで応答性を向上させようとします。
初期ノート
これら2つの変数の1つがUDPに最も関連しているようです。もう一つはTCPに最も関連しているようです。用途を確認しました。ソックス_書き込み可能()(sk_wmem_alloc
)とsk_stream_is_writeable()(sk_wmem_queued
)。
もちろん、UDPとTCPに加えて、より多くのソケットプロトコルがあります。コードを見ると、違いが実装「信頼性」に関連していることがわかります。
これはDCCP(データグラムCongestion Control Protocol) では :-P という機能を使用しています。 DCCPは「信頼できる」プロトコルです。バラよりsk_stream_is_writeable()
dccp_poll()。
ドロップされたパケットの処理に関連している場合、 sk_stream_is_writeable()
ストリームソケット実装がその呼び出しを見つけることができない理由も説明されます。データグラムは転送中に失われません。AF_LOCAL
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 +デバイスキューと区別されるTCPソケットバッファを明確に識別しました。これはすべて本当です。ただし、TCPは特別なケースであり、信頼できる(「ストリーミング」)プロトコルです。
見学することもできますman sendmsg
-
ENOBUFS
ネットワークインタフェースの出力キューがいっぱいです。これは通常、インターフェイスが送信を停止したことを示しますが、一時的な輻輳によっても発生する可能性があります。 (通常、Linuxではこれは発生しません。デバイスキューがオーバーフローすると、パケットは自動的に破棄されます。)
ここでの正確な表現にはUDPソケットバッファは記載されていません。 UDPソケットには専用の転送バッファがありません。。私たちはパケットをqdiscに直接入力します。sk_wmem_alloc
「転送バッファサイズ」を超えると、sendmsg()
呼び出しはブロック(待機)されます。ただし、qdisc にスペースがない場合、パケットは自動的に廃棄されます。