Entonces, he leído mucha información sobre datos auxiliares de flujo de Unix, pero una cosa que falta en toda la documentación es ¿qué se supone que sucederá cuando hay una lectura parcial?
Supongamos que estoy recibiendo los siguientes mensajes en un búfer de 24 bytes
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)
La primera llamada a recvmsg, obtengo todo msg1 (¿y parte de msg2? ¿El sistema operativo lo hará alguna vez?) Si obtengo parte de msg2, ¿obtengo los datos auxiliares de inmediato y necesito guardarlos para la próxima lectura? cuando sé lo que el mensaje realmente me decía que hiciera con los datos? Si libero los 20 bytes de msg1 y luego llamo a recvmsg nuevamente, ¿entregará alguna vez msg3 y msg4 al mismo tiempo? ¿Los datos auxiliares de msg3 y msg4 se concatenan en la estructura del mensaje de control?
Si bien podría escribir programas de prueba para descubrir esto experimentalmente, estoy buscando documentación sobre cómo se comportan los datos auxiliares en un contexto de transmisión. Parece extraño que no pueda encontrar nada oficial en él.
Voy a agregar mis hallazgos experimentales aquí, que obtuve de este programa de prueba:
https://github.com/nrdvana/daemonproxy/blob/master/src/ancillary_test.c
Linux 3.2.59, 3.17.6
Parece que Linux agregará porciones de mensajes con soporte auxiliar al final de otros mensajes siempre y cuando no sea necesario entregar una carga útil auxiliar anterior durante esta llamada a recvmsg. Una vez que se entregan los datos auxiliares de un mensaje, devolverá una lectura breve en lugar de comenzar el siguiente mensaje de datos auxiliares. Entonces, en el ejemplo anterior, las lecturas que obtengo son:
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 proporciona más alineación que Linux y ofrece una lectura breve inmediatamente antes del inicio de un mensaje con datos auxiliares. Pero, felizmente agregará un mensaje no auxiliar al final de un mensaje auxiliar. Entonces, para BSD, parece que si su búfer es más grande que el mensaje de soporte auxiliar, obtendrá un comportamiento casi similar al de un paquete. Las lecturas que obtengo son:
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]
QUE HACER:
Todavía me gustaría saber cómo sucede en Linux, iOS, Solaris, etc., y cómo se podría esperar que suceda en el futuro.
fuente
Respuestas:
Para el resto de su pregunta, las cosas se ponen un poco complicadas.
Por lo tanto, los enchufes BSD modernos coinciden exactamente con este extracto. Esto no es sorprendente :-).
Recuerde que el estándar POSIX se escribió después de UNIX, y después de divisiones como BSD vs Sistema V. Uno de los objetivos principales era ayudar a comprender el rango de comportamiento existente y evitar aún más divisiones en las características existentes.
Linux se implementó sin referencia al código BSD. Parece comportarse de manera diferente aquí.
Si has leído bien, parece que Linux es, además, fusionando "segmentos" cuando un nuevo segmento hace incluir datos auxiliares, pero el segmento anterior no lo hace.
Su punto de vista de que "Linux agregará porciones de mensajes auxiliares al final de otros mensajes siempre que no sea necesario entregar una carga útil previa durante esta llamada a recvmsg", no parece explicarse completamente por el estándar. Una posible explicación implicaría una condición de carrera. Si lee parte de un "segmento", recibirá los datos auxiliares. ¡Quizás Linux interpretó esto como que significa que el resto del segmento ya no cuenta como la inclusión de datos auxiliares! Entonces, cuando se recibe un nuevo segmento, se fusiona, ya sea según el estándar o según la diferencia 1 anterior.
Si desea escribir un programa máximamente portátil, debe evitar esta área por completo. Cuando se usan datos auxiliares, es mucho más común usar sockets de datagramas . Si desea trabajar en todas las plataformas extrañas que técnicamente aspiran a proporcionar algo como POSIX, su pregunta parece aventurarse en un rincón oscuro y no probado.
Se podría argumentar que Linux aún sigue varios principios importantes:
Sin embargo, no estoy convencido de que el comportamiento de Linux sea particularmente útil cuando lo comparas con el comportamiento de BSD. Parece que el programa que describe necesitaría agregar una solución específica para Linux. Y no sé una justificación de por qué Linux esperaría que hicieras eso.
Podría haber sido sensato al escribir el código del kernel de Linux, pero sin haber sido probado o ejercido por ningún programa.
O podría ser ejercido por algún código de programa que funciona principalmente bajo este subconjunto, pero en principio podría tener "errores" o condiciones de carrera.
Si no puede entender el comportamiento de Linux y su uso previsto, creo que se trata de tratar esto como un "rincón oscuro y no probado" en Linux.
fuente