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.
quelle
Antworten:
Für den Rest Ihrer Frage werden die Dinge etwas haarig.
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.
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.
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:
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.
quelle