2013-05-15 9 views
5

Questo è più di un'osservazione e anche un suggerimento per che cosa è il modo migliore per gestire questo scenario.sendto() non bloccano i dgram per ENOBUFS su OSX

Ho due thread uno solo pompa i dati e un altro riceve i dati e fa molto lavoro prima di inviarlo un altro socket. Entrambi i thread sono collegati tramite un socket Domain. Il protocollo usato qui è UDP. Non volevo usare TCP perché è basato sul flusso, il che significa che se c'è poco spazio nella coda i miei dati vengono divisi e inviati. Questo è un male perché Iam invia dati che non dovrebbero essere divisi. Quindi ho usato DGRAM. È interessante notare che quando il thread send sovrascrive il thread recv pompando così tanti dati, a un certo punto il buffer del socket Domain viene riempito e sendto() restituisce ENOBUFS. Ero dell'opinione che se ciò dovesse accadere, sendto() bloccherebbe fino a quando il buffer non sarà disponibile. Questo sarebbe il mio comportamento desiderato. Tuttavia questo non sembra essere il caso. Risolvo questo problema in un modo piuttosto strano.

  • metodo del rendimento CPU Se ottengo ENOBUFS, faccio uno sched_yield(); poiché non c'è pthread_yield() in OSX. Dopo di ciò, provo a inviare nuovamente il nuovo messaggio. Se fallisce, continuo a fare lo stesso fino a quando non viene preso. Questo è male perché Iam spreca cicli di CPU solo facendo qualcosa di inutile. Mi piacerebbe se sendto() bloccato.

  • Metodo sleep Ho provato a risolvere lo stesso problema usando sleep (1) invece di sched_yield(), ma questo di non utilizzare sleep() avrebbe messo il mio processo in sleep invece di solo quel thread di invio.

Entrambi non sembra funzionare per me e Iam a corto di opzioni. Qualcuno può suggerire qual è il modo migliore per gestire questo problema? C'è qualche trucco intelligente di cui non sono consapevole che possa ridurre inutili cicli di CPU? btw, ciò che la pagina man dice di SentTo() è sbagliato, sulla base di questa discussione http://lists.freebsd.org/pipermail/freebsd-hackers/2004-January/005385.html

Il codice Upd nel kernel:

The udp_output function in /sys/netinet/udp_usrreq.c, seems clear: 

     /* 
      * Calculate data length and get a mbuf 
      * for UDP and IP headers. 
      */ 
     M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); 
     if (m == 0) { 
       error = ENOBUFS; 
       if (addr) 
         splx(s); 
       goto release; 
     } 
+0

Come si generano i fili? In realtà, [sleep (3)] (https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/sleep.3.html) dovrebbe funzionare per i thread POSIX. – artistoex

risposta

0

io non sono sicuro perché sendto() non stia bloccando per si ... ma si potrebbe provare a chiamare questa funzione prima di ogni chiamata a sendto():

#include <stdio.h> 
#include <sys/select.h> 

// Won't return until there is space available on the socket for writing 
void WaitUntilSocketIsReadyForWrite(int socketFD) 
{ 
    fd_set writeSet; 
    FD_ZERO(&writeSet); 
    FD_SET(socketFD, &writeSet); 
    if (select(socketFD+1, NULL, &writeSet, NULL, NULL) < 0) perror("select"); 
} 

Btw quanto grande sono i pacchetti che si sta tentando di inviare?

+0

In realtà, Jeremy capisco che i sistemi basati su BSD non bloccano sendto() come fanno Linux e Solaris. Selezionare non funzionerà qui come nel caso di UDP non esiste una coda di buffer di invio che è disponibile in TCP, quindi select non saprà nulla su buff. Permetterà sempre ma fallirà con ENOBUFS. Il codice del kernel per UDP è riportato sotto – user2085689

+0

Sembra UDP su BSD e OSX sendto() non bloccherà. È erroneamente menzionato nelle pagine man. Anche select() non può aiutare.Questa è una discussione molto interessante da leggere sullo stesso problema http://lists.freebsd.org/pipermail/freebsd-hackers/2004-January/005369.html – user2085689

+0

Hmm, io uso select() per limitare il tasso di invio() su socket UDP non bloccanti sotto OSX e che sembra funzionare per me ... ovviamente non impedisce che i pacchetti vengano rilasciati in seguito (ad esempio allo switch di rete), ma lo fa in modo che il mio programma invii il Pacchetti UDP alla velocità che il buffer UDP in uscita può accettarli. Per il tuo caso potresti voler semplicemente byte il proiettile e usare TCP con qualche logica di framing (ad esempio, inviare un'intestazione "packet-size" seguita dai dati nel "pacchetto", in modo che il destinatario possa leggere prima l'intestazione e sapere come molti byte da riempire in un buffer prima di elaborarlo) –

0

sendto() su OS X è in realtà non bloccante (ovvero flag M_DONTWAIT per).

Ti suggerisco di utilizzare la connessione basata sul flusso e di ricevere semplicemente tutti i dati sull'altro lato utilizzando il flag MSG_WAITALL della funzione recv. Se i tuoi dati hanno una struttura rigorosa di quanto sarebbe semplice, basta passare la dimensione corretta allo recv. Se non è sufficiente inviare semplicemente un pacchetto di controllo a dimensione fissa prima con le dimensioni del prossimo blocco di dati e quindi i dati stessi. Sul lato ricevitore, si aspetterebbe il pacchetto di controllo di dimensioni fisse e i dati di dimensione dal pacchetto di controllo.

Problemi correlati