Отметим, что импульсы высокой частоты делятся на целочисленный делитель. Это означает, что результирующий период импульсов не будет точно равен 10 миллисекундам, потому что исходный период 10 миллисекундам не кратен. Поэтому ISR ядра из вышеприведенного примера будет реально вызываться по истечении каждых 9.9999296004 миллисекунд.
Большое дело, скажете вы, ну и что? Ну ладно, для нашего 15-секундного счетчика это годится. 15 секунд — это 1500 отсчетов таймера; расчеты показывают, что погрешность будет в районе 106 микросекунд:
15 с – 1500 * 9.9999296004 мс =
= 15000 мс – 14999.8944006 мс =
= 0.1055994 мс =
= 105.5994 мкс
К сожалению, продолжая наши математические выкладки, приходим к выводу, что при таких раскладах погрешность составляет 608 миллисекунд в день, что равняется приблизительно 18.5 секунд в месяц, или почти 3.7 минут в год!
Можно предположить, что при использовании делителей другого типа ошибка может быть либо меньше, либо больше, в зависимости от погрешности округления. К счастью, ядро это знает и вводит соответствующие поправки.
Ключевой момент всей этой истории состоит в том, что независимо от красивого округленного значения, реальное значение выбирается в сторону ускорения отсчета.
Разрешающая способность отсчета времени
Пусть отсчеты времени таймера генерируются чуть чаще, чем раз в 10 миллисекунд. Смогу ли я надежно обеспечить ожидание длительностью в 3 миллисекунды?
Не-а.
Подумайте, что происходит в ядре. Мы вызываем стандартную библиотечную функцию
Мораль: не следует рассчитывать на то, что разрешающая способность ваших таймеров будет лучше, чем у системного отсчета.
В QNX/Neutrino у приложений есть возможность программной подстройки аппаратного делителя и ядра вместе с ним (чтобы ядро знало, с какой частотой вызывается ISR таймера). Мы поговорим об этом далее в разделе «Опрос и установка часов реального времени».
Флуктуации отсчета времени
Существует еще одно явление, которое вы должны принимать во внимание. Предположим, что разрешающая способность у вас равна 10 миллисекундам, а вы желаете сформировать задержку длительностью в 20 миллисекунд.
Всегда ли вы можете быть уверены, что от момента вызова функции
Никогда.
На это есть две серьезные причины. Первая причина довольно проста: при блокировании поток изымается из очереди готовности. Это означает, что процессор может перейти к другому потоку вашего приоритета. Когда ваши 20 миллисекунд истекут, ваш поток будет помещен в конец очереди готовности по этому приоритету и будет таким образом оставлен на милость потока, выполняющегося в данный момент. Это относится также к обработчикам прерываний и к потокам более высокого приоритета — то, что ваш поток перешел в состояние READY, еще не означает, что ему сразу предоставят процессор.
Вторая причина несколько более хитрая. Чтобы понять ее смысл, посмотрите на нижеприведенный рисунок.
Флуктуации отсчета времени.
Проблема здесь состоит в том, что ваш запрос является асинхронным по отношению к источнику отсчетов. У вас нет никакой возможности синхронизировать аппаратный таймер с вашим запросом. Поэтому в итоге вы получите интервал задержки где-то в диапазоне от 20 до 30 мс — в зависимости от того, в какой момент между отсчетами аппаратных часов возник ваш запрос.
Типы таймеров