J'ai donc lu beaucoup d'informations sur les données auxiliaires d'Unix-stream, mais une chose qui manque dans toute la documentation est ce qui est censé se passer quand il y a une lecture partielle?
Supposons que je reçois les messages suivants dans un tampon de 24 octets
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)
Le premier appel à recvmsg, j'obtiens l'intégralité de msg1 (et une partie de msg2? Est-ce que le système d'exploitation le fera?) quand je sais ce que le message me disait réellement de faire avec les données? Si je libère les 20 octets de msg1 et que j'appelle à nouveau recvmsg, est-ce qu'il livrera jamais msg3 et msg4 en même temps? Les données auxiliaires de msg3 et msg4 sont-elles concaténées dans la structure du message de contrôle?
Bien que je puisse écrire des programmes de test pour le découvrir expérimentalement, je cherche de la documentation sur le comportement des données auxiliaires dans un contexte de streaming. Il semble étrange que je ne trouve rien de officiel là-dessus.
Je vais ajouter mes résultats expérimentaux ici, que j'ai obtenus de ce programme de test:
https://github.com/nrdvana/daemonproxy/blob/master/src/ancillary_test.c
Linux 3.2.59, 3.17.6
Il semble que Linux ajoute des parties de messages auxiliaires à la fin des autres messages tant qu'aucune charge utile auxiliaire préalable n'a dû être remise lors de cet appel à recvmsg. Une fois que les données auxiliaires d'un message sont livrées, il retournera une courte lecture plutôt que de démarrer le prochain message de données auxiliaires. Ainsi, dans l'exemple ci-dessus, les lectures que j'obtiens sont:
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 fournit plus d'alignement que Linux et donne une courte lecture immédiatement avant le début d'un message avec des données auxiliaires. Mais, il ajoutera volontiers un message non auxiliaire à la fin d'un message auxiliaire. Donc, pour BSD, il semble que si votre tampon est plus grand que le message auxiliaire, vous obtenez un comportement presque semblable à un paquet. Les lectures que je reçois sont:
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]
FAIRE:
J'aimerais toujours savoir comment cela se produit sur les anciens Linux, iOS, Solaris, etc., et comment cela pourrait se produire à l'avenir.
la source
Réponses:
Pour le reste de votre question, les choses deviennent un peu velues.
Les sockets BSD modernes correspondent donc exactement à cet extrait. Ce n'est pas surprenant :-).
Rappelez-vous que la norme POSIX a été écrite après UNIX et après des divisions comme BSD vs System V. L'un des principaux objectifs était d'aider à comprendre la gamme de comportements existante et d'éviter encore plus de divisions dans les fonctionnalités existantes.
Linux a été implémenté sans référence au code BSD. Il semble se comporter différemment ici.
Si je vous comprends bien, il semble que Linux est la fusion de plus « segments » lorsqu'un nouveau segment ne comprend des données auxiliaires, mais le segment précédent ne fonctionne pas.
Votre argument selon lequel «Linux ajoutera des parties de messages auxiliaires à la fin des autres messages tant qu'aucune charge utile auxiliaire préalable n'a dû être livrée lors de cet appel à recvmsg», ne semble pas entièrement expliqué par la norme. Une explication possible impliquerait une condition de concurrence. Si vous lisez une partie d'un "segment", vous recevrez les données auxiliaires. Peut-être que Linux a interprété cela comme signifiant que le reste du segment ne comptait plus comme incluant des données auxiliaires! Ainsi, lorsqu'un nouveau segment est reçu, il est fusionné - soit selon la norme, soit selon la différence 1 ci-dessus.
Si vous voulez écrire un programme portable au maximum, vous devez éviter complètement cette zone. Lors de l'utilisation de données auxiliaires, il est beaucoup plus courant d'utiliser des sockets datagrammes . Si vous voulez travailler sur toutes les plates-formes étranges qui aspirent techniquement à fournir quelque chose comme POSIX, votre question semble s'aventurer dans un coin sombre et non testé.
On pourrait dire que Linux suit toujours plusieurs principes importants:
Cependant, je ne suis pas convaincu que le comportement de Linux soit particulièrement utile lorsque vous le comparez au comportement BSD. Il semble que le programme que vous décrivez aurait besoin d'ajouter une solution de contournement spécifique à Linux. Et je ne connais pas la raison pour laquelle Linux s'attendrait à ce que vous fassiez cela.
Cela aurait pu paraître raisonnable lors de l'écriture du code du noyau Linux, mais sans jamais avoir été testé ou exercé par aucun programme.
Ou il pourrait être exercé par un code de programme qui fonctionne principalement sous ce sous-ensemble, mais pourrait en principe avoir des "bogues" ou des conditions de concurrence.
Si vous ne pouvez pas comprendre le comportement de Linux et son utilisation prévue, je pense que cela plaide pour le traiter comme un "coin sombre et non testé" sur Linux.
la source