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

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

В чем проблема с использованием glibc?

Cristian Ciupitu 27.10.2008 17:49

timerfd_create () не имеет ничего общего с POSIX, это специфическая функция Linux.

Maxim Egorushkin 18.01.2011 15:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
19
2
37 872
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

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

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

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

Вы можете посмотреть Linux в реальном времени.

Вы используете ядро ​​Linux 2.4?

Из статьи базы знаний VMware № 1420 (http://kb.vmware.com/kb/1420).

Linux guest operating systems keep time by counting timer interrupts. Unpatched 2.4 and earlier kernels program the virtual system timer to request clock interrupts at 100Hz (100 interrupts per second). 2.6 kernels, on the other hand, request interrupts at 1000Hz - ten times as often. Some 2.4 kernels modified by distribution vendors to contain 2.6 features also request 1000Hz interrupts, or in some cases, interrupts at other rates, such as 512Hz.

Ответ принят как подходящий

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

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

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

Даже на старых ядрах без RT, таких как 2.6.18 в CentOS 5.3, вы получаете таймеры с разрешением 1 мс.

Maxim Egorushkin 18.01.2011 15:20

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

user562374 18.01.2011 16:14

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

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

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

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

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

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

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

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

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

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

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

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

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

Чтобы получить разрешение 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 мс составила avg = 22nsec stddev = 29410nsec.

"У меня есть тест разрешения таймера" - Покажите, пожалуйста, код. Мне очень интересно, что измеряет так называемый "его фактическое истечение"

xakepp35 13.11.2018 18:41

@ xakepp35 Код находится под соглашением о неразглашении, поэтому я не могу его показать. И мне не нравится ваше унизительное «так называемое».

Maxim Egorushkin 13.11.2018 18:50

@MaximEgorushkin «актуальный» здесь в некотором роде субъективен, его действительно нельзя назвать «актуальным». Даже атомные часы могут быть в какой-то степени «актуальными». Я просто хочу сказать, что «актуальный» не «абсолютный» (как я хотел почувствовать, когда читал это). Я спросил об инструменте измерения или источнике времени (например, был ли это clock_gettime()? Внешний аппаратный таймер? Или что?)

xakepp35 13.11.2018 18:54

@ xakepp35 rdtsc преобразован в наносекунды.

Maxim Egorushkin 13.11.2018 18:58

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

Вы можете использовать два подхода:

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

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

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

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

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