Team LiB
Previous Section Next Section

The Timer Interrupt Handler

Now that we have an understanding of HZ, jiffies, and what the system timer's role is, let's look at the actual implementation of the timer interrupt handler. The timer interrupt is broken into two pieces: an architecture-dependent and an architecture-independent routine.

The architecture-dependent routine is registered as the interrupt handler for the system timer and, thus, runs when the timer interrupt hits. Its exact job depends on the given architecture, of course, but most handlers perform at least the following work:

  • Obtain the xtime_lock lock, which protects access to jiffies_64 and the wall time value, xtime

  • Acknowledge or reset the system timer as required

  • Periodically save the updated wall time to the real time clock

  • Call the architecture-independent timer routine, do_timer()

The architecture-independent routine, do_timer(), performs much more work:

  • Increment the jiffies_64 count by one (this is safe, even on 32-bit architectures, because the xtime_lock lock was previously obtained)

  • Update resource usages, such as consumed system and user time, for the currently running process

  • Run any dynamic timers that have expired (discussed in the following section)

  • Execute scheduler_tick(), as discussed in Chapter 4, "Process Scheduling"

  • Update the wall time, which is stored in xtime

  • Calculate the infamous load average

The actual routine is very simple because other functions handle most of the previously discussed work:

void do_timer(struct pt_regs *regs)
{
        jiffies_64++;

        update_process_times(user_mode(regs));
        update_times();
}

The user_mode() macro looks at the state of the processor registers, regs, and returns one if the timer interrupt occurred in user-space and zero if the interrupt occurred in kernel mode. This enables update_process_times() to attribute the previous tick to the proper mode, either user or system:

void update_process_times(int user_tick)
{
        struct task_struct *p = current;
        int cpu = smp_processor_id();
        int system = user_tick ^ 1;

        update_one_process(p, user_tick, system, cpu);
        run_local_timers();
        scheduler_tick(user_tick, system);
}

The update_one_process() function does the actual updating of the process's times. It is rather elaborate, but note how one of either the user_tick or the system value is equal to one and the other is zero, because of the exclusive-or (XOR). Therefore, updates_one_process() can simply add each value to the corresponding counter without a branch:

/*
 * update by one jiffy the appropriate time counter
 */
p->utime += user;
p->stime += system;

The appropriate value is increased by one and the other value remains the same. You might realize that this implies that the kernel credits a process for running the entire previous tick in whatever mode the processor was in when the timer interrupt occurred. In reality, the process might have entered and exited kernel mode many times during the last tick. In fact, the process might not even have been the only process running in the last tick! This granular process accounting is classic Unix, and without much more complex accounting, this is the best the kernel can provide. It is also another reason for a higher frequency tick rate.

Next, the run_local_timers() function marks a softirq (see Chapter 7, "Bottom Halves and Deferring Work") to handle the execution of any expired timers. Timers are covered in a following section, "Timers."

Finally, the scheduler_tick() function decrements the currently running process's timeslice and sets need_resched if needed. On SMP machines, it also balances the per-processor runqueues as needed. This was all discussed in Chapter 4.

When update_process_times() returns, do_timer() calls update_times() to update the wall time:

void update_times(void)
{
        unsigned long ticks;

        ticks = jiffies - wall_jiffies;
        if (ticks) {
                wall_jiffies += ticks;
                update_wall_time(ticks);
        }
        last_time_offset = 0;
        calc_load(ticks);
}

The ticks value is calculated to be the change in ticks since the last update. In normal cases, this is, of course, one. In rare situations, timer interrupts can be missed and the ticks are said to be lost. This can occur if interrupts are off for a long time. It is not the norm and quite often a bug. The wall_jiffies value is increased by the ticks valuethus, it is equal to the jiffies value of the most recent wall time updateand update_wall_time() is called to update xtime, which stores the wall time. Finally, calc_load() is called to update the load average and update_times() returns.

The do_timer() function returns to the original architecture-dependent interrupt handler, which performs any needed cleanup, releases the xtime_lock lock, and finally returns.

All this occurs every 1/HZ of a second. That is 1000 times per second on your PC.

    Team LiB
    Previous Section Next Section