Clock and timer services

Updated: April 19, 2023

Clock services are used to maintain the time of day, which is in turn used by the kernel timer calls to implement interval timers.

Note: Valid dates on a QNX Neutrino system range from January 1970 to at least 2038. The time_t data type is an unsigned 32-bit number, which extends this range for many applications through 2106. The kernel itself uses unsigned 64-bit numbers to count the nanoseconds since January 1970, and so can handle dates through 2554. If your system must operate past 2554 and there's no way for the system to be upgraded or modified in the field, you'll have to take special care with system dates (contact us for help with this).

The ClockTime() kernel call allows you to get or set the system clock specified by an ID (CLOCK_REALTIME), which maintains the system time. Once set, the system time increments by some number of nanoseconds based on the resolution of the system clock. This resolution can be queried or changed using the ClockPeriod() call, which allows you to set the system timer to some multiple of nanoseconds; the OS kernel does the best it can to satisfy the precision of the request with the hardware available.

The interval selected by the specified resolution is always rounded to an integral multiple based on the precision of the underlying hardware timer. Of course, setting it to an extremely low value can result in a significant portion of CPU performance being consumed by servicing timer interrupts.

Within the system page, an in-memory data structure, there's a 64-bit field (nsec) that holds the number of nanoseconds since the system was booted. This field is always monotonically increasing and is never affected by setting the current time of day via ClockTime() or ClockAdjust(). You can access this field via the SYSPAGE_ENTRY(qtime)->nsec macro command.

The ClockCycles() function returns the current value of a free-running 64-bit cycle counter. This is implemented on each processor as a high-performance mechanism for timing short intervals. For example, on Intel x86 processors, an opcode that reads the processor's time-stamp counter is used. Other CPU architectures have similar instructions.

On processors that don't implement such an instruction in hardware, the kernel will emulate one. This will provide a lower time resolution than if the instruction is provided (838.095345 nanoseconds on an IBM PC-compatible system).

In all cases, the SYSPAGE_ENTRY(qtime)->cycles_per_sec field gives the number of ClockCycles() increments in one second.

Note: QNX Neutrino requires that the hardware underlying ClockCycles() be synchronized across all processors on an SMP system. If it isn't, you might encounter some unexpected behavior, such as drifting times and timers.

The ClockId() function returns a special clock ID that you can use to track the CPU time that a process or thread uses. For more information, see Monitoring execution times in the “Understanding the Microkernel's Concept of Time” chapter of the QNX Neutrino Programmer's Guide.

Microkernel call POSIX call Description
ClockTime() clock_gettime(), clock_settime() Get or set the time of day (using a 64-bit value in nanoseconds ranging from 1970 to 2554)
ClockAdjust() N/A Apply small time adjustments to synchronize clocks.
ClockCycles() N/A Read a 64-bit free-running high-precision counter
ClockPeriod() clock_getres() Get or set the period of the clock
ClockId() clock_getcpuclockid(), pthread_getcpuclockid() Get a clock ID for a process or thread CPU-time clock

The kernel can run in a tickless mode in order to reduce power consumption, but this is a bit of a misnomer. The system still has clock ticks, and everything runs as normal unless the system is idle. Only when the system goes completely idle does the kernel turn off clock ticks, and in reality what it does is slow down the clock so that the next tick interrupt occurs just after the next active timer is to fire, so that the timer will fire immediately. To enable tickless operation, specify the -Z option for the startup-* code.