struct sigevent event;
SIGEV_SIGNAL_INIT(&event, SIGUSR1);
timer_create(CLOCK_REALTIME, &event, &timerid);
Это обеспечит нам выдачу сигнала SIGUSR1 вместо SIGALRM.
Сигналы таймера перехватываются обычными обработчиками сигналов, здесь нет ничего необычного.
Таймеры, создающие потоки
Если вы хотите по каждому срабатыванию таймера создавать новый поток, то вы можете это сделать с помощью struct sigevent
и всех остальных таймерных штук, которые мы только что обсудили:
struct sigevent event;
SIGEV_THREAD_INIT(&event, maintenance_func, NULL);
Однако, пользоваться этим надо очень осторожно, потому что если вы определите слишком короткий интервал, вы можете просто утонуть в создаваемых потоках. Они просто поглотят все ресурсы вашего процессора и оперативной памяти.
Опрос и установка часов реального времени, и кое-что еще
Независимо от применения таймеров, вы можете также опрашивать и устанавливать часы реального времени, а также и плавно подстраивать их. Для этих целей можно использовать следующие функции:
Функция | Тип | Описание |
---|---|---|
ClockAdjust() | QNX/Neutrino | Плавная регулировка времени |
ClockCycles() | QNX/Neutrino | Опрос с высоким разрешением |
clock_getres() | POSIX | Выборка базового разрешения |
clock_gettime() | POSIX | Получение текущего времени суток |
ClockPeriod() | QNX/Neutrino | Получение/установка базового разрешения |
clock_settime() | POSIX | Установка текущего времени суток |
ClockTime() | QNX/Neutrino | Получение/установка текущего времени суток |
Функции clock_gettime() и clock_settime() являются POSIX-функциями, основанными на системном вызове ClockTime(). Эти функции могут применяться для получения и установки текущего времени суток. К сожалению, установка здесь является «жесткой», то есть независимо от того, какое время вы указываете в буфере, оно немедленно делается текущим. Это может иметь пугающие последствия, особенно когда получается, что время «повернуло вспять», потому что устанавливаемое время оказалось меньше «реального». Вообще настройка часов таким способом должна выполняться только при включении питания или когда время сильно не соответствует «реальному».
Если нужна плавная корректировка текущего времени, ее можно реализовать с помощью функции ClockAdjust():
int ClockAdjust(clockid_t id,
const struct _clockadjust *new,
const struct _clockadjust *old);
Параметрами здесь являются источник синхроимпульсов (всегда используйте CLOCK_REALTIME) и параметры new и old. Оба эти параметра являются необязательными и могут быть заданы как NULL. Параметр old просто возвращает текущую корректировку. Работа по корректировке часов управляется параметром new, который является указателем на структуру, содержащую два элемента, tick_nsec_inc и tick_count. Действует функция ClockAdjust() очень просто — каждые tick_count отсчетов системных часов к существующему значению системного времени добавляется корректировка tick_nsec_inc. Это означает, что чтобы передвинуть время вперед («догоняя» реальное), вы задаете для tick_nsec_inc положительное значение. Заметьте, что не надо переводить время назад — вместо этого, если ваши часы спешат, задайте для tick_nsec_inc небольшое отрицательное значение, и ваши часы соответственно замедлят ход. Таким образом, вы немного замедляете часы, пока их показания не будут соответствовать действительности. Существует эмпирическое правило, гласящее, что не следует корректировать системные часы значением, превышающим 10% от базового разрешения вашей системы (см. функцию ClockPeriod() и ее друзей, о них мы поговорим в следующем параграфе).
Как мы и говорили на протяжении всей этой главы, нельзя сделать ничего с большей точностью, чем принятая в системе базовая разрешающая способность по времени. Напрашивается вопрос: а как настроить эту базовую разрешающую способность? Для этого вы можете использовать следующую функцию:
int ClockPeriod(clockid_t id,
const struct _clockperiod *new,
struct _clockperiod *old, int reserved);
Как и в случае с описанной выше функцией ClockAdjust(), с помощью параметров new и old вы получаете и/или устанавливаете значения базовой разрешающей способности по времени. Параметры new и old являются указателями на структуры типа struct _clockperiod
, которые, в свою очередь, содержат два элемента — nsec и fract. На настоящий момент элемент fract должен быть равен нулю (это число фемтосекунд (миллиардная доля микросекунды — прим. ред.); нам, вероятно, это еще не скоро потребуется). Параметр nsec указывает, сколько наносекунд содержится в интервале между двумя базовыми отсчетами времени. Значение этого интервала времени по умолчанию — 10 миллисекунд, поэтому значение nsec (если вы используете функцию для получения базового разрешения) будет приблизительно равно 10 миллионам наносекунд. (Как мы уже упоминали ранее в разделе «Источники прерываний таймера», это не будет в точности равняться 10 миллисекундам.)
При этом вы можете, конечно, не стесняться и попробовать назначить базовой разрешающей способности какое-нибудь смехотворно малое значение, но тут вмешается ядро и эту вашу попытку пресечет. В общем случае, в большинстве систем допускаются значения от 1 миллисекунды до сотен микросекунд.
Существует одна система отсчета времени, которая не подчиняется описанным выше правилам «базовой разрешающей способности по времени». Некоторые процессоры оборудованы встроенным высокочастотным (высокоточным) счетчиком, к которому QNX/Neutrino обеспечивает доступ при помощи функции ClockCycles(). Например, в процессоре Pentium, работающем с частотой 200 МГц, этот счетчик увеличивается тоже с частотой в 200 МГц, и поэтому он может обеспечить вам значение времени с точностью до 5 наносекунд. Это особенно полезно, когда вы хотите точно выяснить, сколько времени затрачивается на выполнение конкретного фрагмента кода (в предположении, конечно, что он не будет вытеснен). В этом случае вы должны вызвать функцию ClockCycles() перед началом вашего фрагмента и после его окончания, а потом просто подсчитать разность полученных отсчетов. Более подробно это описано в руководстве по Си-библиотеке.