Блокирование Linux против последовательного чтения без блокировки

У меня есть этот код для чтения из Serial в Linux, но я не знаю, в чем разница между блокированием и неблокированием при чтении последовательного порта и какой лучше в какой ситуации?

1 ответ

Решение

Код, который вы упомянули, плохо написан и прокомментирован. Этот код не соответствует правилам POSIX в отношении переносимости, как описано в разделе " Правильная установка режимов терминала" и в Руководстве по последовательному программированию для операционных систем POSIX. В этом коде не упоминается, что он использует неканонический (он же необработанный) режим и повторно использует термины "блокирование" и "неблокирование" для описания атрибутов VMIN и VTIME.

Традиционное определение "блокирующее" и "неблокирующее" чтение основано на "когда" вызов чтения вернется в вашу программу (и возобновит выполнение со следующим оператором) и на том, будут ли данные, хранящиеся в буфере чтения вашей программы. Чтение с блокировкой является режимом по умолчанию, если только не запрашивается неблокирование путем открытия последовательного порта с параметром O_NONBLOCK или O_NDELAY.

Канонический режим
Для блокирующего канонического вызова чтения последовательного порта, строка (или запись) текста всегда будет возвращаться в предоставленном буфере (если не произошла ошибка). Вызов read будет блокировать (т.е. приостанавливать выполнение вашей программы) столько времени, сколько потребуется для получения и обработки символа завершения строки.

Неблокирующий канонический вызов чтения последовательного порта всегда возвращает "немедленно". Чтение может возвращать или не возвращать какие-либо данные.
Если (после предыдущего вызова чтения) хотя бы строка текста была получена и сохранена в системном буфере, то самая старая строка будет удалена из системного буфера и скопирована в программный буфер. Код возврата будет указывать длину данных.
Если (после предыдущего вызова чтения) символ завершения строки не был получен и обработан, тогда (полная) строка текста недоступна. Read() вернет ошибку EAGAIN (т. Е. Код возврата -1 и errno установлен в EAGAIN). Ваша программа может затем выполнить некоторые вычисления, или запросить ввод-вывод с другого устройства, или задержку / режим ожидания. После произвольной задержки или с помощью уведомления poll() или select() ваша программа может повторить чтение ().

Неканонический режим
Когда последовательный порт сконфигурирован для неканонического режима, элементы массива termios c_cc VMIN и VTIME должны использоваться для управления "блокировкой", но для этого необходимо, чтобы порт был открыт в режиме блокировки по умолчанию, то есть не указывайте O_NONBLOCK open вариант. В противном случае O_NONBLOCK будет иметь приоритет над спецификациями VMIN и VTIME, а read() установит для errno значение EAGAIN и немедленно вернет -1 вместо 0, если данные недоступны. (Такое поведение наблюдается в последних ядрах Linux 3.x; старые версии 2.6.x могут вести себя по-разному.)

Справочная страница termios описывает (индекс массива c_cc) VMIN как "минимальное количество символов для неканонического чтения" и (индекс массива c_cc) VTIME как "тайм-аут в секундах для неканонического чтения".
VMIN должен быть скорректирован вашей программой, чтобы соответствовать ожидаемой типичной длине сообщения или дейтаграммы и / или минимальному размеру данных для извлечения и обработки за чтение ().
VTIME должен быть скорректирован вашей программой, чтобы соответствовать типичной пакетности или скорости поступления последовательных данных, которые ожидаются, и / или максимальному времени ожидания данных или данных.

Значения VMIN и VTIME взаимодействуют, чтобы определить критерий того, когда чтение должно вернуться; их точное значение зависит от того, какой из них ненулевой. Есть четыре возможных случая.
Эта веб-страница объясняет это как:

  • VMIN = 0 и VTIME = 0

    Это абсолютно неблокирующее чтение - вызов выполняется немедленно непосредственно из входной очереди водителя. Если данные доступны, они передаются в буфер вызывающей стороны до nbytes и возвращаются. В противном случае ноль немедленно возвращается, чтобы указать "нет данных". Отметим, что это "опрос" последовательного порта, и это почти всегда плохая идея. Если сделать это многократно, он может потреблять огромное количество процессорного времени и крайне неэффективен. Не используйте этот режим, если вы действительно не знаете, что делаете.

  • VMIN = 0 и VTIME > 0

    Это чистое время чтения. Если данные доступны во входной очереди, они передаются в буфер вызывающей стороны максимум до nbytes и немедленно возвращаются вызывающей стороне. В противном случае драйвер блокируется до тех пор, пока не поступят данные или не истечет десятая часть VTIME с начала вызова. Если таймер истекает без данных, возвращается ноль. Одного байта достаточно, чтобы удовлетворить этот вызов чтения, но если во входной очереди доступно больше, он возвращается вызывающей стороне. Обратите внимание, что это общий таймер, а не межсимвольный.

  • VMIN> 0 и VTIME > 0

    Чтение () выполняется, когда либо символы VMIN были переданы в буфер вызывающего, либо когда истекают десятые доли VTIME между символами. Поскольку этот таймер не запускается до тех пор, пока не прибудет первый символ, этот вызов может блокироваться бесконечно, если последовательная линия не используется. Это наиболее распространенный режим работы, и мы считаем VTIME тайм-аутом между символами, а не общим. Этот вызов никогда не должен возвращать ноль прочитанных байтов.

(По моему опыту, VMIN>0 and VTIME>0 Режим не совсем работает, как рекламируется. Таймер, кажется, очень короткий интервал, намного меньше, чем 1/10 секунды. Я не видел, чтобы это работало на ARM с 2.6 и Linux 3.13 на x86. При высокой скорости передачи данных (115200), с VMIN=1 и VTIME=1, read() иногда возвращает 10 или более байтов. Но чаще всего это только частичное чтение нескольких байтов независимо от значения VTIME. Может быть, эта поломка предпочтительна / желательна? Минимальное 0,1-секундное разделение сообщений просто слишком длинное (и не практичное) при современных скоростях передачи.)

  • VMIN> 0 и VTIME = 0

    Это считанное считывание, которое выполняется только тогда, когда в буфер вызывающего абонента были переданы, по меньшей мере, символы VMIN - компонент синхронизации не задействован. Это чтение может быть выполнено из входной очереди водителя (где вызов может немедленно вернуться) или путем ожидания поступления новых данных: в этом отношении вызов может блокироваться бесконечно. Мы считаем, что это неопределенное поведение, если nbytes меньше, чем VMIN.

Код, который вы упоминаете, настраивает "неблокирующий" режим как VMIN = 0 и VTIME=5. Это не приведет к немедленному возвращению read(), как при неблокирующем каноническом чтении; с этим кодом read() всегда должна ждать как минимум полсекунды перед возвратом. Традиционное определение "неблокирования" состоит в том, что ваша вызывающая программа не прерывается во время системного вызова и сразу же получает контроль (почти). Чтобы получить (безусловный и) немедленный возврат (для неканонического чтения), установите VMIN = 0 и VTIME=0.

Другие вопросы по тегам