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 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.
52.15.59.163