2013-05-15 1 views
5

Il s'agit davantage d'une observation et d'une suggestion quant à la meilleure façon de gérer ce scénario.sendto() Les dgrams ne bloquent pas pour ENOBUFS sous OSX

J'ai deux threads, l'un pompe les données et l'autre les données et fait beaucoup de travail avant de lui envoyer une autre socket. Les deux threads sont connectés via une socket de domaine. Le protocole utilisé ici est UDP. Je ne voulais pas utiliser TCP car il est basé sur les flux, ce qui signifie que s'il y a peu d'espace dans la file d'attente, mes données sont partagées et envoyées. C'est mauvais car j'envoie des données qui ne devraient pas être divisées. D'où j'ai utilisé DGRAM. Il est intéressant de noter que lorsque le thread d'envoi submerge le thread de recv en pompant tant de données, le buffer du socket de domaine est à un certain point rempli et sendto() renvoie ENOBUFS. J'étais d'avis que si cela arrivait, sendto() bloquerait jusqu'à ce que le tampon soit disponible. Ce serait mon comportement désiré. Cependant, cela ne semble pas être le cas. Je résous ce problème d'une manière plutôt étrange.

  • CPU Méthode du rendement Si je ENOBUFS, je fais un sched_yield(); comme il n'y a pas pthread_yield() dans OSX. Après cela, j'essaie de renvoyer à nouveau. Si cela échoue, je continue à faire la même chose jusqu'à ce qu'elle soit prise. C'est mauvais comme Iam gaspiller des cycles de cpu faisant juste quelque chose inutile. J'aimerais si sendto() bloqué. J'ai essayé de résoudre le même problème en utilisant sleep (1) au lieu de sched_yield() mais cela ne sert à rien car sleep() mettrait mon processus en veille au lieu de simplement envoyer ce thread.

Les deux ne semblent pas fonctionner pour moi et je suis à court d'options. Quelqu'un peut-il suggérer quelle est la meilleure façon de gérer ce problème? Y a-t-il des astuces intelligentes que je ne connais pas qui peuvent réduire les cycles inutiles de cpu? BTW, ce que la page de l'homme dit à propos SentTo() ne va pas, sur la base de cette discussion http://lists.freebsd.org/pipermail/freebsd-hackers/2004-January/005385.html

Le code Upd dans le noyau:

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

Comment générer les threads? En fait, [sleep (3)] (https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/sleep.3.html) devrait fonctionner pour les threads POSIX. – artistoex

Répondre

0

Je ne sais pas pourquoi sendto() ne bloque pas pour vous ... mais vous pouvez essayer d'appeler cette fonction avant chaque appel à 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 la taille sont les paquets que vous essayez d'envoyer?

+0

En fait, Jeremy comme je comprends les systèmes BSD ne bloquent pas sur sendto() où Linux et Solaris le font. Sélectionnez ne fonctionnera pas ici comme dans le cas de UDP il n'y a pas de file d'attente de tampon d'envoi qui est disponible dans TCP donc select ne connaîtra pas de buff. Il autorisera toujours mais échouera avec ENOBUFS. Le code du noyau pour UDP est donné ci-dessous – user2085689

+0

Il semble que pour UDP sur BSD et OSX sendto() ne bloque pas. C'est mal mentionné dans les pages de manuel. Même select() ne peut pas aider.C'est une discussion très intéressante à lire sur le même problème http://lists.freebsd.org/pipermail/freebsd-hackers/2004-January/005369.html – user2085689

+0

Hmm, j'utilise select() pour limiter le taux de send() sur des sockets UDP non-bloquantes sous OSX et cela semble fonctionner pour moi ... bien sûr ça n'empêche pas les paquets d'être abandonnés plus tard (par exemple au switch réseau), mais ça fait que mon programme envoie le Paquets UDP à la vitesse que le tampon UDP sortant peut les accepter. Dans votre cas, vous pouvez simplement utiliser le protocole TCP avec une logique de cadrage (par exemple, envoyer un en-tête "packet-size" suivi des données du "paquet", afin que le récepteur puisse lire l'en-tête et savoir comment Beaucoup d'octets à remplir dans un tampon avant de le traiter) –

0

sendto() sous OS X est vraiment non bloquant (c'est-à-dire que l'attribut M_DONTWAIT est activé).

Je vous suggère d'utiliser une connexion basée sur le flux et de recevoir simplement toutes les données de l'autre côté en utilisant l'indicateur MSG_WAITALL de la fonction recv. Si vos données ont une structure stricte, il suffit de passer la bonne taille au recv. Si ce n'est pas simplement envoyer un paquet de contrôle de taille fixe en premier avec la taille du prochain morceau de données, puis les données elles-mêmes. Du côté du récepteur, vous attendez le paquet de contrôle de taille fixe et les données de taille du paquet de contrôle.

Questions connexes