Was passiert mit Unix-Stream-Zusatzdaten bei teilweisen Lesevorgängen?

18

Ich habe also viele Informationen zu Unix-Stream-Zusatzdaten gelesen, aber eine Sache, die in der gesamten Dokumentation fehlt, ist, was passieren soll, wenn ein Teil gelesen wird?

Angenommen, ich empfange die folgenden Nachrichten in einem 24-Byte-Puffer

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)

Beim ersten Aufruf von recvmsg erhalte ich die gesamte Nachricht msg1 (und einen Teil von msg2? Tut das das Betriebssystem jemals?). Wenn ich einen Teil von msg2 erhalte, erhalte ich die Zusatzdaten sofort und muss sie für den nächsten Lesevorgang speichern Wann weiß ich, was die Nachricht tatsächlich von mir verlangte, mit den Daten zu tun? Wenn ich die 20 Bytes von msg1 freigebe und dann recvmsg erneut aufrufe, wird es dann jemals msg3 und msg4 gleichzeitig liefern? Werden die Zusatzdaten von msg3 und msg4 in der Kontrollnachrichtenstruktur verkettet?

Während ich Testprogramme schreiben konnte, um dies experimentell herauszufinden, suche ich nach einer Dokumentation darüber, wie sich Zusatzdaten in einem Streaming-Kontext verhalten. Es scheint seltsam, dass ich nichts Offizielles darauf finden kann.


Ich füge hier meine experimentellen Ergebnisse hinzu, die ich aus diesem Testprogramm erhalten habe:

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

Linux 3.2.59, 3.17.6

Es scheint, dass Linux Teile von untergeordneten Nachrichten an das Ende anderer Nachrichten anfügt, solange keine zusätzliche Nutzlast während dieses Aufrufs an recvmsg übermittelt werden muss. Sobald die Zusatzdaten einer Nachricht übermittelt werden, wird ein kurzer Lesevorgang zurückgegeben, anstatt die nächste Zusatzdatennachricht zu starten. Im obigen Beispiel erhalte ich folgende Lesungen:

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 bietet mehr Alignment als Linux und gibt einen kurzen Lesevorgang unmittelbar vor dem Start einer Nachricht mit Zusatzdaten. Es wird jedoch gerne eine nicht unterstützende Nachricht an das Ende einer unterstützenden Nachricht angehängt. Für BSD sieht es also so aus, als ob Sie ein nahezu paketartiges Verhalten erhalten, wenn Ihr Puffer größer als die unterstützende Nachricht ist. Die Lesungen, die ich erhalte, sind:

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]

MACHEN:

Würde noch gerne wissen , wie es auf älteren Linux geschieht, iOS, Solaris, etc., und wie es könnte zu erwarten in der Zukunft geschehen.

M Conrad
quelle
Verwechseln Sie keine Streams und Pakete. In einem Stream kann nicht garantiert werden, dass die Daten in denselben Blöcken übermittelt werden, in denen sie gesendet wurden. Dazu benötigen Sie ein paketbasiertes Protokoll, kein streambasiertes.
ctrl-alt-delor
Genau deshalb stelle ich diese Frage
M Conrad
Die Reihenfolge sollte erhalten bleiben. Das ist es, was Streams tun. Wenn ein blockierender Lesevorgang 0 zurückgibt, ist dies das Ende des Streams. Wenn eine andere Zahl zurückgegeben wird, muss mindestens eine weitere gelesen werden, um dies herauszufinden. Es gibt keine Nachricht1, Nachricht2 usw. Es wird kein Nachrichtenbegrenzer übertragen. Sie müssen dies zu Ihrem Protokoll hinzufügen, wenn Sie es benötigen.
Strg-Alt-Delor
1
Insbesondere habe ich ein Text-Stream-Protokoll und füge einen Befehl hinzu, der einen Dateideskriptor mit einer Textzeile übergibt. Ich muss wissen, in welcher Reihenfolge diese Zusatzdaten in Bezug auf den Nachrichtentext eingehen, um den Code richtig schreiben zu können.
M Conrad
1
@MConrad: Ich würde versuchen, eine Kopie der POSIX.1g-Spezifikation zu erhalten. Wenn es dort nicht explizit geschrieben ist, können Sie ein implementierungsspezifisches Verhalten erwarten.
Laszlo Valko

Antworten:

1

Zusätzliche Daten werden so empfangen, als ob sie zusammen mit dem ersten normalen Datenoktett im Segment (falls vorhanden) in die Warteschlange gestellt worden wären.

- POSIX.1-2017

Für den Rest Ihrer Frage werden die Dinge etwas haarig.

... In diesem Abschnitt wird ein Datagramm als Datensegment betrachtet, das einen Datensatz abschließt und eine Quelladresse als speziellen Typ von Zusatzdaten enthält.

Datensegmente werden in die Warteschlange gestellt, wenn Daten vom Protokoll an den Socket übermittelt werden. Normale Datensegmente werden bei der Zustellung am Ende der Warteschlange platziert. Wenn ein neues Segment denselben Datentyp wie das vorhergehende Segment enthält und keine zusätzlichen Daten enthält und wenn das vorhergehende Segment einen Datensatz nicht beendet, werden die Segmente logisch zu einem einzelnen Segment zusammengeführt ...

Eine Empfangsoperation darf niemals Daten oder Zusatzdaten von mehr als einem Segment zurückgeben.

Moderne BSD-Sockel passen also genau zu diesem Extrakt. Das überrascht nicht :-).

Denken Sie daran, dass der POSIX-Standard nach UNIX und nach Teilungen wie BSD im Vergleich zu System V geschrieben wurde. Eines der Hauptziele war es, das vorhandene Verhaltensspektrum besser zu verstehen und noch mehr Teilungen in vorhandenen Features zu verhindern.

Linux wurde ohne BSD-Code implementiert. Hier scheint es sich anders zu verhalten.

  1. Wenn ich Sie richtig gelesen, es klingt wie Linux zusätzlich verschmilzt „Segmente“ , wenn ein neues Segment tut Zusatzdaten enthalten, aber das vorherige Segment nicht.

  2. Ihre Feststellung, dass "Linux Teile von untergeordneten Nachrichten an das Ende anderer Nachrichten anfügt, solange bei diesem Aufruf von recvmsg keine zusätzlichen Nutzdaten übermittelt werden müssen", wird durch den Standard nicht vollständig erklärt. Eine mögliche Erklärung wäre eine Rennbedingung. Wenn Sie einen Teil eines "Segments" lesen, erhalten Sie die Zusatzdaten. Vielleicht hat Linux dies so interpretiert, dass der Rest des Segments nicht mehr als Zusatzdaten zählt! Wenn ein neues Segment empfangen wird, wird es entweder gemäß dem Standard oder gemäß dem obigen Unterschied 1 zusammengeführt.

Wenn Sie ein maximal portierbares Programm schreiben möchten, sollten Sie diesen Bereich komplett meiden. Bei der Verwendung von Zusatzdaten werden viel häufiger Datagramm- Sockets verwendet. Wenn Sie auf all den seltsamen Plattformen arbeiten möchten, die technisch vor allem POSIX anbieten möchten, scheint sich Ihre Frage in eine dunkle und ungetestete Ecke zu wagen.


Man könnte argumentieren, dass Linux immer noch mehreren wichtigen Prinzipien folgt:

  1. Msgstr "Zusatzdaten werden so empfangen, als wären sie zusammen mit dem ersten normalen Datenoktett im Segment in der Warteschlange."
  2. Nebendaten werden, wie Sie sagen, niemals "verkettet".

Ich bin jedoch nicht davon überzeugt, dass das Linux-Verhalten besonders nützlich ist , wenn man es mit dem BSD-Verhalten vergleicht. Es scheint, als müsste das von Ihnen beschriebene Programm eine Linux-spezifische Problemumgehung hinzufügen. Und ich kenne keine Rechtfertigung dafür, warum Linux das von Ihnen erwarten würde.

Es mag beim Schreiben des Linux-Kernel-Codes sinnvoll ausgesehen haben, ohne jemals von einem Programm getestet oder getestet worden zu sein.

Oder es könnte von einem Programmcode ausgeführt werden, der meistens unter dieser Teilmenge funktioniert, aber im Prinzip "Bugs" oder Race-Bedingungen im Edge-Case-Modus aufweisen kann.

Wenn Sie das Linux-Verhalten und seine beabsichtigte Verwendung nicht verstehen können, ist dies meines Erachtens ein Argument dafür, dies unter Linux als "dunkle, ungetestete Ecke" zu behandeln.

sourcejedi
quelle
Vielen Dank für die ausführliche Bewertung! Ich denke, dass das Mitnehmen hier ist, dass ich dies mit zwei Puffern (jeder mit Datenteil und Nebenteil) sicher handhaben kann; Wenn ich beim ersten Lesevorgang Dateideskriptoren erhalte, die nicht zur Nachricht gehören, aber eine andere Nachricht beginnt, bedeutet dies, dass ich beim nächsten Lesevorgang definitiv das Ende meiner Datennachricht mit der ersten zusätzlichen Nutzlast finde in diesem zweiten lesen. Abwechselnd sollte es mir immer möglich sein, Nachrichten anhand der Position des ersten Bytes mit Nutzdaten abzugleichen.
M Conrad