The Linux timer interrupt handler doesn’t do all that much directly. For x86, you’ll find the default PIT/HPET timer interrupt handler in arch/x86/kernel/time.c:
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
global_clock_event->event_handler(global_clock_event);
return IRQ_HANDLED;
}
This calls the event handler for global clock events, tick_handler_periodic by default, which updates the jiffies counter, calculates the global load, and updates a few other places where time is tracked.
As a side-effect of an interrupt occurring, __schedule might end up being called, so a timer interrupt can also lead to a task switch (like any other interrupt).
Changing CONFIG_HZ changes the timer interrupt’s periodicity. Increasing HZ means that it fires more often, so there’s more timer-related overhead, but less opportunity for task scheduling to wait for a while (so interactivity is improved); decreasing HZ means that it fires less often, so there’s less timer-related overhead, but a higher risk that tasks will wait to be scheduled (so throughput is improved at the expense of interactive responsiveness). As always, the best compromise depends on your specific workload. Nowadays CONFIG_HZ is less relevant for scheduling aspects anyway; see How to change the length of time-slices used by the Linux CPU scheduler?
See also How is an Interrupt handled in Linux?
Answer from Stephen Kitt on Stack ExchangeThe Linux timer interrupt handler doesn’t do all that much directly. For x86, you’ll find the default PIT/HPET timer interrupt handler in arch/x86/kernel/time.c:
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
global_clock_event->event_handler(global_clock_event);
return IRQ_HANDLED;
}
This calls the event handler for global clock events, tick_handler_periodic by default, which updates the jiffies counter, calculates the global load, and updates a few other places where time is tracked.
As a side-effect of an interrupt occurring, __schedule might end up being called, so a timer interrupt can also lead to a task switch (like any other interrupt).
Changing CONFIG_HZ changes the timer interrupt’s periodicity. Increasing HZ means that it fires more often, so there’s more timer-related overhead, but less opportunity for task scheduling to wait for a while (so interactivity is improved); decreasing HZ means that it fires less often, so there’s less timer-related overhead, but a higher risk that tasks will wait to be scheduled (so throughput is improved at the expense of interactive responsiveness). As always, the best compromise depends on your specific workload. Nowadays CONFIG_HZ is less relevant for scheduling aspects anyway; see How to change the length of time-slices used by the Linux CPU scheduler?
See also How is an Interrupt handled in Linux?
what Linux does in the timer interrupt?
I take this as asking for the in-between actions.
I copy from that __schedule():
* 3. Wakeups don't really cause entry into schedule(). They add a
* task to the run-queue and that's it.
*
* Now, if the new task added to the run-queue preempts the current
* task, then the wakeup sets TIF_NEED_RESCHED and schedule() gets
* called on the nearest possible occasion:
This is a good starting point; it is well documented. This is the start of a long story. What is a "wakeup"? Is it about R and S states? and blocking?
I have only one slight objection to S.K.'s answer, and to one of the links: about HZ being "not so important anymore".
Can be because there was some confusion in the past, some overcorrection from 100HZ to 1000HZ then back to 300HZ, so to fit "multimedia" at the same time (think of HPET origin 2005: "multimedia timer").
Can be with 4 and more cores it also matters less. But depending on what you want to do with your "server" it still should matter - this latency / throughput trade-off can be important under heavy load.
And if the scheduling is not done in the timer interrupts, then it is done in these "other" interrupts.
And why is HZ not configurable (boot or even runtime)?
I've poked around on the internet quite a bit but I've found very little information. Supposedly (as of over 10 years ago according to the few threads I've found) the scheduler is triggered every time the interrupt timer fires, which implies that adjusting the timer frequency changes the scheduler timeslice. Shouldn't that have an impact on latency/throughput? If that's the case:
-
Why is it only settable at compile time? The kernel gained support for setting the preemption model at boottime and at runtime a while ago. Is the difference from changing the interrupt timer frequency that negligible that no one has bothered making it at least make it settable on the kernel command line even with a non-upstreamed patch? Is there some other technical reason? I have no idea if hardware supports changing it at runtime.
-
Different distros set it to a different value. How much does this change influence their performance in different scenarios? To name a few, Debian and Ubuntu use 250Hz, Arch uses 300Hz, and Fedora and Void use 1000Hz. Of note, the XanMod kernel packaged for Debian/Ubuntu keeps the default 250Hz, which is interesting if aiming for the best latency.
As far as I've looked, no one has done comprehensive benchmarks on the effect of changing the interrupt timer frequency. Does anyone know of any?
Videos
The local timer interrupt is a timer implemented on the APIC that interrupts only a particular CPU instead of raising an interrupt that can be handled by any CPU. It's discussed in Bovet & Cesati's "Understanding the Linux Kernel". A snippet:
The local APIC present in recent 80x86 microprocessors (see the section “Interrupts and Exceptions” in Chapter 4) provides yet another time-measuring device: the CPU local timer.
The CPU local timer is a device similar to the Programmable Interval Timer (PIT) just described that can issue one-shot or periodic interrupts. There are, however, a few differences:
- The APIC’s timer counter is 32 bits long, while the PIT’s timer counter is 16 bits long; therefore, the local timer can be programmed to issue interrupts at very low frequencies (the counter stores the number of ticks that must elapse before the interrupt is issued).
- The local APIC timer sends an interrupt only to its processor, while the PIT raises a global interrupt, which may be handled by any CPU in the system.
- The APIC’s timer is based on the bus clock signal (or the APIC bus signal, in older machines). It can be programmed in such a way to decrease the timer counter every 1, 2, 4, 8, 16, 32, 64, or 128 bus clock signals. Conversely, the PIT, which makes use of its own clock signals, can be programmed in a more flexible way.
A less technical answer than Michael Burr's:
Some things need to be done every jiffy, and it doesn't matter on which CPU.
Other things need to be done every jiffy on each CPU. For example, checking if we need to switch to another process.
The local timer interrupt exists for the second type. Whenever it's executed, we check them and do what's needed.