Bloqueo de Linux frente a lectura en serie sin bloqueo

Resuelto Mohsen Zahraee asked hace 10 años • 1 respuestas

Tengo este código para leer desde Serial en Linux, pero no sé cuál es la diferencia entre bloquear y no bloquear al leer el puerto serie y cuál es mejor en cada situación.

Mohsen Zahraee avatar Sep 23 '14 20:09 Mohsen Zahraee
Aceptado

El código que mencionas está, en mi opinión, mal codificado y comentado. Ese código no se ajusta a las prácticas de portabilidad de POSIX como se describe en Configuración adecuada de los modos de terminal y Guía de programación en serie para sistemas operativos POSIX . Ese código no menciona que utiliza el modo no canónico (también conocido como raw) y reutiliza la terminología de "bloqueo" y "no bloqueo" para describir los atributos VMIN y VTIME .

(El autor de ese código informa que es anterior al estándar POSIX y, por lo tanto, su incumplimiento. Eso es comprensible, pero luego publicar y recomendar el uso de código antiguo que puede no ser portátil (es decir, funcionar como se espera en una situación alternativa) ) es cuestionable.)


La definición convencional de lectura "bloqueante" versus "no bloqueante" se basa en "cuándo" la llamada de lectura regresará a su programa (y reanudará la ejecución con la siguiente declaración) y si habrá datos almacenados en el búfer de lectura de su programa. Una lectura de bloqueo es el modo predeterminado, a menos que se solicite no bloqueo abriendo el terminal serie con el indicador O_NONBLOCK u O_NDELAY.

Modo canónico
Para una llamada de lectura canónica de bloqueo de un terminal serie, siempre se devolverá una línea (también conocida como registro) de texto en el búfer proporcionado (a menos que se produzca un error). La llamada de lectura se bloqueará (es decir, suspenderá la ejecución de su programa) durante el tiempo que sea necesario para recibir y procesar un carácter de terminación de línea.

Una llamada de lectura canónica sin bloqueo de un terminal serie siempre devolverá "inmediatamente". La lectura puede devolver o no algún dato.
Si (desde la llamada de lectura anterior) se recibió y almacenó al menos una línea de texto en el búfer del sistema, entonces la línea más antigua se eliminará del búfer del sistema y se copiará en el búfer del programa. El código de retorno indicará la longitud de los datos.
Si (desde la llamada de lectura anterior) no se ha recibido ni procesado un carácter de terminación de línea, entonces no hay ninguna línea de texto (completa) disponible. read () devolverá un error EAGAIN (es decir, un código de retorno -1 y errno establecido en EAGAIN). Luego, su programa puede realizar algún cálculo, solicitar E/S desde otro dispositivo o retrasar/dormir. Ya sea después de un retraso arbitrario o mediante notificación mediante poll() o select() , su programa puede volver a intentar read() .

En esta respuesta se incluye un programa de ejemplo que utiliza el modo canónico de bloqueo para lecturas .

Modo no canónico
Cuando el terminal serial está configurado para el modo no canónico, los elementos de matriz termios c_cc VMIN y VTIME deben usarse para controlar el "bloqueo", pero esto requiere que el terminal se abra en el modo de bloqueo predeterminado, es decir, no especifique el indicador abierto O_NONBLOCK.
De lo contrario, O_NONBLOCK tendrá prioridad sobre las especificaciones VMIN y VTIME, y read() establecerá errno en EAGAIN y devolverá inmediatamente -1 en lugar de 0 cuando no haya datos disponibles. (Este es el comportamiento observado en los kernels Linux 3.x recientes; los kernels 2.6.x más antiguos pueden comportarse de manera diferente).

La página de manual de termios describe ( índice de matriz c_cc ) VMIN como el "número mínimo de caracteres para lectura no canónica" y ( índice de matriz c_cc ) VTIME como el "tiempo de espera en decisegundos para lectura no canónica" . Su programa debe ajustar
VMIN para adaptarse a la longitud típica de mensaje o datagrama que se espera y/o el tamaño mínimo para que los datos se recuperen y procesen por lectura() . Su programa debe ajustar
VTIME para adaptarse a la ráfaga típica o tasa de llegada de datos en serie que se espera y/o el tiempo máximo de espera por datos o un dato.

Los valores VMIN y VTIME interactúan para determinar el criterio sobre cuándo debe regresar la lectura; sus significados precisos dependen de cuáles de ellos son distintos de cero. Hay cuatro casos posibles.
Esta página web lo explica como:

  • VMIN = 0 y VTIME = 0

Esta es una lectura completamente sin bloqueo: la llamada se satisface inmediatamente directamente desde la cola de entrada del controlador. Si hay datos disponibles, se transfieren al búfer de la persona que llama hasta nbytes y se devuelven. De lo contrario, se devuelve cero inmediatamente para indicar "sin datos". Notaremos que esto es un "sondeo" del puerto serie y casi siempre es una mala idea. Si se hace repetidamente, puede consumir enormes cantidades de tiempo del procesador y es muy ineficiente. No utilices este modo a menos que realmente sepas lo que estás haciendo.

  • VMIN = 0 y VTIME > 0

Esta es una lectura puramente cronometrada. Si hay datos disponibles en la cola de entrada, se transfieren al búfer de la persona que llama hasta un máximo de nbytes y se devuelven inmediatamente a la persona que llama. En caso contrario el conductor se bloquea hasta que lleguen los datos, o cuando expiren los décimos VTIME desde el inicio de la llamada. Si el temporizador expira sin datos, se devuelve cero. Un solo byte es suficiente para satisfacer esta llamada de lectura, pero si hay más disponibles en la cola de entrada, se devuelve a la persona que llama. Tenga en cuenta que este es un cronómetro general, no uno entre caracteres.

  • VMIN > 0 y VTIME > 0

Un read() se cumple cuando los caracteres VMIN se han transferido al búfer de la persona que llama o cuando los décimos VTIME expiran entre caracteres. Dado que este temporizador no se inicia hasta que llega el primer carácter, esta llamada puede bloquearse indefinidamente si la línea serie está inactiva. Este es el modo de operación más común y consideramos que VTIME es un tiempo de espera entre caracteres, no general. Esta llamada nunca debería devolver cero bytes leídos.

  • VMIN > 0 y VTIME = 0

Esta es una lectura contada que se cumple solo cuando se han transferido al menos caracteres VMIN al búfer de la persona que llama; no hay ningún componente de temporización involucrado. Esta lectura puede realizarse desde la cola de entrada del conductor (donde la llamada podría regresar inmediatamente), o esperando que lleguen nuevos datos: en este sentido la llamada podría bloquearse indefinidamente. Creemos que es un comportamiento indefinido si nbytes es menor que VMIN.

Tenga en cuenta que cuando VMIN=1 la especificación VTIME será irrelevante. La disponibilidad de cualquier dato siempre satisfará el criterio mínimo de un solo byte, por lo que el criterio de tiempo puede ignorarse (ya que sería una especificación de tiempo entre caracteres con un VMIN distinto de cero). Este caso especial fue señalado por @IanAbbot.


Ese código que mencionas configura el modo "sin bloqueo" como VMIN=0 y VTIME=5. Esto no hará que read() regrese inmediatamente como lo haría una lectura canónica sin bloqueo; con ese código un read() siempre debe esperar al menos medio segundo antes de regresar.
La definición convencional de "sin bloqueo" es que su programa de llamada no tiene prioridad durante la llamada al sistema y recupera el control (casi) inmediatamente.
Para obtener un retorno (incondicional e) inmediato (para una lectura no canónica), configure VMIN=0 y VTIME=0 (con las advertencias correspondientes).

sawdust avatar Sep 24 '2014 00:09 sawdust