Подтвердить что ты не робот

Таймер разрешения 1 мс по рекомендованному Linux-каналу

Мне нужен таймер с разрешением 1 мс в Linux. Он используется для увеличения значения таймера, которое, в свою очередь, используется для просмотра различных событий. POSIX timerfd_create не является опцией из-за требования glibc. Я попробовал timer_create и timer_settimer, но лучшее, что я получаю от них, - разрешение 10 мс, меньшие значения по умолчанию равны 10 мс. Getittimer и setitimer имеют разрешение 10 мс в соответствии с man-страницей.

Единственный способ сделать этот таймер, о котором я могу сейчас думать, - использовать clock_gettime с CLOCK_MONOTONIC в моем основном цикле, если тест прошел, а если так увеличить счетчик (а затем проверить, должны ли различные события срабатывать).

Есть ли лучший способ сделать это, чем постоянно запрашивать в основном цикле? Каково рекомендуемое решение?

Язык, который я использую, является простым старым c

Обновление
Я использую ядро ​​2.6.26. Я знаю, что вы можете прерывать его с частотой 1 кГц, а функции POSIX timer_ * могут быть запрограммированы до 1 мс, но это, похоже, не является надежным, и я не хочу его использовать, потому что может понадобиться новое ядро ​​на некоторых системы. У некоторых запасов ядра, похоже, все еще настроено 100 Гц. И мне нужно будет это обнаружить. Приложение может быть запущено на чем-то еще, чем моя система:)

Я не могу спать за 1 мс, потому что на меня могут реагировать сетевые события.

Как я разрешил его Поскольку это не так важно, я просто заявил, что глобальный таймер имеет разрешение 100 мс. Все события, используя свой собственный таймер, должны установить не менее 100 мс для истечения таймера. Я более или менее задавался вопросом, будет ли лучший способ, следовательно, вопрос.

Почему я принял ответ Я думаю, что ответ от freespace лучше всего описывает, почему это невозможно без системы Linux реального времени.

4b9b3361

Ответ 1

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

10 мс - это стандартное разрешение таймера для большинства операционных систем реального времени (RTOS). Но это спорный вопрос в не RTOS - поведение планировщика и диспетчер будет значительно влиять, как быстро вы можете ответить на угасание таймера. Например, предположим, что у вас был таймер с разрешением менее 10 мс, вы не можете отвечать на истечение таймера, если ваш код не работает. Поскольку вы не можете предсказать, когда ваш код будет запущен, вы не сможете точно ответить на истечение срока действия таймера.

Есть, конечно, ядра linux реального времени, см. http://www.linuxdevices.com/articles/AT8073314981.html для списка. RTOS предлагает возможности, благодаря которым вы можете получить мягкие или твердые гарантии о том, когда ваш код будет запущен. Речь идет о единственном способе надежного и точного ответа на истечение срока действия таймеров и т.д.

Ответ 2

Чтобы получить таймеры с разрешением 1 мс, выполните libevent.

Организуйте свои таймеры в min-heap, то есть вершина кучи - это таймер с самым ранним временем истечения (абсолютное) (rb-tree также будет работать, но с большими накладными расходами). Перед вызовом select() или epoll() в вашем главном цикле событий вычислите дельта в миллисекундах между временем истечения срока действия самого раннего таймера и теперь. Используйте эту дельта как тайм-аут до select(). select() и epoll() тайм-ауты имеют разрешение 1 мс.

У меня есть тест разрешения таймера, который использует механизм, описанный выше (но не libevent). Тест измеряет разницу между желаемым временем истечения таймера и фактическим истечением таймеров 1 мс, 5 мс и 10 мс:

1000 deviation samples of  1msec timer: min=  -246115nsec max=  1143471nsec median=   -70775nsec avg=      901nsec stddev=    45570nsec
1000 deviation samples of  5msec timer: min=  -265280nsec max=   256260nsec median=  -252363nsec avg=     -195nsec stddev=    30933nsec
1000 deviation samples of 10msec timer: min=  -273119nsec max=   274045nsec median=   103471nsec avg=     -179nsec stddev=    31228nsec
1000 deviation samples of  1msec timer: min=  -144930nsec max=  1052379nsec median=  -109322nsec avg=     1000nsec stddev=    43545nsec
1000 deviation samples of  5msec timer: min= -1229446nsec max=  1230399nsec median=  1222761nsec avg=      724nsec stddev=   254466nsec
1000 deviation samples of 10msec timer: min= -1227580nsec max=  1227734nsec median=    47328nsec avg=      745nsec stddev=   173834nsec
1000 deviation samples of  1msec timer: min=  -222672nsec max=   228907nsec median=    63635nsec avg=       22nsec stddev=    29410nsec
1000 deviation samples of  5msec timer: min= -1302808nsec max=  1270006nsec median=  1251949nsec avg=     -222nsec stddev=   345944nsec
1000 deviation samples of 10msec timer: min= -1297724nsec max=  1298269nsec median=  1254351nsec avg=     -225nsec stddev=   374717nsec

Тест выполнялся как процесс реального времени на ядре 2.6.34 Fedora 13, наилучшая достигнутая точность таймера 1 мс была равна avg = 22nsec stddev = 29410nsec.

Ответ 3

Я не уверен, что это лучшее решение, но вы можете подумать о написании небольшого модуля ядра, который использует таймеры высокого разрешения ядра для выполнения синхронизации. В принципе, вы должны создать файл устройства, для которого чтение будет возвращаться только с интервалом 1 мс.

Пример такого типа подхода используется в АТС Asterisk через модуль ztdummy. Если вы google для ztdummy, вы можете найти код, который делает это.

Ответ 4

Я думаю, что вам не удастся достичь 1 мс точности со стандартным Linux даже с постоянным запросом в основном цикле, потому что ядро ​​не гарантирует, что ваше приложение будет постоянно получать процессор. Например, вы можете уложить спать в течение десятков миллисекунд из-за упреждающей многозадачности и мало что можете сделать с этим.

Возможно, вы захотите заглянуть в в реальном времени Linux.

Ответ 5

Кажется, мне кажется, что я получаю хорошие результаты с помощью опроса gettimeofday/usleep - мне не нужно было 1000 таймеров в секунду или что-то еще, но мне нужна была хорошая точность с указанием времени для тиков, которые мне нужны, - мое приложение было MIDI-контроллер драм-машины, и я, кажется, помню, что получаю субмиллисекундную точность, которая вам нужна для драм-машины, если вы не хотите, чтобы она звучала как очень плохой барабанщик (особенно с учетом встроенных задержек MIDI) - iirc (это был 2005 год, так что моя память немного нечеткая). Я получал в течение 200 микросекунд целевого времени с помощью usleep.

Тем не менее, я больше не работал в системе. Если у вас есть контролируемая среда, вы можете уйти с таким решением. Если в системе еще больше (смотрите cron, запускающий updateb и т.д.), Тогда все может развалиться.

Ответ 6

Если вы ориентируетесь на платформу x86, вы должны проверить таймеры HPET. Это аппаратный таймер с большой точностью. Он должен поддерживаться вашим матерьем (сейчас все они поддерживают его), и ваше ядро ​​должно содержать драйвер для него. Я использовал его несколько раз без каких-либо проблем и смог добиться гораздо лучшего разрешения, чем 1 мс.

Вот несколько документов и примеров:

Ответ 7

Работаете ли вы на ядре Linux 2.4?

Из статьи VMware KB # 1420 (http://kb.vmware.com/kb/1420).

Linux-операционные системы Linux поддерживают время путем подсчета таймерных прерываний. Нераспределенные 2.4 и более ранние ядра запрограммируйте таймер виртуальной системы на запросить тактовые прерывания на частоте 100 Гц (100 прерываний в секунду). 2.6, с другой стороны, запросить прерывания на частоте 1000 Гц - в десять раз чаще. Некоторые 2.4 ядра, модифицированные поставщиками дистрибутивов, чтобы содержать 2.6 запросить прерывания 1000 Гц или в некоторых случаи, прерывания по другим ставкам, такие как 512 Гц.

Ответ 9

Сначала получите исходный код ядра и скомпилируйте его с помощью настроенного параметра HZ.

  • Если HZ=1000, таймер прерывает 1000 раз в секунду. Можно использовать HZ=1000 для машины i386.
  • На встроенной машине HZ может быть ограничена 100 или 200.

Для хорошей работы опция PREEMPT_KERNEL должна быть включена. Есть ядра, которые не поддерживают эту опцию должным образом. Вы можете проверить их поиск.

Последние ядра, то есть 2.6.35.10, поддерживают опции NO_HZ, которые превращаются по динамическим тикам. Это означает, что на холостом ходу не будет никаких тиков таймера, но таймер будет генерироваться в указанный момент.

В ядро ​​есть RT-патч, но аппаратная поддержка очень ограничена.

Как правило, RTAI - это решение для всех ваших убийц, но его аппаратная поддержка очень ограничена. Однако хорошие контроллеры с ЧПУ, например emc2, используйте RTAI для их синхронизации, может быть, 5000 Гц, но это может быть тяжелая работа по его установке.

Если вы можете, вы можете добавить оборудование для генерации импульсов. Это сделало бы система, которая может быть адаптирована к любой версии ОС.

Ответ 10

Можете ли вы, по крайней мере, использовать наноселек в петле, чтобы спать в течение 1 мс? Или это вещь glibc?

Обновление: Ничего, я вижу из справочной страницы, это может занять до 10 мс дольше, чем указано до тех пор, пока процесс не будет снова запущен.

Ответ 11

Вам не нужна RTOS для простого приложения реального времени. Все современные процессоры имеют таймеры общего назначения. Получите спецификацию для любого целевого процессора, над которым вы работаете. Посмотрите в источнике ядра, в каталоге arch вы найдете источник, специфичный для процессора, для обработки этих таймеров.

Есть два подхода, которые вы можете предпринять с этим:

1) Ваше приложение ТОЛЬКО запускает ваш конечный автомат и ничего больше. Linux - это просто ваш "загрузчик". Создайте объект ядра, который устанавливает персональное устройство. При вставке в ядро ​​настройте свой таймер GP для непрерывного запуска. Вы знаете частоту, в которой он работает. Теперь в ядре явно отключите ваш сторожевой таймер. Теперь отключите прерывания (аппаратное и программное обеспечение). В ядре Linux с одним процессором вызов функции spin_lock() выполнит это (никогда не отпустите его.) CPU - ВАШ. Busy loop, проверяя значение GPT до тех пор, пока не пройдут требуемые # тиков, когда они будут установлены, установите значение для следующего таймаута и введите свой цикл обработки. Просто убедитесь, что время пакета для вашего кода меньше 1 мс

2) Второй вариант. Предполагается, что вы используете упреждающее ядро ​​Linux. Настройте неиспользуемый GPT рядом с вашей операционной системой. Теперь настройте прерывание, чтобы запустить некоторый настраиваемый запас до того, как произойдет ваш тайм-аут 1 мс (скажем, 50-75 мкс.). Когда прерывание срабатывает, вы немедленно отключите прерывания и запустите ожидание появления окна 1 мс, затем введите свой конечный автомат, а затем позволяя прерываниям в режиме ожидания OUT. Это объясняет тот факт, что вы сотрудничаете с ДРУГИМИ вещами в ядре, которые запрещают прерывания. Это означает, что нет никакой другой активности ядра, которая блокирует прерывания в течение длительного времени (более 100us.) Теперь вы можете ИЗМЕРИТЬ точность вашего события обжига и увеличить окно до тех пор, пока оно не будет соответствовать вашим потребностям.

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

Ответ 12

Как насчет использования устройства "/dev/rtc0" (или "/dev/rtc" ) и связанного с ним интерфейса ioctl()? Я думаю, что он предлагает точный счетчик таймера. Невозможно установить скорость только 1 мс, но близкое значение или 1/1024сек (1024 Гц), или на более высокую частоту, например 8192 Гц.