Alarm, sleep and High Resolution Timers

  • Post author:
  • Post last modified:November 15, 2024
  • Reading time:14 mins read

Alarm system call

The alarm system call arranges for the SIGALRM signal to be sent to the calling process after s seconds.

#include <unistd.h>

unsigned int alarm (unsigned int s);

Any previously set alarm is cleared. And if s is 0, no alarm is set. So, passing 0 as argument to alarm clears any previously set alarm. Alarm returns the time remaining in seconds of any previously set alarm, or 0 if there was no alarm set previously.

sleep, usleep, and nanosleep

With the sleep function, a process can sleep for s seconds. The process wakes up when s seconds are over or a signal, which is not ignored, is received. The returned value is the unslept seconds.

#include <unistd.h>

unsigned int sleep (unsigned int s);

sleep might have been implemented using SIGALRM. So sleep and SIGALRM should not be used in the same process. sleep can easily be implemented as a function using SIGALRM.

The usleep function is similar to sleep, except that the argument usec is in microseconds. Also usec must be less than 1000000. That is, the calling process can only sleep for less than a second.

#include <unistd.h>   

int usleep (useconds_t usec);

Finally, there is the nanosleep system call, which provides the high resolution sleep. nanosleep uses the struct timespec, which is,

struct timespec {    
    time_t tv_sec;   /* seconds */ 
    long   tv_nsec;    /* nanoseconds */   
};

and the call is,

#include <time.h>

int nanosleep (const struct timespec *req, struct timespec *rem);

With the nanosleep call, the calling thread goes to sleep as per the data pointed by req. The thread wakes up when the time is over or the sleep is interrupted by a signal which has not been ignored. The unslept time is returned in the timespec struct pointed by rem. In nanosleep, there is no restriction on the amount of time a thread goes on to sleep.

High Resolution Timers

There are two timing sub-systems in Linux, the Main Timing subsystem and the High Resolution Timing subsystem. In the Main Timing subsystem, the smallest time maintained is denoted by a jiffy, which is the duration of a tick of the system timer interrupt. The number of jiffies in a second, or the frequency of the system tick, is defined by the HZ constant. The value of HZ can be fixed as 100, 250 or 1000 at the time of kernel configuration prior to build, giving the the duration of a clock tick as 10, 4 or 1 millisecond(s) respectively. The times system call gives the time in different units of clock ticks and these number of clock ticks per second are given the system constant _SC_CLK_TCK. A typical value of _SC_CLK_TCK is 100, giving a clock tick of 10 milliseconds.

With kernel version 2.6.21 onwards, high resolution timers (HRT) are available under Linux. For this, the kernel has to be compiled with the configuration parameter CONFIG_HIGH_RES_TIMERS enabled. With high resolution timers, we can get timer resolution to 1 microsecond as compared to a few milliseconds without the high resolution timers. There are many ways to check whether high resolution timers are available,

  • In the /boot directory, check the kernel config file. It should have a line like CONFIG_HIGH_RES_TIMERS=y.
  • Check the contents of /proc/timer_list. For example, the .resolution entry showing 1 nanosecond and event_handler as hrtimer_interrupt in /proc/timer_list indicate that high resolution timers are available.
  • Get the clock resolution using the clock_getres system call.

There are two high resolution timer interfaces in Linux, the Interval Timer (itimer) interface and the POSIX timers interface.

Interval Timer (itimer) interface

There are three interval timers for a process, Once set, the timers decrement and when a timer becomes zero, a specific alarm is sent to the process. The three iterms are,

  • ITIMER_REAL – ITIMER_REAL decrements in real (calendar) time. When it becomes zero, the SIGALRM signal is sent to the process.
  • ITIMER_VIRTUAL – ITIMER_VIRTUAL decrements in the process’s virtual time. That is, it decrements when the process is executing in the user mode. When it becomes zero, the SIGVTALRM signal is sent to the process.
  • ITIMER_PROF – ITIMER_PROF decrements when the process is executing in the user mode and also when the system is running on behalf of the process. When it becomes zero, signal SIGPROF is sent to the process. ITIMER_PROF is for the use of profilers.

The structures used by itimers are,

struct timeval { 
    time_t tv_sec;       /* seconds */   
    suseconds_t    tv_usec;        /* microseconds */
}; 
struct itimerval {      
    struct timeval it_interval; /* next value */     
    struct timeval it_value;       /* current value */  
 };

and the system calls are,

int getitimer (int which, struct itimerval *curr_value);      
int setitimer (int which, const struct itimerval *new_value, 
               struct itimerval *old_value);

The timers decrement the it_value. When it_value, that is, both the members tv_sec and tv_usec, become zero, the relevant alarm is generated. After generating the alarm, the it_value is set to the timer reset value, it_interval. If both, it_value and it_interval are zero, the timer stops.

The first parameter, which, is one of the timers, ITIMER_REAL, ITIMER_VIRTUAL or ITIMER_PROF. The call gettimer gets the value of the timer in the parameter curr_value. Similarly, settimer sets the timer with the parameter new_value. If the timer was on, the current values of it_value and it_interval are copied in the parameter old_value, before the timer it_value and it_interval are copied from the parameter, new_value.

POSIX timers interface

There are high-resolution clocks,

  • CLOCK_REALTIME – This is a system wide real time clock, which can be set by the superuser.
  • CLOCK_MONOTONIC – This is also system wide clock. It can not be set and gives monotonic time since some unspecified starting point.
  • CLOCK_MONOTONIC_RAW – This clock is similar to CLOCK_MONOTONIC, it gives raw time that is not subject to NTP adjustments.
  • CLOCK_PROCESS_CPUTIME_ID – This is a per-process clock.
  • CLOCK_THREAD_CPUTIME_ID – This is a per-thread clock.

Of the above clocks, CLOCK_REALTIME is available in all implementations. Availability of other clocks is implementation dependent. The clock related system calls are,

#include <time.h>  
   
int clock_getres (clockid_t clk_id, struct timespec *res);  
                 
int clock_gettime (clockid_t clk_id, struct timespec *tp);  
   
int clock_settime (clockid_t clk_id, const struct timespec *tp);  

The parameters are clk_id and pointer to struct timespec. The clk_id is one one the above-mentioned clocks. The struct timespec has seconds and nanoseconds as members. clock_getres gives the resolution of the desired clock. clock_gettime and clock_settime are used to read and set the value of clock respectively. Of course, clocks like CLOCK_MONOTONIC can not be set and superuser privilege is required for setting the system wide CLOCK_REALTIME. An example program to find clock resolution is,

/* clocks.c: find clock resolution */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

main ()
{
    struct timespec res;  
 
    if (clock_getres (CLOCK_REALTIME, &res) == -1)
        perror ("clock_getres: CLOCK_REALTIME"); 
    else  
        printf ("CLOCK_REALTIME: %ld s, %ld ns\n", res.tv_sec, 
            res.tv_nsec);  
 
    if (clock_getres (CLOCK_MONOTONIC, &res) == -1) 
        perror ("clock_getres: CLOCK_MONOTONIC"); 
    else  
        printf ("CLOCK_MONOTONIC: %ld s, %ld ns\n", res.tv_sec,
            res.tv_nsec);
  
    if (clock_getres (CLOCK_MONOTONIC_RAW, &res) == -1) 
        perror ("clock_getres: CLOCK_MONOTONIC_RAW");  
    else 
        printf ("CLOCK_MONOTONIC_RAW: %ld s, %ld ns\n", res.tv_sec,
            res.tv_nsec);
  
    if (clock_getres (CLOCK_PROCESS_CPUTIME_ID, &res) == -1) 
        perror ("clock_getres: CLOCK_PROCESS_CPUTIME_ID"); 
    else 
        printf ("CLOCK_PROCESS_CPUTIME_ID: %ld s, %ld ns\n", res.tv_sec,
            res.tv_nsec);
  
    if (clock_getres (CLOCK_THREAD_CPUTIME_ID, &res) == -1)
        perror ("clock_getres: CLOCK_THREAD_CPUTIME_ID");
    else
        printf ("CLOCK_THREAD_CPUTIME_ID: %ld s, %ld ns\n", res.tv_sec,
            res.tv_nsec);
}

The above program needs to be linked with -lrt. In fact, programs using any of these POSIX high-resolution clocks and timers need to be compiled with -lrt. Compiling and running the program,

$ gcc clocks.c -o clocks -lrt   
$ ./clocks
CLOCK_REALTIME: 0 s, 1 ns
CLOCK_MONOTONIC: 0 s, 1 ns
CLOCK_MONOTONIC_RAW: 0 s, 1 ns      
CLOCK_PROCESS_CPUTIME_ID: 0 s, 1 ns
CLOCK_THREAD_CPUTIME_ID: 0 s, 1 ns

THe above output shows the resolution of all these clocks to be 1 nanosecond.

The structure used for the timer calls is,

struct itimerspec {   
    struct timespec it_interval; /* Timer interval */    
    struct timespec it_value;    /* Initial expiration */   
};

As in the case of itemers, the member it_value has the time for current timing operation whereas it_interval is the timing interval and, when the timer expires, it_value is set from it. The system call for creating a high resolution timer is,

#include <signal.h>   
#include <time.h>   
   
int timer_create (clockid_t clockid, struct sigevent *evp,   
                  timer_t *timerid);

The call timer_create is used to create the timer. The pointer to struct sigevent gives the option to specify the signal to be raised once the timer goes off. In case NULL is passed, SIGALRM is generated. The other timer related calls are,

#include <time.h>
    
int timer_delete (timer_t timerid);
    
int timer_settime (timer_t timerid, int flags,   
                   const struct itimerspec *new_value,
                   struct itimerspec *old_value);   
   
int timer_gettime (timer_t timerid, struct itimerspec *curr_value);   
   
int timer_getoverrun (timer_t timerid);

In the timer_settime call, if flag is 0, the timer expires after the time it_value in the struct itimerspec *new_value (secs and nanosecs). If the flag is set to TIMER_ABSTIME, the timer expires when the time in the relevant clock matches the time in the struct itimerspec *new_value. Of course, in both cases, it_interval is the reset value with which it_value is set once the timer goes off. Regarding timer_getoverrun, when the timer goes off, the relevant signal (like SIGALRM) is generated. Between the time the timer goes off and the time signal is delivered to the process, it is possible that the timer would have gone off a number of times. For these times, a signal is not generated. timer_getoverrun system call gives the number of times the timer has gone off since the last signal was generated and was being delivered to the process. An example program for high resolution timers is,

/*   hires_timer.c: 100 microsecond timer example program   */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>

void print_time (int);

timer_t timer1;

int main (int argc, char **argv)
{
    timer_t timer2;
    struct itimerspec new_value, old_value;
    struct sigaction action;
    struct sigevent sevent;
    sigset_t set;
    int signum;

    /* SIGALRM for printing time */
    memset (&action, 0, sizeof (struct sigaction));
    action.sa_handler = print_time;
    if (sigaction (SIGALRM, &action, NULL) == -1)
        perror ("sigaction");

    /* for program completion */
    memset (&sevent, 0, sizeof (struct sigevent));
    sevent.sigev_notify = SIGEV_SIGNAL;
    sevent.sigev_signo = SIGRTMIN;

    if (timer_create (CLOCK_MONOTONIC, NULL, &timer1) == -1)
        perror ("timer_create");


    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_nsec = 100000;  /* 100 us */
    new_value.it_value.tv_sec = 0;
    new_value.it_value.tv_nsec = 100000;     /* 100 us */
    if (timer_settime (timer1, 0, &new_value, &old_value) == -1)
        perror ("timer_settime");

    if (sigemptyset (&set) == -1)
        perror ("sigemptyset");

    if (sigaddset (&set, SIGRTMIN) == -1)
        perror ("sigaddset");

    if (sigprocmask (SIG_BLOCK, &set, NULL) == -1)
        perror ("sigprocmask");

    if (timer_create (CLOCK_MONOTONIC, &sevent, &timer2) == -1)
        perror ("timer_create");

    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_nsec = 0;  /* one time timer, no reset */
    new_value.it_value.tv_sec = 0;
    new_value.it_value.tv_nsec = 1000000; /* 1 ms */
    if (timer_settime (timer2, 0, &new_value, &old_value) == -1)
        perror ("timer_settime");

    /* wait for completion signal (1 ms)  */
    if (sigwait (&set, &signum) == -1)
        perror ("sigwait");

    exit (EXIT_SUCCESS);
}

void print_time (int signum)
{   
    struct timespec tp;
    char buffer [80];

    if (clock_gettime (CLOCK_MONOTONIC, &tp) == -1)
        perror ("clock_gettime");

    sprintf (buffer, "%ld s %ld ns overrun = %d\n", tp.tv_sec,
                      tp.tv_nsec, timer_getoverrun (timer1));
    write (STDOUT_FILENO, buffer, strlen (buffer));
}

In the above program, there is a timer named timer1, which goes off every 100 us. When timer1 goes off, it generates SIGALRM signal. The SIGALRM signal handling function prints the clock time and the timer overrun, if any. There is a second timer, timer2, which goes off after 1 ms and generates SIGRTMIN signal. We wait for this signal and terminate the process when it comes. The program generates an output like,

$ gcc hires_timer.c -o hires_timer -lrt    
$ ./hires_timer      
85769 s 129731553 ns overrun = 0      
85769 s 129885313 ns overrun = 1      
85769 s 130011713 ns overrun = 0      
85769 s 130108793 ns overrun = 0      
85769 s 130203153 ns overrun = 0      
85769 s 130305033 ns overrun = 0      
85769 s 130404753 ns overrun = 0      
85769 s 130505313 ns overrun = 0      
85769 s 130615513 ns overrun = 0      

High resolution sleep

The high-resolution sleep calls are,

int nanosleep (const struct timespec *req, struct timespec *rem);   
   
int clock_nanosleep (clockid_t clock_id, int flag,   
const struct timespec *request, struct timespec *remain);  

We discussed nanosleep earlier in this tutorial. clock_nanosleep gives the provision of specifying the clock to be used. Then, there is flag argument, similar to the flag argument in timer_settime. If flag is 0, it is a case of relative sleep. The sleep is for struct timespec *request seconds and nanoseconds, or till there is a signal interrupting the sleep. If flag is TIMER_ABSTIME, the sleep is till the time in the clock identified by clock_id matches the time in struct timespec *request or a signal comes along.

See also

  1. Linux process execution time
  2. Calendar Time Under Linux
  3. hwclock, the hardware clock query and set program
  4. Synchronize your computer’s clock using the NTP

Karunesh Johri

Software developer, working with C and Linux.
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments