Последовательная связь с низкой задержкой в ​​Linux

Я реализую протокол через последовательные порты в Linux. Протокол основан на схеме ответа на запрос, поэтому пропускная способность ограничена временем, которое требуется для отправки пакета на устройство и получения ответа. Устройства в основном основаны на arm и работают под управлением Linux> = 3.0. У меня проблемы с уменьшением времени приема-передачи ниже 10 мс (115200 бод, 8 бит данных, без контроля четности, 7 байт на сообщение).

Какие интерфейсы ввода-вывода дадут мне наименьшую задержку: выбор, опрос, epoll или опрос вручную с помощью ioctl? Влияет ли блокирующий или неблокирующий ввод-вывод на задержку?

Я попытался установить флаг low_latency с помощью setserial. Но казалось, что это не подействовало.

Могу ли я попытаться уменьшить задержку чем-нибудь еще? Поскольку я контролирую все устройства, можно было бы даже исправить ядро, но этого не следует делать.

---- Редактировать ----

Используемый последовательный контроллер - 16550A.


person JustMaximumPower    schedule 29.10.2012    source источник
comment
Какой тип последовательного интерфейса вы используете? USB / последовательные интерфейсы могут работать медленно.   -  person    schedule 29.10.2012
comment
вам нужно проверить, на что потрачены эти 10 мс, потому что, если они будут потеряны другим устройством, вы не сможете оптимизировать больше.   -  person Ottavio Campana    schedule 29.10.2012
comment
Каков размер сообщений с запросами и ответами? Если оба имеют размер более 100 байт, то вы не можете получить время приема-передачи ‹10 мс (с 115200).   -  person SKi    schedule 29.10.2012
comment
115200 работает очень медленно, поэтому вам гарантирована большая задержка при простом перемещении байтов. Лучше всего будет увеличить скорость до 921600 бод. Или, что еще лучше, переключитесь на гигабитный Ethernet.   -  person TJD    schedule 29.10.2012
comment
@OttavioCampana в данный момент тратит время на ожидание ввода. Я опрашиваю, пока ioctl не сообщит мне, что ввод доступен, и затем я его прочту.   -  person JustMaximumPower    schedule 30.10.2012
comment
@duskwuff используется последовательный порт платы. Так что USB нет.   -  person JustMaximumPower    schedule 30.10.2012
comment
@JustMaximumPower, если время потрачено, оно потеряно, значит, это устройство работает медленно.   -  person Ottavio Campana    schedule 30.10.2012


Ответы (7)


Схемы запроса / ответа обычно неэффективны и быстро обнаруживаются на последовательном порту. Если вас интересует пропускная способность, обратите внимание на оконный протокол, например протокол отправки файлов kermit.

Теперь, если вы хотите придерживаться своего протокола и уменьшить задержку, выберите, опрос, чтение - все это даст вам примерно одинаковую задержку, потому что, как указал Энди Росс, настоящая задержка связана с аппаратной обработкой FIFO.

Если вам повезет, вы можете настроить поведение драйвера без установки патчей, но вам все равно нужно посмотреть код драйвера. Однако, если ARM обрабатывает частоту прерываний 10 кГц, это, безусловно, не пойдет на пользу общей производительности системы ...

Другой вариант - заполнить свой пакет так, чтобы вы каждый раз достигали порогового значения FIFO. Он также подтвердит, является ли это проблемой порогового значения FIFO.

10 мс @ 115200 достаточно для передачи 100 байтов (при условии 8N1), поэтому вы видите, вероятно, потому, что флаг low_latency не установлен. Пытаться

setserial /dev/<tty_name> low_latency

Он установит флаг low_latency, который используется ядром при перемещении данных на уровень tty:

void tty_flip_buffer_push(struct tty_struct *tty)
{
         unsigned long flags;
         spin_lock_irqsave(&tty->buf.lock, flags);
         if (tty->buf.tail != NULL)
                 tty->buf.tail->commit = tty->buf.tail->used;
         spin_unlock_irqrestore(&tty->buf.lock, flags);

         if (tty->low_latency)
                 flush_to_ldisc(&tty->buf.work);
         else
                 schedule_work(&tty->buf.work);
}

Вызов schedule_work может отвечать за наблюдаемую вами задержку в 10 мс.

person shodanex    schedule 30.10.2012
comment
Причина, по которой я использую схему запроса / ответа, заключается в том, чтобы контролировать доступ к шине, поскольку к ней может быть подключено множество устройств. Заполнение пакетов не повлияло на задержку - person JustMaximumPower; 30.10.2012
comment
@JustMaximumPower: попробуйте скомпилировать setserial для вашей платформы и выполните setserial / dev / ttySx low_latency - person shodanex; 30.10.2012
comment
чтение документации low_latency кажется именно тем, что мне нужно. Однако на мою программу это никак не повлияло. У меня есть теория, почему мне нужно проверять. Я вернусь к тебе. - person JustMaximumPower; 31.10.2012

Последовательные порты в linux «обернуты» в конструкции терминала в стиле unix, что приводит к задержке в 1 тик, то есть 10 мс. Попробуйте, если stty -F /dev/ttySx raw low_latency поможет, правда, никаких гарантий.

На ПК вы можете пойти на хардкор и напрямую поговорить со стандартными последовательными портами, выполнить setserial /dev/ttySx uart none, чтобы отвязать драйвер Linux от последовательного порта hw и управлять портом через inb/outb регистры портов. Я пробовал это, отлично работает.

Обратной стороной является то, что вы не получаете прерываний при поступлении данных и вам нужно опрашивать регистр. довольно часто.

Вы должны иметь возможность сделать то же самое на стороне устройства руки, может быть намного сложнее на экзотическом последовательном порту hw.

person Dima Tisnek    schedule 22.01.2013

Поговорив с еще несколькими инженерами по этой теме, я пришел к выводу, что эта проблема не решается в пользовательском пространстве. Поскольку нам нужно перейти через мост на землю ядра, мы планируем реализовать модуль ядра, который использует наш протокол и дает нам задержки <1 мс.

--- редактировать ---

Оказывается, я был совершенно неправ. Все, что было необходимо, - это увеличить тактовую частоту ядра. К 100 тикам по умолчанию добавлена ​​задержка 10 мс. 1000 Гц и отрицательное значение nice для последовательного процесса дают мне временное поведение, которого я хотел достичь.

person JustMaximumPower    schedule 15.11.2012
comment
Любая ссылка или документ, по которому можно перейти на то же самое. Я также ищу последовательную передачу данных со скоростью более 10 мс через виртуальный последовательный порт на базе USB. Получение данных от микроконтроллера, на который необходимо ответить как можно скорее в течение 100 мкс или около того. Могу попробовать с 1 мс. - person Rick2047; 20.11.2019
comment
@ Rick2047 Вот некоторая документация по скорости тиков ядра elinux.org/Kernel_Timer_Systems. Имейте в виду, что этот вопрос / ответ относится к 2012 году. С тех пор многое изменилось. Я не уверен, насколько это применимо к ядрам без тиков. - person JustMaximumPower; 21.11.2019

Вот что делает setserial, чтобы установить низкую задержку для файлового дескриптора порта:

ioctl(fd, TIOCGSERIAL, &serial);
serial.flags |= ASYNC_LOW_LATENCY;
ioctl(fd, TIOCSSERIAL, &serial);
person Kuba hasn't forgotten Monica    schedule 19.04.2017
comment
Я делаю это в коде, и он меняет таймер задержки (как показано на примере cat), но на самом деле не влияет на задержку. Нужно ли мне закрывать и открывать порт или что-то делать после установки флага? - person Patrick; 02.05.2017
comment
Больше ничего делать не нужно. - person Kuba hasn't forgotten Monica; 02.05.2017
comment
Похоже, вам нужно запустить код от имени пользователя root, чтобы он заработал. - person Patrick; 03.05.2017
comment
@Patrick Вот что получается, если не проверять коды возврата :) - person Kuba hasn't forgotten Monica; 03.05.2017
comment
serial_struct находится в <linux/serial.h>, BTW. - person Alnitak; 15.07.2017

Ни один из этих системных вызовов не влияет на задержку. Если вы хотите как можно быстрее читать и записывать один байт из пользовательского пространства, вы действительно не добьетесь большего успеха, чем простая пара read()/write(). Попробуйте заменить последовательный поток сокетом из другого процесса пользовательского пространства и посмотрите, улучшатся ли задержки. Если нет, то ваши проблемы связаны со скоростью процессора и аппаратными ограничениями.

Вы уверены, что ваше оборудование вообще может это сделать? Нередко можно встретить UART с буфером, обеспечивающим задержку в много байтов.

person Andy Ross    schedule 29.10.2012
comment
Хорошо, это начинает выглядеть как твое право. На плате используется микросхема 16550A, которая имеет FIFO приема и запускает прерывание после получения 14 байтов. Теперь возникает вопрос, как я могу изменить размер FIFO или обойти его? - person JustMaximumPower; 30.10.2012
comment
Это просто последовательный порт ПК? Если это так, вы можете покопаться в драйвере linux (похоже, он находится в drivers / tty / serial / 8250), чтобы увидеть, есть ли ioctl, обеспечивающий программный контроль над глубиной FIFO. - person Andy Ross; 30.10.2012

Короче: используйте USB-адаптер и ASYNC_LOW_LATENCY.

Я использовал USB-адаптер на базе FT232RL на Modbus со скоростью 115,2 кбит / с.

Я получаю около 5 транзакций (на 4 устройства) примерно за 20 мс с ASYNC_LOW_LATENCY. Это включает в себя две транзакции на устройство с медленным подключением (время отклика 4 мс).

Без ASYNC_LOW_LATENCY общее время составляет около 60 мс.

С USB-адаптерами FTDI ASYNC_LOW_LATENCY устанавливает межсимвольный таймер на самом чипе на 1 мс (вместо 16 мс по умолчанию).

В настоящее время я использую самодельный USB-адаптер, и я могу установить задержку для самого адаптера на любое значение, которое я хочу. Установка на 200 мкСм снижает еще на 20 мСм.

person Renate    schedule 04.09.2017

На этих скоростях линии вы не должны видеть столь больших задержек, независимо от того, как вы проверяете готовность.

Вам необходимо убедиться, что последовательный порт находится в необработанном режиме (так что вы выполняете «неканоническое чтение») и что VMIN и VTIME установлены правильно. Вы хотите убедиться, что VTIME равен нулю, чтобы межсимвольный таймер никогда не сработал. Я бы, вероятно, начал с установки VMIN на 1 и настроился оттуда.

Накладные расходы на системные вызовы - ничто по сравнению с временем в сети, поэтому select () vs. poll () и т. Д. Вряд ли что-то изменит.

person janm    schedule 30.10.2012