Что может задержать мой вызов select()?

У меня есть небольшая программа, работающая на Linux (на встроенном ПК с двухъядерным процессором Intel Atom 1.6GHz с Debian 6, работающим под управлением Linux 2.6.32-5), который обменивается данными с внешним оборудованием через FTDI USB-to-serial converter ( используя модуль ядра ftdi_sio и устройство /dev/ttyUSB*). По существу, в моем основном цикле я запускаю

  • clock_gettime() с помощью CLOCK_MONOTONIC
  • select() с таймаутом 8 мс
  • clock_gettime() как раньше
  • Вывести разницу во времени двух вызовов clock_gettime()

Чтобы иметь некоторый уровень "мягких" гарантий реального времени, этот поток работает как SCHED_FIFO с максимальным приоритетом (отображается как "RT" в top). Это единственный поток в системе, работающий в этом приоритете, ни один другой процесс не имеет таких приоритетов. Мой процесс имеет еще один поток SCHED_FIFO с более низким приоритетом, а все остальное - в SCHED_OTHER. Два потока "реального времени" не связаны с ЦП и очень мало отличаются от ожидания ввода-вывода и передачи данных.

Ядро, которое я использую, не имеет патчей RT_PREEMPT (я могу переключиться на этот патч в будущем). Я знаю, что если я хочу "правильного" реального времени, мне нужно переключиться на RT_PREEMPT или, лучше, Xenomai или тому подобное. Но тем не менее я хотел бы знать, что стоит за следующими временными аномалиями в ядре "vanilla":

  • Примерно 0,03% всех вызовов select() рассчитаны на более 10 мс (помните, что время ожидания составляло 8 мс).
  • Три худших случая (из более чем 12 миллионов вызовов) составили 31,7 мс, 46,8 мс и 64,4 мс.
  • Все вышесказанное произошло в течение 20 секунд друг от друга, и я думаю, что какое-то задание cron могло мешать (хотя системные журналы имеют низкую информацию, кроме того, что в то время выполнялся cron.daily).

Итак, мой вопрос: какие факторы могут быть задействованы в таких крайних случаях? Это то, что может произойти внутри самого ядра Linux, т.е. Я должен переключиться на RT_PREEMPT или даже на не-USB-интерфейс и Xenomai, чтобы получить более надежные гарантии? Может ли /proc/sys/kernel/sched_rt_runtime_us укусить меня? Есть ли другие факторы, которые я, возможно, пропустил?

Еще один способ поставить этот вопрос: что еще я могу сделать, чтобы уменьшить эти аномалии задержки, не переключаясь на "более сложную" среду реального времени?

Обновление. Я наблюдал новый "худший худший случай" около 118,4 мс (один раз в общей сложности около 25 миллионов вызовов select()). Даже когда я не использую ядро ​​с каким-либо расширением в реальном времени, меня несколько беспокоит то, что крайний срок может быть пропущен более чем на одну десятую секунды.

Ответ 1

Без дополнительной информации трудно указать на что-то конкретное, поэтому я просто угадываю здесь:

  • Прерывания и код, вызываемый прерываниями, занимают так много времени в ядре, что поток реального времени значительно задерживается. Это зависит от частоты прерываний, которые задействованы обработчики прерываний и т.д.
  • Нить с более низким приоритетом не будет прерываться внутри ядра до тех пор, пока он не даст процессор или не покинет ядро.
  • Как указано в этом SO-ответе, прерывания управления ЦП и термическое управление также могут вызывать значительные временные задержки (до 300 мс наблюдалось плакатом).

118 мс кажется довольно много для процессора с частотой 1,6 ГГц. Но одного водителя, который случайно заблокирует процессор в течение некоторого времени, будет достаточно. Если возможно, попробуйте отключить некоторые драйверы или использовать разные комбинации драйверов/аппаратных средств.

sched_rt_period_us и sched_rt_period_us не должны быть проблемой, если они настроены на разумные значения, и ваш код ведет себя так, как вы ожидаете. Тем не менее, я бы удалил предел для потоков RT и посмотрел, что произойдет.

Что еще вы можете сделать? Напишите драйвер устройства! Это не так сложно, и обработчики прерываний получают более высокий приоритет, чем потоки реального времени. Может быть проще переключиться на ядро ​​реального времени, но YMMV.