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

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

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

4b9b3361

Ответ 1

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

(Автор этого кода сообщает, что он предшествует стандарту POSIX и, следовательно, его несоответствию. Это понятно, но затем опубликовать и отстаивать использование старого кода, который может быть не переносимым (т.е. Функционировать, как ожидается, в альтернативной ситуации). ) сомнительно.)

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

Канонический режим
Для блокирующего канонического вызова чтения последовательного порта строка (или запись) текста всегда будет возвращаться в предоставленном буфере (если не произошла ошибка). Вызов 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

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

  • VMIN = 0 и VTIME> 0

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

  • VMIN> 0 и VTIME> 0

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

(По моему опыту, VMIN>0 and VTIME>0 работают не совсем так, как объявлено. Таймер, похоже, имеет очень короткий интервал, намного меньше VMIN>0 and VTIME>0 секунды. Я не видел, чтобы он работал на 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.