Cosa succede con i dati ausiliari del flusso unix su letture parziali?


18

Quindi ho letto molte informazioni su dati ausiliari unix-stream, ma una cosa che manca in tutta la documentazione è cosa dovrebbe succedere quando c'è una lettura parziale?

Supponiamo che sto ricevendo i seguenti messaggi in un buffer di 24 byte

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 prima chiamata a recvmsg, ottengo tutto msg1 (e parte di msg2? Il sistema operativo lo farà mai?) Se ricevo parte di msg2, ottengo subito i dati accessori e devo salvarli per la lettura successiva quando so cosa mi stava effettivamente dicendo il messaggio con i dati? Se libererò i 20 byte da msg1 e poi richiamerò di nuovo recvmsg, consegnerà mai msg3 e msg4 contemporaneamente? I dati ausiliari di msg3 e msg4 vengono concatenati nella struttura del messaggio di controllo?

Mentre potrei scrivere programmi di test per scoprirlo sperimentalmente, sto cercando documentazione su come si comportano i dati accessori in un contesto di streaming. Sembra strano che non riesca a trovare nulla di ufficiale su di esso.


Aggiungerò qui i miei risultati sperimentali, che ho ottenuto da questo programma di test:

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

Linux 3.2.59, 3.17.6

Sembra che Linux aggiungerà parti di messaggi portanti accessori alla fine di altri messaggi purché non sia necessario consegnare alcun payload ausiliario precedente durante questa chiamata a recvmsg. Una volta consegnati i dati ausiliari di un messaggio, verrà restituita una lettura breve anziché avviare il successivo messaggio di dati ausiliari. Quindi, nell'esempio sopra, le letture che ottengo sono:

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 fornisce un maggiore allineamento rispetto a Linux e fornisce una breve lettura immediatamente prima dell'inizio di un messaggio con dati ausiliari. Tuttavia, aggiungerà felicemente un messaggio non secondario alla fine di un messaggio secondario. Quindi, per BSD, sembra che se il buffer è più grande del messaggio secondario, si ottiene un comportamento simile a un pacchetto. Le letture che ottengo sono:

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]

FARE:

Mi piacerebbe ancora sapere come accadrà su Linux, iOS, Solaris, ecc. Precedenti e come ci si potrebbe aspettare che accada in futuro.


Non confondere flussi e pacchetti, in un flusso non esiste alcuna garanzia che i dati vengano consegnati negli stessi blocchi che sono stati inviati, per questo sarebbe necessario un protocollo basato su pacchetti, non basato su flusso.
ctrl-alt-delor,

questo è esattamente il motivo per cui sto ponendo questa domanda
M Conrad,

L'ordine dovrebbe essere preservato. Questo è ciò che fanno i flussi. Se una lettura di blocco restituisce 0, allora è la fine del flusso. Se restituisce un altro numero, potrebbe essercene di più, devi fare almeno un'altra lettura per scoprirlo. Non esistono messaggi come 1, 2 e così via. Nessun delimitatore di messaggi viene trasmesso. Devi aggiungerlo al tuo protocollo, se ne hai bisogno.
ctrl-alt-delor,

1
In particolare, ho un protocollo di flusso di testo e sto aggiungendo un comando che passa un descrittore di file con una riga di testo. Devo sapere quale ordine ricevono questi dati accessori in relazione al testo del messaggio per poter scrivere correttamente il codice.
M Conrad,

1
@MConrad: proverei a ottenere una copia della specifica POSIX.1g. Se non è scritto esplicitamente lì, allora potresti aspettarti un comportamento specifico dell'implementazione.
Laszlo Valko,

Risposte:


1

I dati accessori vengono ricevuti come se fossero in coda insieme al primo ottetto di dati normale nel segmento (se presente).

- POSIX.1-2017

Per il resto della tua domanda, le cose diventano un po 'pelose.

... Ai fini di questa sezione, un datagramma è considerato un segmento di dati che termina un record e che include un indirizzo di origine come un tipo speciale di dati ausiliari.

I segmenti di dati vengono inseriti nella coda quando i dati vengono inviati al socket dal protocollo. I segmenti di dati normali vengono posizionati alla fine della coda quando vengono consegnati. Se un nuovo segmento contiene lo stesso tipo di dati del segmento precedente e non include dati ausiliari e se il segmento precedente non termina un record, i segmenti vengono logicamente uniti in un singolo segmento ...

Un'operazione di ricezione non deve mai restituire dati o dati accessori da più di un segmento.

Quindi le prese moderne BSD corrispondono esattamente a questo estratto. Questo non è sorprendente :-).

Ricorda che lo standard POSIX è stato scritto dopo UNIX e dopo suddivisioni come BSD vs System V. Uno degli obiettivi principali era quello di aiutare a comprendere la gamma esistente di comportamento e prevenire ulteriori divisioni nelle funzionalità esistenti.

Linux è stato implementato senza riferimento al codice BSD. Sembra comportarsi diversamente qui.

  1. Se ho letto bene, suona come Linux è inoltre la fusione "segmenti", quando un nuovo segmento non includere dati ausiliari, ma il segmento precedente non lo fa.

  2. Il tuo punto che "Linux aggiungerà porzioni di messaggi ausiliari alla fine di altri messaggi purché non sia necessario consegnare un carico utile ausiliario precedente durante questa chiamata a recvmsg", non sembra del tutto spiegato dallo standard. Una possibile spiegazione implicherebbe una condizione di razza. Se leggi parte di un "segmento", riceverai i dati accessori. Forse Linux lo ha interpretato nel senso che il resto del segmento non conta più come includere i dati accessori! Pertanto, quando viene ricevuto un nuovo segmento, viene unito - secondo lo standard o secondo la differenza 1 sopra.

Se vuoi scrivere un programma al massimo portatile, dovresti evitare del tutto questa area. Quando si utilizzano dati ausiliari, è molto più comune utilizzare i socket di datagramma . Se vuoi lavorare su tutte le strane piattaforme che tecnicamente aspirano a fornire qualcosa per lo più come POSIX, la tua domanda sembra avventurarsi in un angolo buio e non testato.


Si potrebbe sostenere che Linux segue ancora diversi principi significativi:

  1. "I dati accessori vengono ricevuti come se fossero in coda insieme al primo ottetto di dati normale nel segmento".
  2. I dati ausiliari non vengono mai "concatenati", come si dice.

Tuttavia, non sono convinto che il comportamento di Linux sia particolarmente utile quando lo confronti con il comportamento di BSD. Sembra che il programma che descrivi dovrebbe aggiungere una soluzione alternativa specifica per Linux. E non conosco una giustificazione per cui Linux si aspetterebbe che tu lo facessi.

Potrebbe sembrare ragionevole quando si scrive il codice del kernel Linux, ma senza essere mai stato testato o esercitato da alcun programma.

Oppure potrebbe essere esercitato da un codice di programma che funziona principalmente in questo sottoinsieme, ma in linea di principio potrebbe avere "bug" o condizioni di gara limite.

Se non riesci a dare un senso al comportamento di Linux e al suo uso previsto, penso che sostenga che questo sia un "angolo oscuro e non testato" su Linux.


Grazie per l'approfondita recensione! Penso che il takeaway qui sia che posso gestirlo in modo sicuro con due buffer (ciascuno con una porzione di dati e una porzione accessoria); Se ricevo descrittori di file alla prima lettura e non appartengono al messaggio, ma inizia un altro messaggio, quindi se la lettura successiva contiene anche dati ausiliari significa che sicuramente troverò la fine del mio messaggio di dati che possiede il primo payload ausiliario in quella seconda lettura. Alternando avanti e indietro, dovrei sempre essere in grado di abbinare il messaggio al payload in base alla posizione del primo byte.
M Conrad,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.