Kernel Timers

The ultimate resources for time-keeping in the kernel are the timers. Timers are used to dispatch execution of a function (a timer handler) at a particular time in the future. This is different from task queues, in that you can specify when in the future your function must be called, whereas you can’t tell exactly when a queued task will be executed. On the other hand, kernel timers are similar to task queues in that a function registered in a kernel timer is executed only once--timers aren’t cyclic.

There are times when you need to execute operations detached from any process’s context, like turning off the floppy motor or terminating another lengthy shutdown operation. In that case, delaying the return from close wouldn’t be fair to the application program. Using a task queue is also overkill, because a queued task must continually re-register itself while making its time calculations.

A timer is much easier to use. You register your function once and the kernel calls it once when the timer expires. Such a functionality is used often within the kernel proper, but it is sometimes needed by the drivers as well, as in the example of the floppy motor.

Linux uses two kinds of timers, so-called ``old timers'' and new timers. I’ll quickly mention the old timers before showing you how to use the better new timers. The new timers, indeed, are not that new; they were introduced before Linux 1.0.

The old timers consist of 32 static timers. They survive only for compatibility reasons (and because removing them would mean modifying and testing several device drivers).

The data structure underlying the old timers is a bitmask of active timers and an array of timer structures, each containing the pointer to a handling function and the expiration time for the timer. The main problem with the old timers is that each device needing a timer to run a deferred operation has to have a timer number statically assigned to it.

This implementation was acceptable some years ago, when the number of supported devices (and thus the need for timers) was limited, but it is inadequate for current versions of Linux.

I won’t show you how to use the old timers; I mentioned them here for the benefit of the curious reader.

The New Timer List

The new timers are organized in a doubly-linked list. This means that you can create as many timers as you want. A timer is characterized by its timeout value (in jiffies) and the function to be called when the timer expires. The timer handler receives an argument, which is stored in the data structure, together with a pointer to the handler itself.

The data structure of a timer looks like the following, which is extracted from <linux/timer.h>:

struct timer_list {
    struct timer_list *next;          /* never touch this */
    struct timer_list *prev;          /* never touch this */
    unsigned long expires;            /* the timeout, in jiffies */
    unsigned long data;               /* argument to the handler */
    void (*function)(unsigned long);  /* handler of the timeout */
};

As you can see, the implementation of timers is slightly different from that of task queues, though the nature of the list item is similar. The two data structures aren’t quite the same because they were created by two different programmers at almost the same time; one was not copied from the other. Thus, the timer handler takes an argument that is unsigned long instead of void *, and the handler itself is called function instead of routine.

The timeout of a timer is a ``jiffy'' value in that timer->function is required to run when jiffies is equal to or greater than timer->expires. The timeout is an absolute value; it’s not relative to the current time and doesn’t need to be updated.

Once a timer_list structure is initialized, add_timer inserts it into a sorted list, which is then looked up more or less 100 times a second (even if the timer tick is more frequent as it sometimes is, to save CPU time).

In short, these are the functions used to act on timers:

void init_timer(struct timer_list * timer);

This inline function is used to initialize the timer structure. Currently, it zeroes only the prev and next pointers. Programmers are strongly urged to use this function to initialize a timer and to never explicitly touch the pointers in the structure, in order to be forward-compatible.

void add_timer(struct timer_list * timer);

This function inserts a timer into the global list of active timers. It’s interesting to note that the first implementations of the kernel timers behave differently from the current ones; in Linux 1.2, the function add_timer expects timer->expires to be relative to the current jiffy count, so it adds jiffies to the value before inserting the structure in the global list. This incompatibility is dealt with by sysdep.h in the source files.

int del_timer(struct timer_list * timer);

If a timer needs to be removed from the list before it expires, del_timer should be called. When a timer expires, on the other hand, it is automatically removed from the list.

An example of timer usage can be seen in the jiq module. The file /proc/jitimer uses a timer to generate two data lines; the printing function is the same as above for the task queues. The first data line is generated from the read call, while the second line is printed by the timer function after 100 jiffies have elapsed.

The code for /proc/jitimer is the following:

struct timer_list jiq_timer;

void jiq_timedout(unsigned long ptr)
{
    jiq_print((void *)ptr);            /* print a line */
    wake_up_interruptible(&jiq_wait);  /* awake the process */
}


int jiq_read_run_timer(char *buf, char **start, off_t offset,
                       int len, int unused)
{

    jiq_data.len = 0;         /* prepare the argument for jiq_print() */
    jiq_data.buf = buf;
    jiq_data.jiffies = jiffies;
    jiq_data.queue = NULL;    /* don't requeue */

    init_timer(&jiq_timer);              /* init the timer structure */
    jiq_timer.function = jiq_timedout;
    jiq_timer.data = (unsigned long)&jiq_data;
    jiq_timer.expires = jiffies + HZ; /* one second */

    jiq_print(&jiq_data);   /* print and go to sleep */
    add_timer(&jiq_timer);
    interruptible_sleep_on(&jiq_wait);
    return jiq_data.len;
}

Running head /proc/jitimer gives the following output:

time  delta intr_count pid command
2121704   0        0    1092 head
2121804 100        1       0 swapper

It’s apparent from the value of intr_count in the second line that the timer function runs ``at interrupt time.''

What can appear strange when using timers is that the timer expires at just the right time, even if the processor is executing in a system call. I suggested earlier that when a process is running in kernel space, it won’t be scheduled away; the clock tick, however, is special, and it does all of its tasks independent of the current process. You can try to look at what happens when you read /proc/jitbusy in the background and /proc/jitimer in the foreground. Although the system appears to be locked solid by the busy-waiting system call, both the timer queue and the kernel timers continue running.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.59.163