web-dev-qa-db-fra.com

E / S non bloquantes UNIX: O_NONBLOCK vs. FIONBIO

Dans tous les exemples et discussions que je rencontre dans le contexte de la programmation de socket BSD, il semble que la méthode recommandée pour définir un descripteur de fichier en mode d'E/S non bloquant consiste à utiliser le drapeau O_NONBLOCK sur fcntl(), par exemple.

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

Je fais de la programmation réseau sous UNIX depuis plus de dix ans et j'ai toujours utilisé l'appel FIONBIO ioctl() pour le faire:

int opt = 1;
ioctl(fd, FIONBIO, &opt);

Je n'ai jamais vraiment réfléchi à pourquoi. Je viens de l'apprendre de cette façon.

Quelqu'un a-t-il des commentaires sur les mérites respectifs possibles de l'un ou de l'autre? J'imagine que le locus de portabilité diffère quelque peu, mais je ne sais pas dans quelle mesure car ioctl_list(2) ne parle pas de cet aspect des méthodes individuelles ioctl.

88
Alex Balashov

Avant la normalisation, il y avait ioctl(...FIONBIO...) et fcntl(...O_NDELAY...), mais ceux-ci se sont comportés de manière incohérente entre les systèmes, et même au sein du même système. Par exemple, il était courant que FIONBIO travaille sur des sockets et O_NDELAY travailler sur les ttys, avec beaucoup d’incohérences pour des choses comme les pipes, les fifos et les périphériques. Et si vous ne saviez pas quel type de descripteur de fichier vous aviez, il vous faudrait définir les deux pour en être sûr. Mais en outre, une lecture non bloquante sans données disponibles a également été indiquée de manière incohérente; Selon le système d'exploitation et le type de descripteur de fichier, la lecture peut renvoyer 0, ou -1 avec le code d'erreur EAGAIN ou -1 avec le code d'erreur EWOULDBLOCK. Même aujourd'hui, définir FIONBIO ou O_NDELAY sur Solaris, une lecture sans données renvoie 0 sur un tty ou un tuyau, ou -1 avec errno EAGAIN sur un socket. Cependant 0 est ambigu, car il est également renvoyé pour EOF.

POSIX a résolu ce problème en introduisant O_NONBLOCK, qui a un comportement normalisé sur différents systèmes et types de descripteurs de fichiers. Parce que les systèmes existants souhaitent généralement éviter tout changement de comportement susceptible de rompre la compatibilité avec les versions antérieures, POSIX a défini un nouvel indicateur plutôt que d'imposer un comportement spécifique à l'un des autres. Certains systèmes comme Linux traitent tous les trois de la même manière et définissent également EAGAIN et EWOULDBLOCK sur la même valeur, mais les systèmes souhaitant conserver un autre comportement hérité pour la compatibilité ascendante peuvent le faire lorsque les mécanismes plus anciens sont utilisés.

Les nouveaux programmes devraient utiliser fcntl(...O_NONBLOCK...), tel que normalisé par POSIX.

127
mark4o

Je crois que fcntl() est une fonction POSIX. Where as ioctl() est une fonction UNIX standard. Voici une liste de POSIX io . ioctl() est très spécifique au noyau/pilote/système d'exploitation, mais je suis sûr que ce que vous utilisez fonctionne sur la plupart des versions d'Unix. d'autres ioctl() pourraient ne fonctionner que sur certains systèmes d'exploitation ou même sur certains révolutions de leur noyau.

6
EdH

Comme @Sean l'a dit, fcntl() est largement normalisé, et donc disponible sur toutes les plateformes. La fonction ioctl() est antérieure à fcntl() sous Unix, mais elle n'est pas du tout normalisée. Que la ioctl() a fonctionné pour vous sur toutes les plates-formes qui vous intéressent est une chance, mais elle n'est pas garantie. En particulier, les noms utilisés pour le second argument sont mystérieux et peu fiables sur toutes les plateformes. En effet, ils sont souvent uniques au pilote de périphérique particulier référencé par le descripteur de fichier. (Les appels ioctl() utilisés pour un périphérique graphique à mappage binaire s'exécutant il y a vingt ans sur un ICL Perq sous PNX (Perq Unix) ne sont jamais traduits ni sur un autre site, par exemple.)

5
Jonathan Leffler