Что может задержать мой вызов 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.