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:
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;
The manual page for setitimer(2) usually mentions three helpful macros.
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 */
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.
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.
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.
The program shown in Listing 17.6 is more interesting. It starts realtime, virtual, and profile timers all at once.
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.
3.129.194.106