web-dev-qa-db-fra.com

Blocage Linux vs lecture série non bloquante

j'ai ce code pour lire à partir de Serial sous Linux, mais je ne sais pas quelle est la différence entre le blocage et le non-blocage dans la lecture du port série et lequel est le meilleur dans quelle situation?

25

Le code que vous mentionnez est IMO mal codé et commenté. Ce code n'est pas conforme aux pratiques POSIX de portabilité décrites dans Réglage correct des modes de terminal et Guide de programmation série pour les systèmes d'exploitation POSIX . Ce code ne mentionne pas qu'il utilise le mode non canonique (aka raw) et réutilise la terminologie "bloquante" et "non bloquante" pour décrire --- VMIN et VTIME attributs.

(L'auteur de ce code rapporte qu'il est antérieur au standard POSIX, et donc à sa non-conformité. C'est compréhensible, mais pour ensuite publier et préconiser l'utilisation d'un ancien code qui peut ne pas être portable (c'est-à-dire fonctionner comme prévu dans une situation alternative) ) est discutable.)

La définition conventionnelle d'une lecture "bloquante" par rapport à "non bloquante" est basée sur "quand" l'appel lu reviendra à votre programme (et reprendra son exécution avec l'instruction suivante) et s'il y aura des données stockées dans le tampon de lecture de votre programme. Une lecture bloquante est le mode par défaut, sauf si un non blocage est demandé en ouvrant le port série avec l'option O_NONBLOCK ou O_NDELAY.

Mode canonique
Pour un blocage lecture canonique appel d'un port série, une ligne (aka record) de texte sera toujours renvoyée dans le tampon fourni (sauf erreur). L'appel lu bloquera (c'est-à-dire suspendra l'exécution de votre programme) aussi longtemps qu'il faudra pour qu'un caractère de terminaison de ligne soit reçu et traité.

Un appel de lecture canonique non bloquant d'un port série retournera toujours "immédiatement". La lecture peut renvoyer ou non des données.
Si (depuis le dernier appel de lecture) au moins une ligne de texte a été reçue et stockée dans le tampon système, alors la ligne la plus ancienne sera supprimée du tampon système et copiée dans le tampon du programme. Le code retour indiquera la longueur des données.
Si (depuis le dernier appel lu) un caractère de terminaison de ligne n'a pas été reçu et traité, aucune ligne de texte (complète) n'est disponible. read () renverra une erreur EAGAIN (c'est-à-dire un code de retour -1 et errno défini sur EAGAIN). Votre programme peut alors effectuer certains calculs, ou demander des E/S à partir d'un autre appareil, ou retarder/dormir. Soit après un délai arbitraire, soit par notification par poll () ou select (), votre programme peut réessayer read ().

Un exemple de programme utilisant le mode canonique de blocage pour les lectures est inclus dans cette réponse .

Mode non canonique
Lorsque le port série est configuré pour le mode non canonique, les éléments de tableau termiosc_cc VMIN et VTIME doivent être utilisés pour contrôler le "blocage", mais cela nécessite que le port soit ouvert dans le mode de blocage par défaut, c'est-à-dire ne spécifiez pas l'option d'ouverture O_NONBLOCK. Sinon, O_NONBLOCK aura la priorité sur les spécifications VMIN et VTIME, et read () définira errno sur EAGAIN et renverra immédiatement -1 à la place de 0 lorsqu'il n'y a pas de données disponibles. (C'est le comportement observé dans les noyaux Linux 3.x récents; les noyaux 2.6.x plus anciens peuvent se comporter différemment.)

La page de manuel termios décrit (c_cc index du tableau) VMIN comme "nombre minimum de caractères pour la lecture non canonique" et (c_cc index du tableau) VTIME comme délai "en décisecondes pour la lecture non canonique ".
VMIN doit être ajusté par votre programme pour s'adapter à la longueur de message ou de datagramme typique attendue et/ou à la taille minimale des données à récupérer. & process per read ().
VTIME doit être ajusté par votre programme pour tenir compte du taux de rafale ou d'arrivée typique des données série qui est attendu et/ou du temps maximum pour attendre des données ou une donnée.

Les valeurs VMIN et VTIME interagissent pour déterminer le critère de retour de la lecture; leurs significations précises dépendent de celles qui sont non nulles. Il y a quatre cas possibles.
Cette page Web l'explique comme:

  • VMIN = 0 et VTIME = 0

    Il s'agit d'une lecture totalement non bloquante - l'appel est satisfait immédiatement directement à partir de la file d'attente d'entrée du pilote. Si des données sont disponibles, elles sont transférées vers la mémoire tampon de l'appelant jusqu'à nbytes et renvoyées. Sinon, zéro est immédiatement renvoyé pour indiquer "aucune donnée". Nous noterons qu'il s'agit d'une "interrogation" du port série, et c'est presque toujours une mauvaise idée. S'il est répété, il peut consommer énormément de temps processeur et est très inefficace. N'utilisez ce mode que si vous savez vraiment, vraiment ce que vous faites.

  • VMIN = 0 et VTIME> 0

    Il s'agit d'une lecture chronométrée pure. Si des données sont disponibles dans la file d'attente d'entrée, elles sont transférées dans la mémoire tampon de l'appelant jusqu'à un maximum de nbytes et retournées immédiatement à l'appelant. Sinon, le pilote bloque jusqu'à ce que les données arrivent, ou lorsque les dixièmes de VTIME expirent depuis le début de l'appel. Si le temporisateur expire sans données, zéro est renvoyé. Un seul octet est suffisant pour satisfaire cet appel en lecture, mais si davantage est disponible dans la file d'attente d'entrée, il est renvoyé à l'appelant. Notez qu'il s'agit d'un temporisateur global, et non inter-caractères.

  • VMIN> 0 et VTIME> 0

    Une lecture () est satisfaite lorsque des caractères VMIN ont été transférés vers le tampon de l'appelant ou lorsque des dixièmes de VTIME expirent entre les caractères. Comme ce temporisateur ne démarre pas avant l'arrivée du premier caractère, cet appel peut se bloquer indéfiniment si la ligne série est inactive. Il s'agit du mode de fonctionnement le plus courant, et nous considérons que VTIME est un délai d'attente inter-caractères, et non global. Cet appel ne doit jamais renvoyer zéro octet lu.

(D'après mon expérience, le mode VMIN>0 and VTIME>0 Ne fonctionne pas tout à fait comme annoncé. Le minuteur semble être un intervalle très court, beaucoup moins d'une 1/10e de seconde. Je ne l'ai pas vu fonctionner sur ARM avec 2.6 et Linux 3.13 sur x86. À un débit rapide (115200), avec VMIN = 1 et VTIME = 1, un read () renvoie parfois 10 octets ou plus. Mais le plus souvent, ce n'est qu'une lecture partielle de quelques octets quelle que soit la valeur VTIME. Peut-être que cette cassure est préférée/souhaitée? Une séparation de message de 0,1 sec minimum est tout simplement trop longue (et pas pratique) à des vitesses de transmission rapides modernes.)

  • VMIN> 0 et VTIME = 0

    Il s'agit d'une lecture comptée qui n'est satisfaite que lorsqu'au moins des caractères VMIN ont été transférés vers la mémoire tampon de l'appelant - aucun composant de synchronisation n'est impliqué. Cette lecture peut être satisfaite à partir de la file d'attente d'entrée du conducteur (où l'appel pourrait revenir immédiatement), ou en attendant l'arrivée de nouvelles données: à cet égard, l'appel pourrait bloquer indéfiniment. Nous pensons que c'est un comportement indéfini si nbytes est inférieur à VMIN.

Ce code que vous mentionnez configure le mode "non bloquant" comme VMIN = 0 et VTIME = 5. Cela ne provoquera pas le retour immédiat de read () comme le ferait une lecture canonique non bloquante; avec ce code, un read () devrait toujours attendre au moins une demi-seconde avant de revenir. La définition conventionnelle d'un "non blocage" est que votre programme appelant n'est pas préempté pendant l'appel système et reprend le contrôle (presque) immédiatement. Pour obtenir un retour immédiat (inconditionnel et) (pour une lecture non canonique), définissez VMIN = 0 et VTIME = 0.

57
sawdust