Interval Timer Functions

The release of BSD4.2 UNIX introduced interval timers. This new facility provided the programmer the capability to create

  • A realtime timer

  • A virtual timer

  • A system virtual (profile) timer

These timers provided three different ways to measure time. The realtime timer measures elapsed time in the same way as the alarm(3) function. The virtual timer measures CPU time used while the process executes in user mode.

The system virtual timer, however, measures the time of execution for the current process in system and user modes. The system mode time measured is the execution time spent within the kernel on behalf of the current process. This timer is intended to assist interpreters in measuring the CPU profile of an interpreted program.

The new realtime timer provides additional advantages over the older alarm(3) function:

  • It allows microsecond resolution if the platform supports it.

  • It is capable of repeating.

The Interval Timer API

The new timer functionality came in the form of two functions: getitimer(2) and setitimer(2). These allow the caller to query and configure the timers, respectively.

#include <sys/time.h>
#define ITIMER_REAL      0           /* Realtime timer (SIGALRM) */
#define ITIMER_VIRTUAL   1           /* User time timer (SIGVTALRM) */
#define ITIMER_PROF      2           /* System + user time (SIGPROF) */

int getitimer(int which, struct itimerval *ovalue);

int setitimer(int which,             /* timer selection */
    const struct itimerval *value,   /* new timer settings */
    struct itimerval *ovalue);       /* old timer settings */

struct timeval {
        long    tv_sec;              /* seconds */
        long    tv_usec;             /* and microseconds */
};

struct itimerval {
        struct  timeval it_interval; /* timer interval */
        struct  timeval it_value;    /* current value */
};

The getitimer(2) and setitimer(2) functions require the programmer to choose one of the values ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF for the argument which. The ovalue argument receives a copy of the settings in the selected timer. In setitimer(2), where the timer settings are changed, the ovalue argument receives the former timer values. The new timer values are supplied by the value argument. Both functions return 0 when they succeed and -1 with an errno code if they fail.

Each timer generates a signal, as follows:

ITIMER_REAL SIGALRM
ITIMER_VIRTUAL SIGVTALRM
ITIMER_PROF SIGPROF

Note

Interval timers are not inherited by the child process after a fork(2) call. Interval timers do continue after an exec(2) call is made, however.


The itimerval structure has two members. The member it_value represents the time remaining until the next timer expiry. If this value is specified as 0, the timer is canceled. The member it_interval specifies the value to be loaded into the timer with the next timer expiry. If this value specifies zero time, then the timer is not reactivated.

The following specifies a timer that will expire once, 5.25 seconds after activation:

struct itimerval tmr;

tmr.it_value.tv_sec = 5;
tmr.it_value.tv_usec = 250000;
tmr.it_interval.tv_sec = 0;
tmr.it_interval.tv_usec = 0;

The next example shows how to define a timer that will expire after 3.75 seconds and repeat every 4.25 seconds thereafter:

struct itimerval tmr;

tmr.it_value.tv_sec = 3;
tmr.it_value.tv_usec = 750000;
tmr.it_interval.tv_sec = 4;
tmr.it_interval.tv_usec = 250000;

Interval Timer Macros

The manual page for setitimer(2) usually mentions three helpful macros.

  • timerclear(tvp) clears the timer value.

  • timerisset(tvp) indicates if the timer value is non-zero.

  • timercmp(tvp,uvp,cmp) comparestwo timer values.

The following example shows how timerclear() can be used to clear a time value:

struct itimerval tmr;

timerclear(&tmr);              /* Clear time value tmr */

The following tests to see if timer value tmr is zero:

if ( timerisset(&tmr) )
    /* tmr is non-zero */

The last example tests to see if variables tm1 and tm2 represent the same timer values:

struct itimerval tm1;
struct itimerval tm2;

if ( timercmp(&tm1,&tm2,=) )
    /* Values are equal */

The following tests to see if the variable tm1 represents less time than tm2:

struct itimerval tm1;
struct itimerval tm2;

if ( timercmp(&tm1,&tm2,<) )
    /* tm1 < tm2 */

Interval Timer Restrictions

Most UNIX platforms insist that the microsecond component of the interval time specification (tv_usec) not exceed one second (1,000,000 microseconds). Otherwise, an error will be reported.

A programmer that is striving for maximum UNIX platform portability should keep a few other things in mind when designing programs around interval timers. While setitimer(2) may be independent of the alarm(3) function call (UnixWare 7), you may not always be able to depend on this. Additionally, the sleep(3) function may be implemented in terms of alarm(3), or it may be implemented in terms of the ITIMER_REAL interval timer. HPUX 11 documents that "interaction between setitimer() and any of alarm(), sleep() or usleep() [functions] is unspecified."

The granularity of the timer will be very platform specific. While the specification permits the programmer to specify units of microseconds, your platform may round the time specifications to a less precise value. If your application is time critical, you may need to test your interval timer before relying on a particular level of precision.

Linux documents that generation and delivery of the timer signal are separate. This means that under severe conditions it is possible for realtime timer signals to be lost if they occur too soon to be handled. They are not stacked or counted.

Note

Some UNIX platforms document additional interval timers. For example, Solaris 8 documents the timer ITIMER_REALPROF, which delivers the same signal SIGPROF but has different semantics.


Note also that the ITIMER_PROF is capable of causing interrupted system calls, because the signal can be raised while executing in system mode. This means that your application must properly plan for the EINTR error code from system calls.

Creating One-Shot Timers

Listing 17.5 illustrates the use of the interval timer. This program establishes a simple one-shot realtime timer that raises the signal SIGALRM and then exits.

Code Listing 17.5. r1shot.c—A Simple One-Shot Realtime Timer Demonstration
1:   /* r1shot.c */
2:
3:   #include <stdio.h>
4:   #include <stdlib.h>
5:   #include <unistd.h>
6:   #include <signal.h>
7:   #include <errno.h>
8:   #include <sys/time.h>
9:
10:  static int count = 0;               /* Counter */
11:
12:  /*
13:   * Signal handler :
14:   */
15:  static void
16:  handler(int signo) {
17:      int e = errno;                  /* Save errno */
18:
19:      ++count;                        /* Increment count */
20:      write(1,"<<<SIGALRM>>>
",14);
21:      errno = e;                      /* Restore errno */
22:  }
23:
24:  /*
25:   * Main program :
26:   */
27:  int
28:  main(int argc,char **argv) {
29:      int z;                          /* Status return code */
30:      struct sigaction new_sigalrm;   /* New signal action */
31:      struct itimerval old_timer;     /* Old timer values */
32:      struct itimerval new_timer;     /* New timer values */
33:
34:      /*
35:       * Establish the signal action required for SIGALRM :
36:       */
37:      new_sigalrm.sa_handler = handler;
38:      sigemptyset(&new_sigalrm.sa_mask);
39:      new_sigalrm.sa_flags = 0;
40:      sigaction(SIGALRM,&new_sigalrm,NULL);
41:
42:      /*
43:       * Establish a one-shot realtime timer :
44:       */
45:      new_timer.it_interval.tv_sec = 0;
46:      new_timer.it_interval.tv_usec = 0;
47:      new_timer.it_value.tv_sec = 5;
48:      new_timer.it_value.tv_usec = 250000;    /* 5.25 seconds */
49:
50:      puts("Starting ITIMER_REAL...");
51:
52:      z = setitimer(ITIMER_REAL,&new_timer,&old_timer);
53:      if ( z ) {
54:          perror("setitimer(ITIMER_REAL)");
55:          return 1;
56:      }
57:
58:      /*
59:       * A loop :
60:       */
61:      do  {
62:          /* Do Work...*/ ;
63:      }  while ( count < 1 );
64:
65:      printf("ITIMER_REAL count is %d.
",count);
66:      return 0;
67:  }

The program in Listing 17.5 establishes a signal handler for the signal SIGALRM in lines 34–40. The function handler() is called when the signal is raised, and it simply increments variable count in line 19 and reports a message in line 20.

The one-shot timer is configured in lines 45–52. Notice that the it_interval member values are 0, causing the timer to not restart when the initial value expires.

Warning

The program in Listing 17.5 uses a CPU-intensive loop in lines 61–63. Out of courtesy to others, do not invoke this program often in a multiuser environment.


Compiling and running the program should yield the following:

$ make r1shot
cc -c  -Wall r1shot.c
cc -o r1shot r1shot.o
$ ./r1shot
Starting ITIMER_REAL...
<<<SIGALRM>>>
ITIMER_REAL count is 1.
$

You will see the message <<<SIGALRM>>> raised 5.25 seconds after the program starts. Then the program reports the final value of count and exits normally.

Establishing Repeating Timers

The program shown in Listing 17.6 is more interesting. It starts realtime, virtual, and profile timers all at once.

Code Listing 17.6. timers.c—A Program That Uses Realtime, Virtual, and Profile Timers
1:   /* timers.c */
2:
3:   #include <stdio.h>
4:   #include <stdlib.h>
5:   #include <unistd.h>
6:   #include <signal.h>
7:   #include <errno.h>
8:   #include <sys/time.h>
9:
10:  static int count = 0;               /* Counter */
11:
12:  /*
13:   * Signal handler :
14:   */
15:  static void
16:  handler(int signo) {
17:      int e = errno;                  /* Save errno */
18:      char *signame = "?";            /* Signal name */
19:
20:      switch( signo ) {
21:      case SIGALRM :                  /* Realtime timer expired */
22:          ++count;                    /* Increment counter */
23:          signame = "<<<SIGALRM>>>
";
24:          break;
25:      case SIGVTALRM :                /* Virtual timer expired */
26:          signame = "<<<SIGVTALRM>>>
";
27:          break;
28:      case SIGPROF :                  /* System virtual timer expired */
29:          signame = "<<<SIGPROF>>>
";
30:          break;
31:      }
32:
33:      write(1,signame,strlen(signame));
34:      errno = e;                      /* Restore errno */
35:  }
36:
37:  /*
38:   * Main program :
39:   */
40:  int
41:  main(int argc,char **argv) {
42:      int z;                          /* Status return code */
43:      struct sigaction new_sigalrm;   /* New signal action */
44:      struct itimerval real_timer;    /* Real timer values */
45:      struct itimerval virt_timer;    /* User mode timer */
46:      struct itimerval prof_timer;    /* System+User mode timer */
47:      struct itimerval timer_values;  /* Timer values */
48:
49:      /*
50:       * Establish the signal action required for SIGALRM :
51:       */
52:      new_sigalrm.sa_handler = handler;
53:      sigemptyset(&new_sigalrm.sa_mask);
54:      new_sigalrm.sa_flags = 0;
55:
56:      sigaction(SIGALRM,&new_sigalrm,NULL);
57:      sigaction(SIGVTALRM,&new_sigalrm,NULL);
58:      sigaction(SIGPROF,&new_sigalrm,NULL);
59:
60:      /*
61:       * Establish a realtime timer :
62:       */
63:      real_timer.it_interval.tv_sec = 3;
64:      real_timer.it_interval.tv_usec = 500000; /* 3.5 seconds */
65:      real_timer.it_value.tv_sec = 3;
66:      real_timer.it_value.tv_usec = 500000;
67:
68:      virt_timer.it_interval.tv_sec = 0;
69:      virt_timer.it_interval.tv_usec = 500000; /* 0.5 seconds */
70:      virt_timer.it_value.tv_sec = 0;
71:      virt_timer.it_value.tv_usec = 500000;
72:
73:      prof_timer.it_interval.tv_sec = 0;
74:      prof_timer.it_interval.tv_usec = 500000; /* 0.5 seconds */
75:      prof_timer.it_value.tv_sec = 0;
76:      prof_timer.it_value.tv_usec = 500000;
77:
78:      puts("Starting ITIMER_REAL...");
79:      z = setitimer(ITIMER_REAL,&real_timer,NULL);
80:      if ( z ) {
81:          perror("setitimer(ITIMER_REAL)");
82:          return 1;
83:      }
84:
85:      puts("Starting ITIMER_VIRTUAL...");
86:      z = setitimer(ITIMER_VIRTUAL,&virt_timer,NULL);
87:      if ( z ) {
88:          perror("setitimer(ITIMER_VIRTUAL)");
89:          return 1;
90:      }
91:
92:      puts("Starting ITIMER_PROF...");
93:      z = setitimer(ITIMER_PROF,&prof_timer,NULL);
94:      if ( z ) {
95:          perror("setitimer(ITIMER_PROF)");
96:          return 1;
97:      }
98:
99:      /*
100:      * A loop :
101:      */
102:     do  {
103:         /* Perform work which involves system time */
104:         getitimer(ITIMER_PROF,&timer_values);
105:         (void) timer_values;
106:     } while ( count < 2 );
107:
108:     printf("ITIMER_REAL count is %d.
",count);
109:     return 0;
110: }

Lines 52–58 establish a single handler function handler() to process signals SIGALRM, SIGVTALRM, and SIGPROF. The counter variable count is incremented only when handler() receives the signal SIGALRM (see line 22).

The three timers are configured in lines 63–76. The timers themselves are started in lines 78–97.

After the program begins executing, the do { } while loop in lines 102–106 repeatedly calls on getitimer(2) to read the current timer values for ITIMER_PROF. This is performed so that much of the CPU time expended in this demonstration will be in system mode.

Note

The program in Listing 17.6 is very CPU intensive. To be courteous to other users of the same system, do not run this program frequently.


Compiling and running this program on a FreeBSD system yielded the following:

$ make timers
cc -c  -Wall timers.c
cc -o timers timers.o
$ ./timers
Starting ITIMER_REAL...
Starting ITIMER_VIRTUAL...
Starting ITIMER_PROF...
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGVTALRM>>>
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGALRM>>>
<<<SIGVTALRM>>>
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGVTALRM>>>
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGPROF>>>
<<<SIGALRM>>>
ITIMER_REAL count is 2.
$

You can see the names of the different signals that were raised when the timers expired. Note how the timers kept working in this example.

Note also that the signal SIGPROF occurs more frequently than the signal SIGVTALRM. This should tell you that more CPU time was being spent in system mode in the do { } while loop than in user mode.

The SIGALRM signal occurred twice because the while clause exits after the counter count reaches 2. Since the realtime timer was configured to expire at 3.5 seconds, the entire output represents approximately 7 seconds of time.

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

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