Linux Timers
This article discusses using Timers in user
space in Linux. Various methods to start and stop dynamic timers, calculate
time spent between instructions etc, are discussed below. The first section
deals with the varius sources for timer interrupts. The next section provides
details on the various APIs that “user space” applications can use for their
timer needs. Finally, some code snippets are provided that will help the reader
on how exactly to use some of the timer methods explained earlier.
Timer sources
RTC
The notes for this section are taken from [1]. Linux has two
largely-compatible user space RTC API families
·
/dev/rtc ... is the RTC provided by PC compatible
systems (Old "RTC Class" Drivers)
·
/dev/rtc0, /dev/rtc1 ... supported by a wide
variety of RTC chips on all systems (New “RTC Class" Drivers)
Both the above RTC methods use the same API to make
requests, but the hardware may not offer the same functionality.
Old "RTC Class" Drivers:
All PCs have a Real Time Clock (RTC) built into them, which
tracks wall clock time and is battery backed. It tracks the Coordinated
Universal Time (UTC, formerly "Greenwich Mean Time"). It can also be
used to generate signals from a slow 2Hz to a relatively fast 8192Hz, in
increments of powers of two. These signals are reported by interrupt number 8.
The interrupts are reported via /dev/rtc (major 10, minor
135, read only character device) in the form of an unsigned long. The low byte
contains the type of interrupt (update-done, alarm-rang, or periodic) that was
raised, and the remaining bytes contain the number of interrupts since the last
read. Status information is reported
through the pseudo-file /proc/driver/rtc if the /proc filesystem was
enabled. The driver has built in locking
so that only one process is allowed to have the /dev/rtc interface open at a
time.
A user process can monitor these interrupts by doing a read
or a select on /dev/rtc -- either will block/stop the user process until the
next interrupt is received.
For managing clock time, if the kernel time is synchronized
with an external source, the kernel will write the time back to the CMOS clock
every 11 minutes. In the process of doing this, the kernel briefly turns off
RTC periodic interrupts, so be aware of this if you are doing serious work.
The alarm and/or interrupt frequency are programmed into the
RTC via various ioctl calls as listed in ./include/linux/rtc.h
New "RTC Class" Drivers
Because Linux supports many non-ACPI and non-PC platforms,
some of which have more than one RTC style clock, it needed a more portable
solution than expecting a single battery-backed MC146818 clone on every system.
Accordingly, a new "RTC Class" framework has been defined. It offers three different userspace
interfaces:
·
/dev/rtcN ... much the same as the older
/dev/rtc interface
·
/sys/class/rtc/rtcN ... sysfs attributes support
readonly access to some RTC attributes.
·
/proc/driver/rtc ... the first RTC (rtc0) may
expose itself using a procfs interface.
More information is (currently) shown here than through sysfs.
This new framework removes the "one RTC per
system" restriction.
TSC
All 80x86 microprocessors include a CLK input pin, which
receives the clock signal of an external oscillator. Starting with the Pentium,
80x86 microprocessors sport a counter that is increased at each clock
signal. The Time Stamp Counter is a
64-bit register present on all x86 processors since the Pentium [2]. It counts
the number of clock ticks since reset. Instruction RDTSC returns the TSC in
EDX:EAX. Its opcode is 0F 31.
For Pentiums, you can get the number
of clock cycles elapsed since the last reboot with the following C code [5]
#include
static uint64_t get_tsc(void) {
unsigned
long long tsc;
__asm__
__volatile__ ("rdtsc" : "=A" (tsc));
return
tsc; }
It is preferred that developers use read the value of
CLOCK_MONOTONIC clock using POSIX clock_gettime function [3]. Note that
gettimeofday() uses the clock_gettime(CLOCK_REALTIME) back end [4]. Similarly
settimeofday() and clock_settime(CLOCK_REALTIME) use the same backend code, the
only difference being that clock_settime allows for nanosecond resolution in
setting the time. The result of clock_gettime() is guaranteed to be monotonic,
and the resolution of clock_gettime() is nanoseconds.
PIT
It is also called
the System Clock and accurately generates interrupts at regular time intervals.
The chip itself has 3 channels: Channel 0 is tied to is tied to IRQ0, to
interrupt the CPU at predictable and regular times, Channel 1 is system
specific, and Channel 2 is connected to the system speaker. In this section, we
are only concerned with Channel 0 - mapped to IRQ0. (The PIT is also used to
drive an audio amplifier connected to the computer's internal speaker.)
HPET (hrtimers)
The High Precision Event Timer
(HPET) is a new timer chip developed jointly by
Intel and Microsoft. The hrtimers set of APIs uses these high precision timers.
If an HPET is present in the system, it is preferred for use as a sytem clock
verses using a PIT.
ACPI Power Management Timer (ACPI PMT)
Timer APIs
Interval Timers: setitimer
Interval timers are
activated by means of the POSIX setitimer( ) system call. The first parameter
specifies which of the following policies should be adopted:
·
ITIMER_REAL: The actual elapsed time; the
process receives SIGALRM signals.
·
ITIMER_VIRTUAL: The time spent by the process in
User Mode; the process receives SIGVTALRM signals.
·
ITIMER_PROF: The time spent by the process both
in User and in Kernel Mode; the process receives SIGPROF signals.
The interval timers
can be either single-shot or periodic. The second parameter of setitimer( )
points to a structure of type itimerval that specifies the initial duration of
the timer (in seconds and nanoseconds) and the duration to be used when the
timer is automatically reactivated (or zero for single-shot timers).The third
parameter of setitimer( ) is an optional pointer to an itimerval structure that
is filled by the system call with the previous timer parameters.
To implement an
interval timer for each of the preceding policies, the process descriptor
includes three pairs of fields:
·
it_real_incr and it_real_value
·
it_virt_incr and it_virt_value
·
it_prof_incr and it_prof_value
The first field of
each pair stores the interval in ticks between two signals; the other field
stores the current value of the timer.
The ITIMER_REAL
interval timer is implemented by using dynamic timers because the kernel must
send signals to the process even when it is not running on the CPU. Therefore,
each process descriptor includes a dynamic timer object called real_timer. The
setitimer( ) system call initializes the real_timer fields and then invokes
add_timer( ) to insert the dynamic timer in the proper list. When the timer
expires, the kernel executes the it_real_fn( ) timer function. In turn, the
it_real_fn( ) function sends a SIGALRM signal to the process; then, if
it_real_incr is not null, it sets the expires field again, reactivating the
timer.
The ITIMER_VIRTUAL
and ITIMER_PROF interval timers do not require dynamic timers, because they can
be updated while the process is running. The account_it_virt( ) and account_it_prof(
) functions are invoked by update_ process_times( ), which is called either by
the PIT's timer interrupt handler (UP) or by the local timer interrupt handlers
(SMP). Therefore, the two interval timers are updated once every tick, and if
they are expired, the proper signal is sent to the current process.
Interval Timers: alarm
The alarm system call is the same as
settimer system call with REAL signal (explained above). Expiry times are given
in seconds.
Posix Timers: timer_create
The POSIX 1003.1b standard introduced a new type of software
timers for User Mode programs in particular, for multithreaded and real-time
applications. These timers are often referred to as POSIX timers.
These set of calls allow per thread creation of timers, with
a user defined signal to be sent on timer expiry. The Linux 2.6 kernel offers
two types of POSIX clocks:
·
CLOCK_REALTIME:
This virtual clock represents the real-time clock of the system which is
essentially the value of the xtime variable.
·
CLOCK_MONOTONIC:
This virtual clock represents the real-time clock of the system purged
of every time warp due to the synchronization with an external time source.
The Linux kernel implements the POSIX timers by means of
dynamic timers. POSIX timers, however, are much more flexible and reliable than
traditional interval timers. A couple of significant differences between them
are:
·
When a traditional interval timer decays, the
kernel always sends a SIGALRM signal to the process that activated the timer.
Instead, when a POSIX timer decays, the kernel can send every kind of signal,
either to the whole multithreaded application or to a single specified thread.
The kernel can also force the execution of a notifier function in a thread of
the application, or it can even do nothing (it is up to a User Mode library to
handle the event).
·
If a traditional interval timer decays many
times but the User Mode process cannot receive the SIGALRM signal (for instance
because the signal is blocked or the process is not running), only the first signal
is received: all other occurrences of SIGALRM are lost. The same holds for
POSIX timers, but the process can invoke the timer_getoverrun( ) system call to
get the number of times the timer decayed since the generation of the first
signal.
In a process that has multiple threads, it
is important to understand that as per POSIX API, there are certain properties
that are not shared among threads. Some of these are signal mask, errno
variable, thread id (via gettid call), etc. Then, there are certain properties
that are shared among the threads in the same thread group. These are PID,
Parent PID, Interval timers and Posix timers.
Use the following call to check if your
Linux system is NPTL compliant. If the following shell command
getconf
GNU_LIBPTHREAD_VERSION
returns a string containing NPTL, then your
system uses NPTL libraries, and the above POSIX compliance holds true.
Select Call: timerfd
The system calls
timerfd_create/gettime/settime operate on a timer that delivers timer
expiration notifications via a file descriptor.
They provide an alternative to the use of setitimer or timer_create,
with the advantage that the file descriptor may be monitored by select, poll,
and epoll [7].
Library: libevent
The libevent API provides a mechanism to execute a callback
function when a specific event occurs on a file descriptor or after a timeout
has been reached. Furthermore, libevent also support callbacks due to signals
or regular timeouts [8,9].
Standard usage
Every program that uses libevent must include the
header, and pass the -levent flag to the linker. Before using
any of the functions in the library, you must call event_init() or
event_base_new() to perform one-time initialization of the libevent library.
Event notification
For each file descriptor that you wish to monitor, you must
declare an event structure and call event_set() to initialize the members of
the structure. To enable notification, you add the structure to the list of
monitored events by calling event_add(). The event structure must remain
allocated as long as it is active, so it should be allocated on the heap.
Finally, you call event_dispatch() to loop and dispatch events.
Timers
libevent can also be used to create timers that invoke a
callback after a certain amount of time has expired. The evtimer_set() function
prepares an event struct to be used as a timer. To activate the timer, call
evtimer_add(). Timers can be deactivated by calling evtimer_del().
Timeouts
In addition to simple timers, libevent can assign timeout events
to file descriptors that are triggered whenever a certain amount of time has
passed with no activity on a file descriptor. The timeout_set() function
initializes an event struct for use as a timeout. Once initialized, the event
must be activated by using timeout_add(). To cancel the timeout, call
timeout_del().
No comments:
Post a Comment