Таймер разрешения 1 мс под Linux рекомендуется - PullRequest
18 голосов
/ 27 октября 2008

Мне нужен таймер с разрешением 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 реального времени.

Ответы [ 12 ]

18 голосов
/ 18 января 2011

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

Организуйте свои таймеры в min-heap , то есть верхняя часть кучи - это таймер с самым ранним временем истечения (абсолютным) (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 мс была avg = 22 нсек, stddev = 29410 нсек.

15 голосов
/ 27 октября 2008

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

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

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

6 голосов
/ 27 октября 2008

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

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

5 голосов
/ 27 октября 2008

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

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

3 голосов
/ 18 января 2011

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

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

2 голосов
/ 20 января 2009

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

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

1 голос
/ 18 января 2011

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

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

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

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

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

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

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

1 голос
/ 27 октября 2008

Имеется патч ktimer для ядра Linux:

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

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

НТН

1 голос
/ 27 октября 2008

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

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

)

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

0 голосов
/ 31 мая 2014

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...