Reducing the power consumption of clocks and timers
If your system needs to manage power consumption, you can set up your timers to help the system to sleep for longer intervals, saving power.
The kernel can reduce power consumption by running in tickless mode.
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.
In order for the kernel to enter tickless mode, the following must all be true:
- You must have enabled tickless operation by specifying the -Z option for the startup-* code. If this option is set, the QTIME_FLAG_TICKLESS flag is set in the qtime member of the system page (see the System Page chapter of Building Embedded Systems).
- The clock must not be in the process of being adjusted because of a call to ClockAdjust().
- All processors must be idle.
If the kernel decides to enter tickless mode, it determines the number of nanoseconds until the next timer is supposed to expire. If that expiry is more than a certain time away, the kernel reprograms the timer hardware to generate its next interrupt shortly after that, and then sets some variables to indicate that it's gone tickless. When something other than the idle thread is made ready, the kernel stops tickless operation, checks the list of active timers, and fires off any that were supposed to expire before it went to sleep.
Here are some tips for helping the kernel reduce power consumption:
- Avoid polling loops and periodic timers.
Typical implementations of polling loops give the kernel no option to delay your application's execution when in low-power mode. On every loop iteration, you wake up the system and cause extra power usage as a result.
Polling loops are commonly caused by using the following functions in an unbounded loop:
- sleep()
- nanosleep()
- pthread_cond_timedwait()
- select() or poll() with a timeout
If you must use a polling loop, make its interval and tolerance as long as possible. This will minimize the power impact while allowing the kernel (through timer tolerance) to batch as many wakeups as possible.
- Use timer tolerance.
When setting up a timer, you can specify how much tolerance the kernel is allowed when scheduling your thread after the timer fires. The kernel uses your timer's tolerance only when the system isn't awake. Essentially, tolerance allows the system to sleep longer and then do more work when it does wake up. For more information, see
Tolerant and high-resolution timers
in this chapter. - Use
ionotify()
combined with a tolerant timer in place of select() with a timeout.
The select() and poll() functions can't use a tolerant timer. You can duplicate the behavior of these functions by using ionotify(), while using a tolerant timer for the timeout. This method also has the advantage of providing a QNX OS pulse when input is available, letting you use your application's existing MsgReceive() loop.