部分読み込み時のUnixストリーム補助データはどうなりますか?

部分読み込み時のUnixストリーム補助データはどうなりますか?

だから私はUnixストリームのセカンダリデータについてたくさん読んでいますが、すべての文書に欠けているものの1つは、部分的な読み取りが発生したときに何が起こるべきかということです。

24 バイトのバッファーに次のメッセージを受信したとします。

msg1 [20 byes]   (no ancillary data)
msg2 [7 bytes]   (2 file descriptors)
msg3 [7 bytes]   (1 file descriptor)
msg4 [10 bytes]  (no ancillary data)
msg5 [7 bytes]   (5 file descriptors)

初めてrecvmsgを呼び出すと、msg1全体(およびmsg2の一部?OSはこれを行いますか?)の両方を取得します。 msg2の一部を取得したら、すぐに補助データを取得し、次の読み取り用に保存する必要がありますか?メッセージが実際にデータで何をすべきかを教えてくれることをご存知ですか? msg1で20バイトをオフにしてからrecvmsgを再度呼び出すと、msg3とmsg4の両方を渡しますか? msg3とmsg4の補助データが制御メッセージ構造に接続されていますか?

これを実験的に調べるためにテストプログラムを書くことができますが、私は以下を探しています。文書ストリーミングコンテキストでセカンダリデータがどのように機能するかを説明します。奇妙なことは、正式な情報が見つからないことです。


このテストプログラムで得られた実験結果をここに追加します。

https://github.com/nrdvana/daemonproxy/blob/master/src/ancillary_test.c

Linux 3.2.59、3.17.6

Linuxは、recvmsg呼び出し中に前のセカンダリペイロードを渡す必要がない限り、セカンダリベアラメッセージの一部を別のメッセージの末尾に追加するようです。メッセージのセカンダリデータが送信されると、次のセカンダリデータメッセージを開始するのではなく、単純な読み取りを返します。したがって、上記の例で私が得た読み値は次のようになります。

recv1: [24 bytes] (msg1 + partial msg2 with msg2's 2 file descriptors)
recv2: [10 bytes] (remainder of msg2 + msg3 with msg3's 1 file descriptor)
recv3: [17 bytes] (msg4 + msg5 with msg5's 5 file descriptors)
recv4: [0 bytes]

BSD 4.4、10.0

BSDはLinuxよりも多くのソートを提供し、すぐに短い読み取りを実行できます。今後補助データでメッセージを開始します。ただし、セカンダリベアラメッセージの末尾にセカンダリ以外のベアラメッセージを追加することは可能です。したがって、BSD を使用すると、バッファがセカンダリ ベアラ メッセージより大きい場合、パケットとほぼ同様の動作が発生します。私が得る読書は次のとおりです。

recv1: [20 bytes] (msg1)
recv2: [7 bytes]  (msg2, with msg2's 2 file descriptors)
recv3: [17 bytes] (msg3, and msg4, with msg3's 1 file descriptor)
recv4: [7 bytes]  (msg5 with 5 file descriptors)
recv5: [0 bytes]

すること:

以前のバージョンのLinux、iOS、Solarisなどでどのように発生するのか、それともどのように発生するのかはまだ疑問に思っています。できる将来、このようなことが起こると予想されます。

ベストアンサー1

セカンダリデータは、セグメント(存在する場合)の最初の汎用データオクテットとともにキューにあるかのように受信されます。

-POSIX.1-2017

残りの質問については、状況が少し難しくなります。

...このセクションの目的に応じて、データグラムはレコードを終了し、特別なタイプの補助データとしてソースアドレスを含むデータセグメントと見なされます。

プロトコルを介してデータがソケットに送信されると、データセグメントはキューに入れられます。汎用データセグメントは、送信時にキューの末尾に配置されます。新しいセグメントが前のセグメントと同じタイプのデータを含み、補助データを含まず、以前のセグメントがレコードを終了しない場合、セグメントは論理的に単一のセグメントにマージされます。

受信操作は、複数のセグメントからデータまたは補助データを返すべきではありません。

したがって、最新のBSDソケットはこの抜粋と正確に一致します。これは驚くべきことではありません:-).

POSIX規格はUNIX以降、そしてBSDとSystem Vなどが分離された後に作成されました。主な目的の1つは、既存の動作範囲を理解し、既存の機能のさらなる断片化を防ぐことです。

Linux 実装では BSD コードを参照しません。ここでは別の方法で行動しているようです。

  1. 私が正しく聞いた場合、Linuxはまた、新しいセグメントが現れるときに「セグメント」をマージするように聞こえます。する補助データは含まれていますが、前の段落には含まれていません。

  2. 「Recvmsgの呼び出し中に以前のセカンダリペイロードを渡す必要がない限り、Linuxはセカンダリフォワーダメッセージの一部を別のメッセージの末尾に追加します」というポイントは、標準によって完全に説明されていないようです。 1つの可能な説明は、競争条件に関連しています。 「スニペット」の一部を読むと、補助データが届きます。おそらく、Linuxはこれをセグメントの残りの部分が補助データを含むとは見なさないという意味に解釈します。したがって、新しいセグメントが受信されると、標準に従って、または上記のdiff 1に従ってマージされます。

ポータブルなプログラムを作成するには、この領域を完全に避ける必要があります。セカンダリデータを扱うときは、次を使用する方が一般的です。データグラムソケット。技術的にほとんどPOSIXのようなものを提供することを熱望するすべての奇妙なプラットフォームで作業したい場合は、あなたの質問は暗くてテストされていないコーナーで冒険を離れているようです。


Linuxはまだいくつかの重要な原則に従うと言うことができます。

  1. 「セカンダリデータは、セグメントの最初の一般データオクテットとともにキューにあるかのように受信されます。」
  2. あなたが言ったように、補助データは決して「接続」されません。

しかし、私はLinuxの行動が特別だとは思わない。効果がある、BSD動作と比較したとき。説明されている手順では、Linux関連の回避策を追加する必要があるようです。 Linuxがなぜこれを期待しているのかわかりません。

Linuxカーネルコードを書くと合理的に見えるかもしれませんが、どんなプログラムでもテストも使用もされていません。

あるいは、いくつかのプログラムコードによって実行されてもよい。最大このサブセットで動作しますが、原則として極端な場合は、「バグ」または競合状態がある可能性があります。

Linuxの動作と意図した用途を理解できない場合は、Linuxの「暗くてテストされていない隅」と考えることができると思います。

おすすめ記事