таймер разрешения 1 мс под Linux рекомендуемый способ

StackOverflow https://stackoverflow.com/questions/240058

  •  04-07-2019
  •  | 
  •  

Вопрос

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

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

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

Язык, который я использую, - это простой старый c

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

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

Организуйте свои таймеры в минимальная куча, то есть вершина кучи - это таймер с самым ранним истечением (абсолютного) времени (rb-дерево также работало бы, но с большими накладными расходами).Перед звонком 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

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

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

Пример такого подхода используется в Asterisk PBX с помощью модуля ztdummy.Если вы загуглите по запросу ztdummy, вы можете найти код, который это делает.

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

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

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

Вот некоторая документация и примеры:

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

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

Вы работаете на ядре Linux 2.4?

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

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

Есть патч ktimer для ядра Linux:

http://lwn.net/Articles/167897/

http://www.kernel.org/pub/linux/kernel/projects/rt/

HTH

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

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

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

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

Существует исправление RT для ядра, но аппаратная поддержка очень ограничена.

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top