Operating systems use timers for purposes such as process scheduling, timeouts for network protocols, and periodic updates of system statistics. Applications access system time and timer functions to measure performance or to identify the time when events occur. Applications also use timers to implement protocols and to control interaction with users such as that needed for rate-limited presentations. This chapter discusses representations of time in the POSIX base standard as well as interval timers in the POSIX:XSI Extension and POSIX:TMR Extension. The chapter also explores concepts such as timer drift and timer overrun and demonstrates how to use POSIX realtime signals with timers.
POSIX specifies that systems should keep time in terms of seconds since the Epoch and that each day be accounted for by exactly 86,400 seconds. The Epoch is defined as 00:00 (midnight), January 1, 1970, Coordinated Universal Time (also called UTC, Greenwich Mean Time or GMT). POSIX does not specify how an implementation should align its system time with the actual time and date.
Most operations need to be measured with timers with greater than one-second resolution. Two POSIX extensions, the POSIX:XSI Extension and the POSIX:TMR Extension, define time resolutions of microseconds and nanoseconds, respectively.
The POSIX base standard supports only a time resolution of seconds and expresses time since the Epoch using a time_t
type, which is usually implemented as a long
. A program can access the system time (expressed in seconds since the Epoch) by calling the time
function. If tloc
is not NULL
, the time
function also stores the time in *tloc
.
SYNOPSIS #include <time.h> time_t time(time_t *tloc); POSIX:CX
If successful, time
returns the number of seconds since the Epoch. If unsuccessful, time
returns (time_t)
–1. POSIX does not define any mandatory errors for time
.
Example 9.1.
The time_t
type is usually implemented as a long
. If a long
is 32 bits, at approximately what date would time_t
overflow? (Remember that one bit is used for the sign.) What date would cause an overflow if an unsigned long
were used? What date would cause an overflow if a 64-bit data type were used?
Answer:
For a 32-bit long
, time would overflow in approximately 68 years from January 1, 1970, so the system would not have a “Y2K” problem until the year 2038. For a time_t
value that is an unsigned long
, the overflow would occur in the year 2106, but this would not allow time
to return an error. For a 64-bit data type, the overflow would not occur for another 292 billion years, long after the sun has died!
The difftime
function computes the difference between two calendar times of type time_t
, making it convenient for calculations involving time. The difftime
function has two time_t
parameters and returns a double
containing the first parameter minus the second.
SYNOPSIS #include <time.h> double difftime(time_t time1, time_t time0); POSIX:CX
No errors are defined for difftime
.
Example 9.2. simpletiming.c
The following program calculates the wall-clock time that it takes to execute function_to_time
.
# include <stdio.h> # include <time.h> void function_to_time(void); int main(void) { time_t tstart; tstart = time(NULL); function_to_time(); printf("function_to_time took %f seconds of elapsed time ", difftime(time(NULL), tstart)); return 0; }
Example 9.2 uses a time resolution of one second, which may not be accurate enough unless function_to_time
involves substantial computation or waiting. Also, the time
function measures wall-clock or elapsed time, which may not meaningfully reflect the amount of CPU time used. Section 9.1.5 presents alternative methods of timing code.
The time_t
type is convenient for calculations requiring the difference between times, but it is cumbersome for printing dates. Also, a program should adjust dates and times to account for factors such as time zone, daylight-saving time and leap seconds.
The localtime
function takes a parameter specifying the seconds since the Epoch and returns a structure with the components of the time (such as day, month and year) adjusted for local requirements. The asctime
function converts the structure returned by localtime
to a string. The ctime
function is equivalent to asctime(localtime(clock))
. The gmtime
function takes a parameter representing seconds since the Epoch and returns a structure with the components of time expressed as Coordinated Universal Time (UTC).
SYNOPSIS #include <time.h> char *asctime(const struct tm *timeptr); char *ctime(const time_t *clock); struct tm *gmtime(const time_t *timer); struct tm *localtime(const time_t *timer); POSIX:CX
No errors are defined for these functions.
The ctime
function takes one parameter, a pointer to a variable of type time_t
, and returns a pointer to a 26-character English-language string. The ctime
function takes into account both the time zone and daylight saving time. Each of the fields in the string has a constant width. The string might be stored as follows.
Sun Oct 06 02:21:35 1986
Example 9.3. timeprint.c
The following program prints the date and time. The printf
format did not include '
'
because ctime
returns a string that ends in a newline.
#include <stdio.h> #include <time.h> int main(void) { time_t tcurrent; tcurrent = time(NULL); printf("The current time is %s", ctime(&tcurrent)); return 0; }
Example 9.4. badtiming.c
What is wrong with the following program that prints the time before and after the function function_to_time
executes?
#include <stdio.h> #include <time.h> void function_to_time(void); int main(void) { time_t tend, tstart; tstart = time(NULL); function_to_time(); tend = time(NULL); printf("The time before was %sThe time after was %s", ctime(&tstart), ctime(&tend)); return 0; }
Answer:
The ctime
function uses static storage to hold the time string. Both calls to ctime
store the string in the same place, so the second call may overwrite the first value before it is used. Most likely, both times will be printed as the same value.
The gmtime
and localtime
functions break the time into separate fields to make it easy for programs to output components of the date or time. ISO C defines the struct tm
structure to have the following members.
int tm_sec; /* seconds after the minute [0,60] */ int tm_min; /* minutes after the hour [0,59] */ int tm_hour; /* hours since midnight [0,23] */ int tm_mday; /* day of the month [1,31] */ int tm_mon; /* months since January [0,11] */ int tm_year; /* years since 1900 */ int tm_wday; /* days since Sunday [0,6] */ int tm_yday; /* days since January 1 [0,365] */ int tm_isdst; /* flag indicating daylight-saving time */
Example 9.5.
The following code segment prints the number of days since the beginning of the year.
struct tm *tcurrent; tcurrent = localtime(time(NULL)); printf("%d days have elapsed since Jan 1 ", tcurrent->tm_yday);
Unfortunately, the asctime
, ctime
and localtime
are not thread-safe. The POSIX:TSF Thread Safe Extension specifies thread-safe alternatives that have a caller-supplied buffer as an additional parameter.
SYNOPSIS #include <time.h> char *asctime_r(const struct tm *restrict timeptr, char *restrict buf); char *ctime_r(const time_t *clock, char *buf); struct tm *gmtime_r(const time_t *restrict timer, struct tm *restrict result); struct tm *localtime_r(const time_t *restrict timer, struct tm *restrict result); POSIX:TSF
If successful, these functions return a pointer to the parameter holding the result. For asctime_r
and ctime_r
, the result is in buf
. For gmtime_r
and localtime_r
, the result is in result
. If unsuccessful, these functions return a NULL
pointer.
A time scale of seconds is too coarse for timing programs or controlling program events. The POSIX:XSI Extension uses the struct timeval
structure to express time on a finer scale. The struct timeval
structure includes the following members.
time_t tv_sec; /* seconds since the Epoch */ time_t tv_usec; /* and microseconds */
Certain POSIX functions that support a timeout option (e.g., select
) specify the timeout values by using variables of type struct timeval
. In this case, the structure holds the length of the interval in seconds and microseconds.
The gettimeofday
function retrieves the system time in seconds and microseconds since the Epoch. The struct timeval
structure pointed to by tp
receives the retrieved time. The tzp
pointer must be NULL
and is included for historical reasons.
SYNOPSIS #include <sys/time.h> int gettimeofday(struct timeval *restrict tp, void *restrict tzp); POSIX:XSI
The gettimeofday
function returns 0. No values are reserved to indicate an error. However, many systems have implemented gettimeofday
so that it returns –1 and sets errno
if unsuccessful. Our programs check to make sure gettimeofday
returns 0.
Program 9.1 shows how to measure the running time of function_to_time
by using gettimeofday
. The gettimeofdaytiming
program reads the time before and after calling function_to_time
and prints the time difference as a number of microseconds.
Example 9.7.
What is the maximum duration that can be timed by the method of Program 9.1? How could you extend this?
Answer:
If a long
is 32 bits, the maximum duration is 231 – 1 microseconds, or approximately 35 minutes. You could extend this by using a long long
(usually 64 bits) for timedif
. Changes must be made in the declaration of timedif
, the definition of MILLION
(1000000LL
) and the format specifier (lld
).
Example 9.1. gettimeofdaytiming.c
A program that measures the running time of a function by using gettimeofday
.
#include <stdio.h> #include <sys/time.h> #define MILLION 1000000L void function_to_time(void); int main(void) { long timedif; struct timeval tpend; struct timeval tpstart; if (gettimeofday(&tpstart, NULL)) { fprintf(stderr, "Failed to get start time "); return 1; } function_to_time(); /* timed code goes here */ if (gettimeofday(&tpend, NULL)) { fprintf(stderr, "Failed to get end time "); return 1; } timedif = MILLION*(tpend.tv_sec - tpstart.tv_sec) + tpend.tv_usec - tpstart.tv_usec; printf("The function_to_time took %ld microseconds ", timedif); return 0; }
The gettimeofdaytest
program shown in Program 9.2 tests gettimeofday
resolution by calling gettimeofday
in a loop until it produces 20 differences. Program 9.2 displays the differences along with the average difference and the number of calls made to gettimeofday
. On most systems, the resolution will be a small number of microseconds. If the number of calls to gettimeofday
is not much more than 21, then the limiting factor on the resolution is the time it takes to execute gettimeofday
. On most modern systems, many consecutive calls to gettimeofday
will return the same value. Often, one of the values displayed will be much greater than the others. This can happen if a context switch occurs while the timing loop is executing.
A clock is a counter that increments at fixed intervals called the clock resolution. The POSIX:TMR Timers Extension contains clocks that are represented by variables of type clockid_t
. POSIX clocks may be systemwide or only visible within a process. All implementations must support a systemwide clock with a clockid_t
value of CLOCK_REALTIME
corresponding to the system realtime clock. Only privileged users may set this clock, but any user can read it.
Example 9.2. gettimeofdaytest.c
A program to test the resolution of gettimeofday
.
#include <stdio.h> #include <sys/time.h> #define MILLION 1000000L #define NUMDIF 20 int main(void) { int i; int numcalls = 1; int numdone = 0; long sum = 0; long timedif[NUMDIF]; struct timeval tlast; struct timeval tthis; if (gettimeofday(&tlast, NULL)) { fprintf(stderr, "Failed to get first gettimeofday. "); return 1; } while (numdone < NUMDIF) { numcalls++; if (gettimeofday(&tthis, NULL)) { fprintf(stderr, "Failed to get a later gettimeofday. "); return 1; } timedif[numdone] = MILLION*(tthis.tv_sec - tlast.tv_sec) + tthis.tv_usec - tlast.tv_usec; if (timedif[numdone] != 0) { numdone++; tlast = tthis; } } printf("Found %d differences in gettimeofday: ", NUMDIF); printf("%d calls to gettimeofday were required ", numcalls); for (i = 0; i < NUMDIF; i++) { printf("%2d: %10ld microseconds ", i, timedif[i]); sum += timedif[i]; } printf("The average nonzero difference is %f ", sum/(double)NUMDIF); return 0; }
The struct timespec
structure specifies time for both POSIX:TMR clocks and timers, as well as the timeout values for the POSIX thread functions that support timeouts. The struct timespec
structure has at least the following members.
time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */
POSIX provides functions to set the clock time (clock_settime)
, to retrieve the clock time (clock_gettime
), and to determine the clock resolution (clock_getres
). Each of these functions takes two parameters: a clockid_t
used to identify the particular clock and a pointer to a struct timespec
structure.
SYNOPSIS #include <time.h> int clock_getres(clockid_t clock_id, struct timespec *res); int clock_gettime(clockid_t clock_id, struct timespec *tp); int clock_settime(clockid_t clock_id, const struct timespec *tp); POSIX:TMR
If successful, these functions return 0. If unsuccessful, these functions return –1 and set errno
. All three functions set errno
to EINVAL
if clockid_t
does not specify a known clock. The clock_settime
also sets errno
to EINVAL
if tp
is out of the range of clock_id
or if tp->tv_nsec
is not in the range [0, 109).
Example 9.8. clockrealtimetiming.c
The following program measures the running time of function_to_time
by using the POSIX:TMR clocks.
#include <stdio.h> #include <time.h> #define MILLION 1000000L void function_to_time(void); int main (void) { long timedif; struct timespec tpend, tpstart; if (clock_gettime(CLOCK_REALTIME, &tpstart) == -1) { perror("Failed to get starting time"); return 1; } function_to_time(); /* timed code goes here */ if (clock_gettime(CLOCK_REALTIME, &tpend) == -1) { perror("Failed to get ending time"); return 1; } timedif = MILLION*(tpend.tv_sec - tpstart.tv_sec) + (tpend.tv_nsec - tpstart.tv_nsec)/1000; printf("The function_to_time took %ld microseconds ", timedif); return 0; }
The CLOCK_REALTIME
typically has a higher resolution than gettimeofday
. Program 9.3 which is similar to Program 9.2 tests the resolution of CLOCK_REALTIME
by measuring the average of 20 changes in the clock reading. The program also calls clock_getres
to display the nominal resolution in nanoseconds for setting the clock and for timer interrupts (Section 9.5). This nominal resolution is typically large, on the order of milliseconds, and is unrelated to the resolution of clock_gettime
for timing. The resolution of clock_gettime
is typically better than one microsecond.
Example 9.3. clockrealtimetest.c
A program to test the resolution of CLOCK_REALTIME
.
#include <stdio.h> #include <time.h> #define BILLION 1000000000L #define NUMDIF 20 int main(void) { int i; int numcalls = 1; int numdone = 0; long sum = 0; long timedif[NUMDIF]; struct timespec tlast; struct timespec tthis; if (clock_getres(CLOCK_REALTIME, &tlast)) perror("Failed to get clock resolution"); else if (tlast.tv_sec != 0) printf("Clock resolution no better than one second "); else printf("Clock resolution: %ld nanoseconds ", (long)tlast.tv_nsec); if (clock_gettime(CLOCK_REALTIME, &tlast)) { perror("Failed to get first time"); return 1; } while (numdone < NUMDIF) { numcalls++; if (clock_gettime(CLOCK_REALTIME, &tthis)) { perror("Failed to get a later time"); return 1; } timedif[numdone] = BILLION*(tthis.tv_sec - tlast.tv_sec) + tthis.tv_nsec - tlast.tv_nsec; if (timedif[numdone] != 0) { numdone++; tlast = tthis; } } printf("Found %d differences in CLOCK_REALTIME: ", NUMDIF); printf("%d calls to CLOCK_REALTIME were required ", numcalls); for (i = 0; i < NUMDIF; i++) { printf("%2d: %10ld nanoseconds ", i, timedif[i]); sum += timedif[i]; } printf("The average nonzero difference is %f ", sum/(double)NUMDIF); return 0; }
The time
function measures real time, sometimes called elapsed time or wall-clock time. In a multiprogramming environment many processes share the CPU, so real time is not an accurate measure of execution time. The virtual time for a process is the amount of time that the process spends in the running state. Execution times are usually expressed in terms of virtual time rather than wall-clock time.
The times
function fills the struct tms
structure pointed to by its buffer
parameter with time-accounting information.
SYNOPSIS #include <sys/times.h> clock_t times(struct tms *buffer); POSIX
If successful, times
returns the elapsed real time, in clock ticks, since an arbitrary point in the past such as system or process startup time. The return value may overflow its possible range. If times
fails, it returns (clock_t)
–1 and sets errno
.
The struct tms
structure contains at least the following members.
clock_t tms_utime; /* user CPU time of process */ clock_t tms_stime; /* system CPU time on behalf of process */ clock_t tms_cutime /* user CPU time of process and terminated children */ clock_t tms_cstime; /* system CPU time of process and terminated children */
Program 9.4 estimates the total of the amount of CPU time used by function_to_time
as well as the fraction of the total CPU time used. It displays the total time in units of seconds expressed as a double
. The resolution of the calculation is in clock ticks. A typical value for the number of ticks per second is 100. This number is suitable for accounting but does not have enough resolution for performance measurements of short events. If function_to_time
takes only a few clock ticks to execute, you can obtain better resolution by calling it in a loop several times and dividing the resulting time by the number of iterations of the loop.
Program 9.4 calls sysconf
as introduced in showtimes
(Program 2.10 on page 53) to determine the number of clock ticks in a second. The calculation does not include any CPU time used by children of the process, but it does include both the user time and the system time used on behalf of the process. The fraction of the total CPU time may be inaccurate if a context switch occurs during the execution of the function.
Program 9.5, which is similar to the time
shell command, prints the number of clock ticks and seconds used to execute an arbitrary program. The timechild
function passes its own command-line argument array to execv
in the same way as does Program 3.5 on page 81 and calculates the child’s time by subtracting the process time from the total time. Since the process has only one child, what is left is the child’s time.
Example 9.4. cpufraction.c
A program that calculates the CPU time in seconds for function_to_time
and its fraction of total.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/times.h> void function_to_time(void); int main(void) { double clockticks, cticks; clock_t tcend, tcstart; struct tms tmend, tmstart; if ((clockticks = (double) sysconf(_SC_CLK_TCK)) == -1) { perror("Failed to determine clock ticks per second"); return 1; } printf("The number of ticks per second is %f ", clockticks); if (clockticks == 0) { fprintf(stderr, "The number of ticks per second is invalid "); return 1; } if ((tcstart = times(&tmstart)) == -1) { perror("Failed to get start time"); return 1; } function_to_time(); if ((tcend = times(&tmend)) == -1) { perror("Failed to get end times"); return 1; } cticks = tmend.tms_utime + tmend.tms_stime - tmstart.tms_utime - tmstart.tms_stime; printf("Total CPU time for operation is %f seconds ",cticks/clockticks); if ((tcend <= tcstart) || (tcend < 0) || (tcstart < 0)) { fprintf(stderr, "Tick time wrapped, couldn't calculate fraction ); return 1; } printf("Fraction of CPU time used is %f ", cticks/(tcend - tcstart)); return 0; }
Example 9.9.
The following command line uses timechild
of Program 9.5 to time the execution of Program 9.4.
timechild cpufraction
Example 9.5. timechild.c
A program that executes its command-line argument array as a child process and returns the amount of time taken to execute the child.
#include <errno.h> #include <stdio.h> #include <unistd.h> #include <sys/times.h> #include <sys/types.h> #include <sys/wait.h> #include "restart.h" int main(int argc, char *argv[]) { pid_t child; double clockticks; double cticks; struct tms tmend; if (argc < 2){ /* check for valid number of command-line arguments */ fprintf (stderr, "Usage: %s command ", argv[0]); return 1; } if ((child = fork()) == -1) { perror("Failed to fork"); return 1; } if (child == 0) { /* child code */ execvp(argv[1], &argv[1]); perror("Child failed to execvp the command"); return 1; } if (r_wait(NULL) == -1) { /* parent code */ perror("Failed to wait for child"); return 1; } if (times(&tmend) == (clock_t)-1) { perror("Failed to get end time"); return 1; } if ((clockticks = (double) sysconf(_SC_CLK_TCK)) == -1) { perror("Failed to determine clock ticks per second"); return 1; } if (clockticks == 0) { fprintf(stderr, "Invalid number of ticks per second "); return 1; } cticks = tmend.tms_cutime + tmend.tms_cstime - tmend.tms_utime - tmend.tms_stime; printf("%s used %ld clock ticks or %f seconds ", argv[1], (long)cticks, cticks/clockticks); return 0; }
A process that voluntarily blocks for a specified time is said to sleep. The sleep
function causes the calling thread to be suspended either until the specified number of seconds has elapsed or until the calling thread catches a signal.
SYNOPSIS #include <unistd.h> #unsigned sleep(unsigned seconds); POSIX
The sleep
function returns 0 if the requested time has elapsed or the amount of unslept time if interrupted. The sleep
function interacts with SIGALRM
, so avoid using them concurrently in the same process.
Example 9.10. beeper.c
The following program beeps every n
seconds, where n
is passed as a command-line argument.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { int sleeptime; if (argc != 2) { fprintf(stderr, ";Usage:%s n ", argv[0]); return 1; } sleeptime = atoi(argv[1]); fprintf(stderr, "Sleep time is %d ", sleeptime); for ( ; ; ) { sleep(sleeptime); printf(" 07"); fflush(stdout); } }
The nanosleep
function causes the calling thread to suspend execution until the time interval specified by rqtp
has elapsed or until the thread receives a signal. If nanosleep
is interrupted by a signal and rmtp
is not NULL
, the location pointed to by rmtp
contains the time remaining, allowing nanosleep
to be restarted. The system clock CLOCK_REALTIME
determines the resolution of rqtp
.
SYNOPSIS #include <time.h> int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); POSIX:TMR
If successful, nanosleep
returns 0. If unsuccessful, nanosleep
returns –1 and sets errno
. The following table lists the mandatory errors for nanosleep
.
| cause |
---|---|
|
|
|
|
The data structures used by nanosleep
allow for nanosecond resolution, but the resolution of CLOCK_REALTIME
is typically much larger, on the order of 10 ms. The nanosleep
function is meant to replace usleep
, which is now considered obsolete. The main advantage of nanosleep
over usleep
is that nanosleep
, unlike sleep
or usleep
, does not affect the use of any signals, including SIGALRM
.
Program 9.6 tests the resolution of the nanosleep
function. It executes 100 calls to nanosleep
with a sleep time of 1000 nanoseconds. If nanosleep
had a true resolution of 1 ns, this would complete in 100 μsec. The program takes about one second to complete on a system with a 10 ms resolution.
A timer generates a notification after a specified amount of time has elapsed. In contrast to a clock, which increments to track the passage of time, a timer usually decrements its value and generates a signal when the value becomes zero. A computer system typically has a small number of hardware interval timers, and the operating system implements multiple software timers by using these hardware timers.
Operating systems use interval timers in many ways. An interval timer can cause a periodic interrupt, triggering the operating system to increment a counter. This counter can keep the time since the operating system was booted. UNIX systems traditionally keep the time of day as the number of seconds since January 1, 1970. If an underlying interval timer generates an interrupt after 100 microseconds and is restarted each time it expires, the timer interrupt service routine can keep a local counter to measure the number of seconds since January 1, 1970, by incrementing this local counter after each 10,000 expirations of the interval timer.
Example 9.6. nanotest.c
A function that tests the resolution of nanosleep
.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/time.h> #define COUNT 100 #define D_BILLION 1000000000.0 #define D_MILLION 1000000.0 #define MILLION 1000000L #define NANOSECONDS 1000 int main(void) { int i; struct timespec slptm; long tdif; struct timeval tend, tstart; slptm.tv_sec = 0; slptm.tv_nsec = NANOSECONDS; if (gettimeofday(&tstart, NULL) == -1) { fprintf(stderr, "Failed to get start time "); return 1; } for (i = 0; i < COUNT; i++) if (nanosleep(&slptm, NULL) == -1) { perror("Failed to nanosleep"); return 1; } if (gettimeofday(&tend, NULL) == -1) { fprintf(stderr,"Failed to get end time "); return 1; } tdif = MILLION*(tend.tv_sec - tstart.tv_sec) + tend.tv_usec - tstart.tv_usec; printf("%d nanosleeps of %d nanoseconds ", COUNT, NANOSECONDS); printf("Should take %11d microseconds or %f seconds ", NANOSECONDS*COUNT/1000, NANOSECONDS*COUNT/D_BILLION); printf("Actually took %11ld microseconds or %f seconds ", tdif, tdif/D_MILLION); printf("Number of seconds per nanosleep was %f ", (tdif/(double)COUNT)/MILLION); printf("Number of seconds per nanosleep should be %f , NANOSECONDS/D_BILLION); return 0; }
Time-sharing operating systems can also use interval timers for process scheduling. When the operating system schedules a process, it starts an interval timer for a time interval called the scheduling quantum. If this timer expires and the process is still executing, the scheduler moves the process to a ready queue so that another process can execute. Multiprocessor systems need one of these interval timers for each processor.
Most scheduling algorithms have a mechanism for raising the priority of processes that have been waiting a long time to execute. The scheduler might use an interval timer for priority management. Every time the timer expires, the scheduler raises the priority of the processes that have not executed.
The interval timers of the POSIX:XSI Extension use a struct itimerval
structure that contains the following members.
struct timeval it_value; /* time until next expiration */ struct timeval it_interval; /* value to reload into the timer */
Here it_value
holds the time remaining before the timer expires, and it_interval
holds the time interval to be used for resetting the timer after it expires. Recall that a struct timeval
structure has fields for seconds and microseconds.
A conforming POSIX:XSI implementation must provide each process with the following three user interval timers.
| decrements in real time and generates a |
| decrements in virtual time (time used by the process) and generates a |
| decrements in virtual time and system time for the process and generates a |
POSIX provides the getitimer
function for retrieving the current time interval and the setitimer
function for starting and stopping a user interval timer. The which
parameter specifies the timer (i.e., ITIMER_REAL
, ITIMER_VIRTUAL
or ITIMER_PROF)
. The getitimer
function stores the current value of the time for timer which
in the location pointed to by value
. The setitimer
function sets the timer specified by which
to the value pointed to by value
. If ovalue
is not NULL
, setitimer
places the previous value of the timer in the location pointed to by ovalue
. If the timer was running, the it_value
member of *ovalue
is nonzero and contains the time remaining before the timer would have expired.
SYNOPSIS #include <sys/time.h> int getitimer(int which, struct itimerval *value); int setitimer(int which, const struct itimerval *restrict value, struct itimerval *restrict ovalue); POSIX:XSI
If successful, these functions return 0. If unsuccessful, they return –1 and set errno
. The setitimer
function sets errno
to EINVAL
if the number of microseconds in value
is not in the range [0, 106).
If the it_interval
member of *value
is not 0, the timer restarts with this value when it expires. If the it_interval
of *value
is 0, the timer does not restart after it expires. If the it_value
of *value
is 0, setitimer
stops the timer if it is running.
Program 9.7 uses an ITIMER_PROF
timer to print out an asterisk for each two seconds of CPU time used. The program first calls setupinterrupt
to install myhandler
as the signal handler for SIGPROF
. Then, the program calls setupitimer
to set up a periodic timer, using ITIMER_PROF
, that expires every 2 seconds. The ITIMER_PROF
timer generates a SIGPROF
signal after every two seconds of CPU time used by the process. The process catches the SIGPROF
signal and handles it with myhandler
. This handler function outputs an asterisk to standard error.
Example 9.7. periodicasterisk.c
A program that prints an asterisk for each two seconds of CPU time used.
#include <errno.h> #include <signal.h> #include <stdio.h> #include <unistd.h> #include <sys/time.h> /* ARGSUSED */ static void myhandler(int s) { char aster = '*'; int errsave; errsave = errno; write(STDERR_FILENO, &aster, 1); errno = errsave; } static int setupinterrupt(void) { /* set up myhandler for SIGPROF */ struct sigaction act; act.sa_handler = myhandler; act.sa_flags = 0; return (sigemptyset(&act.sa_mask) || sigaction(SIGPROF, &act, NULL)); } static int setupitimer(void) { /* set ITIMER_PROF for 2-second intervals */ struct itimerval value; value.it_interval.tv_sec = 2; value.it_interval.tv_usec = 0; value.it_value = value.it_interval; return (setitimer(ITIMER_PROF, &value, NULL)); } int main(void) { if (setupinterrupt() == -1) { perror("Failed to set up handler for SIGPROF"); return 1; } if (setupitimer() == -1) { perror("Failed to set up the ITIMER_PROF interval timer"); return 1; } for ( ; ; ); /* execute rest of main program here */ }
Example 9.11.
Write a program that sets ITIMER_REAL
to expire in two seconds and then sleeps for ten seconds. How long does it take for the program to terminate? Why?
Answer:
POSIX states that the interaction between setitimer
and any of alarm
, sleep
or usleep
is unspecified, so we can’t predict how long it will take. Avoid this combination in your programs by using nanosleep
instead of sleep
.
Example 9.12.
What is wrong with the following code, which should print out the number of seconds remaining on the ITIMER_VIRTUAL
interval timer?
struct itimerval *value; getitimer(ITIMER_VIRTUAL, value); fprintf(stderr, "Time left is %ld seconds ", value->it_value.tv_sec);
Answer:
Although the variable value
is declared as a pointer to a struct itimerval
structure, it does not point to anything. That is, there is no declaration of an actual struct itimerval
structure that value
represents.
Program 9.8 uses the interval timer ITIMER_VIRTUAL
to measure the execution time of function_to_time
. This example, unlike Program 9.1, uses virtual time. Remember that the value returned by getitimer
is the time remaining, so the quantity is decreasing.
Example 9.13.
How can you modify Program 9.8 to compensate for the overhead of calling setitimer
and getitimer
?
Answer:
Call the setitimer
and getitimer
pair with no intervening statements and use the time difference as an estimate of the timing overhead.
Example 9.14.
What happens if we replace the final return
in Program 9.8 with the infinite loop for( ; ; );
?
Answer:
After using one million seconds of virtual time, the program receives a SIGVTALRM
signal and terminates. One million seconds is approximately 12 days.
Example 9.8. xsitimer.c
A program that uses a POSIX:XSI interval timer to measure the execution time of a function.
#include <stdio.h> #include <sys/time.h> #define MILLION 1000000L void function_to_time(void); int main(void) { long diftime; struct itimerval ovalue, value; ovalue.it_interval.tv_sec = 0; ovalue.it_interval.tv_usec = 0; ovalue.it_value.tv_sec = MILLION; /* a large number */ ovalue.it_value.tv_usec = 0; if (setitimer(ITIMER_VIRTUAL, &ovalue, NULL) == -1) { perror("Failed to set virtual timer"); return 1; } function_to_time(); /* timed code goes here */ if (getitimer(ITIMER_VIRTUAL, &value) == -1) { perror("Failed to get virtual timer"); return 1; } diftime = MILLION*(ovalue.it_value.tv_sec - value.it_value.tv_sec) + ovalue.it_value.tv_usec - value.it_value.tv_usec; printf("The function_to_time took %ld microseconds or %f seconds. ", diftime, diftime/(double)MILLION); return 0; }
In the base POSIX standard, a signal handler is a function with a single integer parameter that represents the signal number of the generating signal. The POSIX:XSI Extension and the POSIX:RTS Realtime Signal Extension have expanded signal-handling capabilities to include the queueing of signals and the passing of information to signal handlers. The standard expands the sigaction
structure to allow for additional parameters to the signal handler. If _POSIX_REALTIME_SIGNALS
is defined, your implementation supports realtime signals.
Section 8.4 introduces the sigaction
function for examining or specifying the action associated with a signal. The struct sigaction
structure contains at least the fields given below and specifies the action taken by the sigaction
function.
SYNOPSIS #include <signal.h> struct sigaction { void (*sa_handler)(int); /* SIG_DFL, SIG_IGN, or pointer to function */ sigset_t sa_mask; /* additional signals to be blocked during execution of handler */ int sa_flags; /* special flags and options */ void(*sa_sigaction) (int, siginfo_t *, void *); /* realtime handler */ }; POSIX:CX
The sa_sigaction
member specifies an alternative type of signal handler. This handler is used if sa_flags & SA_SIGINFO
is nonzero. The form of this handler must be as follows.
void func(int signo, siginfo_t *info, void *context);
The signo
parameter, which is equivalent to the parameter of sa_handler
, gives the number of the caught signal. The context
is not currently defined by the POSIX standard. The siginfo_t
structure has at least the following members.
int si_signo; /* signal number */ int si_code; /* cause of the signal */ union sigval si_value; /* signal value */
The si_signo
parameter contains the signal number. This value is the same as the value passed by the signo
parameter of func
.
The si_code
parameter reports the cause of the signal. POSIX defines the following values for si_code: SI_USER
, SI_QUEUE
, SI_TIMER
, SI_ASYNCIO
and SI_MESGQ
. A value of SI_USER
means that the signal was generated explicitly by a function such as kill
, raise
or abort
. In these situations, there is no way of generating a value for si_value
, so it is not defined. A value of SI_QUEUE
means that the sigqueue
function generated the signal. A value of SI_TIMER
means that a POSIX:RTS timer expired and generated the signal. A value of SI_ASYNCIO
means completion of asynchronous I/O, and a value of SI_MESGQ
means the arrival of a message on an empty message queue. The si_code
variable may have other, implementation-defined values.
POSIX defines the contents of si_value
only when the implementation supports the POSIX:RTS Extension and the si_code
is SI_QUEUE
, SI_TIMER
, SI_ASYNCIO
or SI_MESGQ
. In these cases, the si_value
contains the application-specified signal value. The union sigval
is defined as follows.
int sival_int; void *sival_ptr;
According to this definition, either an integer or a pointer can be transmitted to the signal handler by the generator of the signal.
When multiple signals are pending, POSIX guarantees that at least one instance is delivered if the signal is unblocked. Additional instances may be lost. For applications in which it is important to receive every signal, use the POSIX:RTS signal queuing facility. The sigqueue
function is an extension to kill
that permits signals to be queued. Multiple instances of a signal generated with the kill
function may not be queued, even if instances of the same signal generated by sigqueue
are.
The sigqueue
function sends signal signo
with value value
to the process with ID pid
. If signo
is zero, error checking is performed, but no signal is sent. If SA_SIGINFO
in the sa_flags
field of the struct sigaction
structure was set when the handler for signo
was installed, the signal is queued and sent to the receiving process. If SA_SIGINFO
was not set for signo
, the signal is sent at least once but might not be queued.
SYNOPSIS #include <signal.h> int sigqueue(pid_t pid, int signo, const union sigval value); POSIX:RTS
If successful, sigqueue
returns 0. If unsuccessful, sigqueue
returns –1 and sets errno
. The following table lists the mandatory errors for sigqueue
.
| cause |
---|---|
| system does not have resources to queue this signal |
|
|
| caller does not have the appropriate privileges |
| no process corresponds to |
Example 9.15.
The following code segment checks to see whether process ID mypid
corresponds to a valid process.
pid_t mypid; union sigval qval; if ((sigqueue(mypid, 0, qval) == -1) && (errno == ESRCH)) fprintf(stderr, "%ld is not a valid process ID ", (long)mypid);
Program 9.9 shows a program that sends queued signals to a process. The program behaves like the kill
command, but it calls sigqueue
instead of kill
. The process ID, the signal number and the signal value are command-line arguments.
The union sigval
union can hold either a pointer or an integer. When the signal is generated from the same process by sigqueue
, a timer, asynchronous I/O or a message queue, the pointer can pass an arbitrary amount of information to the signal handler. It does not make sense to use sigqueue
to send a pointer from another process unless the address space of the sending process is accessible to the receiver.
Example 9.9. sendsigqueue.c
A program that sends a queued signal to a process.
#include <signal.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int pid; int signo; int sval; union sigval value; if (argc != 4) { fprintf(stderr, "Usage: %s pid signal value ", argv[0]); return 1; } pid = atoi(argv[1]); signo = atoi(argv[2]); sval = atoi(argv[3]); fprintf(stderr,"Sending signal %d with value %d to process %d ", signo, sval, pid); value.sival_int = sval; if (sigqueue(pid, signo, value) == -1) { perror("Failed to send the signal"); return 1; } return 0; }
Program 9.10 prints its process ID, sets up a signal handler for SIGUSR1
, and suspends itself until a signal arrives. The signal handler just displays the values it receives from its parameters. Notice that the signal handler uses fprintf
, which is not async-signal safe. This risky use works only because the main
program does not use fprintf
after it sets up the handler. The signal handler blocks other SIGUSR1
signals. Any other signal causes the process to terminate. You can use Program 9.9 in conjunction with Program 9.10 to experiment with POSIX realtime signals.
The asyncmonitorsignal.c
module of Program 8.14 on page 292 showed how to use a realtime signal with asynchronous I/O. The read is started by initread
. Three fields of the aio_sigevent
structure are used to set up the signal. The sigev_notify
field is set to SIGEV_SIGNAL
, and the signal number is set in the sigev_signo
field. Setting the sigev_value.sival_ptr
field to &aiocb
makes this pointer available to the signal handler in the si_value.sival_ptr
field of the handler’s second parameter. In Program 8.14, aiocb
was a global variable, so it was accessed directly. Instead, aiocb
could have been local to initread
with a static storage class.
Example 9.10. sigqueuehandler.c
A program that receives SIGUSR1
signals and displays their values. See the text for comments about using fprintf
in the signal handler.
#include <signal.h> #include <stdio.h> #include <unistd.h> static void my_handler(int signo, siginfo_t* info, void *context) { char *code = NULL; switch(info->si_code) { case SI_USER: code = "USER"; break; case SI_QUEUE: code = "QUEUE"; break; case SI_TIMER: code = "TIMER"; break; case SI_ASYNCIO: code = "ASYNCIO"; break; case SI_MESGQ: code = "MESGQ"; break; default: code = "Unknown"; } fprintf(stderr, "Signal handler entered for signal number %d ", signo); fprintf(stderr, "Signal=%3d, si_signo=%3d, si_code=%d(%s), si_value=%d ," signo, info->si_signo, info->si_code, code, info->si_value.sival_int); } int main(void) { struct sigaction act; fprintf(stderr, "Process ID is %ld ", (long)getpid()); fprintf(stderr, "Setting up signal SIGUSR1 = %d ready ", SIGUSR1); act.sa_flags = SA_SIGINFO; act.sa_sigaction = my_handler; if ((sigemptyset(&act.sa_mask) == -1) || (sigaction(SIGUSR1, &act, NULL) == -1)) { perror("Failed to set up SIGUSR1 signal"); return 1; } /* no fprintf calls from here on */ for( ; ; ) pause(); }
The interval timer facility of the POSIX:XSI Extension gives each process a small fixed number of timers, one of each of the types ITIMER_REAL
, ITIMER_VIRTUAL
, ITIMER_PROF
and so on. The POSIX:TMR Extension takes an alternative approach in which there are a small number of clocks, such as CLOCK_REALTIME
, and a process can create many independent timers for each clock.
POSIX:TMR timers are based on the struct itimerspec
structure, which has the following members.
struct timespec it_interval; /* timer period */ struct timespec it_value; /* timer expiration */
As with POSIX:XSI timers, the it_interval
is the time used for resetting the timer after it expires. The it_value
member holds the time remaining before expiration. The struct timespec
structure has the potential of offering better resolution than struct timeval
since its fields measure seconds and nanoseconds rather than seconds and microseconds.
A process can create specific timers by calling timer_create
. The timers are per-process timers that are not inherited on fork
. The clock_id
parameter of timer_create
specifies which clock the timer is based on, and *timerid
holds the ID of the created timer. The evp
parameter specifies the asynchronous notification to occur when the timer expires. The timer_create
function creates the timer and puts its ID in the location pointed to by timerid
.
SYNOPSIS #include <signal.h> #include <time.h> int timer_create(clockid_t clock_id, struct sigevent *restrict evp, timer_t *restrict timerid); struct sigevent { int sigev_notify /* notification type */ int sigev_signo; /* signal number */ union sigval sigev_value; /* signal value */ }; union sigval { int sival_int; /* integer value */ void *sival_ptr; /* pointer value */ }; POSIX:TMR
If successful, timer_create
returns 0. If unsuccessful, timer_create
returns –1 and sets errno
. The following table lists the mandatory errors for timer_create
.
| cause |
---|---|
| system does not have resources to honor request, or calling process already has maximum number of timers allowed |
| specified clock ID is not defined |
The members of the struct sigevent
structure shown in the synopsis are required by the POSIX:TMR Extension. The standard does not prohibit an implementation from including additional members.
Example 9.16.
The following code segment creates a POSIX:TMR timer based on the CLOCK_REALTIME
.
timer_t timerid; if (timer_create(CLOCK_REALTIME, NULL, &timerid) == -1) perror("Failed to create a new timer);
The *evp
parameter of timer_create
specifies which signal should be sent to the process when the timer expires. If evp
is NULL
, the timer generates the default signal when it expires. For CLOCK_REALTIME
, the default signal is SIGALRM
. For the timer expiration to generate a signal other than the default signal, the program must set evp->sigev_signo
to the desired signal number. The evp->sigev_notify
member of the struct sigevent
structure specifies the action to be taken when the timer expires. Normally, this member is SIGEV_SIGNAL
, which specifies that the timer expiration generates a signal. The program can prevent the timer expiration from generating a signal by setting the evp->sigev_notify
member to SIGEV_NONE
.
The timer_delete
function deletes the POSIX:TMR timer with ID timerid
.
SYNOPSIS #include <time.h> int timer_delete(timer_t timerid); POSIX:TMR
If successful, timer_delete
returns 0. If unsuccessful, timer_delete
returns –1 and sets errno
. The timer_delete
function sets errno
to EINVAL
if timerid
does not correspond to a valid timer.
Example 9.17.
What happens if a program calls timer_delete
when there are pending signals for timerid
?
Answer:
POSIX does not specify what happens to pending signals. You should not make any assumptions about their disposition when calling timer_delete
.
If several timers generate the same signal, the handler can use evp->sigev_value
to distinguish which timer generated the signal. To do this, the program must use the SA_SIGINFO
flag in the sa_flags
member of struct sigaction
when it installs the handler for the signal. (See Program 9.13 for an example of how to do this.)
The following three functions manipulate the per-process POSIX:TMR timers. The timer_settime
function starts or stops a timer that was created by timer_create
. The flags
parameter specifies whether the timer uses relative or absolute time. Relative time is similar to the scheme used by POSIX:XSI timers, whereas absolute time allows for greater accuracy and control of timer drift. Absolute time is further discussed in Section 9.6. The timer_settime
function sets the timer specified by timerid
to the value pointed to by value
. If ovalue
is not NULL
, timer_settime
places the previous value of the timer in the location pointed to by ovalue
. If the timer was running, the it_value
member of *ovalue
is nonzero and contains the time remaining before the timer would have expired. Use timer_gettime
like getitimer
to get the time remaining on an active timer.
It is possible for a timer to expire while a signal is still pending from a previous expiration of the same timer. In this case, one of the signals generated may be lost. This is called timer overrun. A program can determine the number of such overruns for a particular timer by calling timer_getoverrun
. Timer overruns occur only for signals generated by the same timer. Signals generated by multiple timers, even timers using the same clock and signal, are queued and not lost.
SYNOPSIS #include <time.h> int timer_getoverrun(timer_t timerid); int timer_gettime(timer_t timerid, struct itimerspec *value); int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue); POSIX:TMR
If successful, the timer_settime
and timer_gettime
functions return 0, and the timer_getoverrun
function returns the number of timer overruns. If unsuccessful, all three functions return –1 and set errno
. All three functions set errno
to EINVAL
when timerid
does not correspond to a valid POSIX:TMR timer. The timer_settime
function also sets errno
to EINVAL
when the nanosecond field of value
is not in the range [0, 109).
Program 9.11 shows how to create a timer that generates periodic interrupts. It generates a SIGALRM
interrupt every two seconds of real time.
Example 9.18.
Why didn’t we use strlen
in Program 9.11 to find the length of the message?
Answer:
The strlen
function is not guaranteed to be async-signal safe.
Example 9.19.
Program 9.11 uses pause
in an infinite loop at the end of the program but Program 9.7 does not. What would happen if we used pause
in Program 9.7?
Answer:
Nothing! There is no output.Program 9.7 measures virtual time and the process is not using any virtual time when it is suspended. Program 9.11 uses real time.
Example 9.11. periodicmessage.c
A program that displays a message every two seconds.
#include <errno.h> #include <signal.h> #include <stdio.h> #include <time.h> #include <unistd.h> #define BILLION 1000000000L #define TIMER_MSG "Received Timer Interrupt " /* ARGSUSED */ static void interrupt(int signo, siginfo_t *info, void *context) { int errsave; errsave = errno; write(STDOUT_FILENO, TIMER_MSG, sizeof(TIMER_MSG) - 1); errno = errsave; } static int setinterrupt() { struct sigaction act; act.sa_flags = SA_SIGINFO; act.sa_sigaction = interrupt; if ((sigemptyset(&act.sa_mask) == -1) || (sigaction(SIGALRM, &act, NULL) == -1)) return -1; return 0; } static int setperiodic(double sec) { timer_t timerid; struct itimerspec value; if (timer_create(CLOCK_REALTIME, NULL, &timerid) == -1) return -1; value.it_interval.tv_sec = (long)sec; value.it_interval.tv_nsec = (sec - value.it_interval.tv_sec)*BILLION; if (value.it_interval.tv_nsec >= BILLION) { value.it_interval.tv_sec++; value.it_interval.tv_nsec -= BILLION; } value.it_value = value.it_interval; return timer_settime(timerid, 0, &value, NULL); } int main(void) { if (setinterrupt() == -1) { perror("Failed to setup SIGALRM handler"); return 1; } if (setperiodic(2.0) == -1) { perror("Failed to setup periodic interrupt"); return 1; } for ( ; ; ) pause(); }
Program 9.12 creates a POSIX:TMR timer to measure the running time of function_to_time
. The program is similar to Program 9.8, but it uses real time rather than virtual time.
Example 9.12. tmrtimer.c
A program that uses a POSIX:TMR timer to measure the running time of a function.
#include <stdio.h> #include <time.h> #define MILLION 1000000L #define THOUSAND 1000 void function_to_time(void); int main(void) { long diftime; struct itimerspec nvalue, ovalue; timer_t timeid; if (timer_create(CLOCK_REALTIME, NULL, &timeid) == -1) { perror("Failed to create a timer based on CLOCK_REALTIME"); return 1; } ovalue.it_interval.tv_sec = 0; ovalue.it_interval.tv_nsec = 0; ovalue.it_value.tv_sec = MILLION; /* a large number */ ovalue.it_value.tv_nsec = 0; if (timer_settime(timeid, 0, &ovalue, NULL) == -1) { perror("Failed to set interval timer"); return 1; } function_to_time(); /* timed code goes here */ if (timer_gettime(timeid, &nvalue) == -1) { perror("Failed to get interval timer value"); return 1; } diftime = MILLION*(ovalue.it_value.tv_sec - nvalue.it_value.tv_sec) + (ovalue.it_value.tv_nsec - nvalue.it_value.tv_nsec)/THOUSAND; printf("The function_to_time took %ld microseconds or %f seconds. ", diftime, diftime/(double)MILLION); return 0; }
One of the problems associated with POSIX:TMR timers and POSIX:XSI timers, as described so far, is the way they are set according to relative time. Suppose you set a periodic interrupt with an interval of 2 seconds, as in Program 9.7 or Program 9.11. When the timer expires, the system automatically restarts the timer for another 2-second interval. Let’s say the latency between when the timer was due to expire and when the timer was reset is 5 μsec. The actual period of the timer is 2.000005 seconds. After 1000 interrupts the timer will be off by 5 ms. This inaccuracy is called timer drift.
The problem can be even more severe when the timer is restarted from the timer signal handler rather than from the it_interval
field of struct itimerval
or struct itimerspec
. In this case, the latency depends on the scheduling of the processes and the timer resolution. A typical timer resolution is 10 ms. With a latency of 10 ms, the timer drift will be 10 seconds after 1000 iterations.
Example 9.20.
Consider an extreme case of a repeating timer with period of 22 ms when the timer has a resolution of 10 ms. Estimate the timer drift for 10 expirations of the timer.
Answer:
If you set the time until expiration to be 22 ms, this value will be rounded up to the clock resolution to give 30 ms, giving a drift of 8 ms every 30 ms. These results are summarized in the following table. The drift grows by 8 ms on each expiration.
expiration number | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
---|---|---|---|---|---|---|---|---|---|---|---|
time | 0 | 30 | 60 | 90 | 120 | 150 | 180 | 210 | 240 | 270 | 300 |
drift | 0 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 |
desired expiration | 22 | 44 | 66 | 88 | 110 | 132 | 154 | 176 | 198 | 220 | 242 |
timer set for | 22 | 22 | 22 | 22 | 22 | 22 | 22 | 22 | 22 | 22 | 22 |
rounded to resolution | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 |
One way to handle the drift problem is keep track of when the timer should actually expire and adjust the value for setting the timer each time. This method uses absolute time for setting the timer rather than relative time.
Example 9.21.
For the specific case described by Exercise 9.20, devise a procedure for setting the timers according to absolute time. What is the timer drift for 10 iterations? Work out a chart similar to the one of Exercise 9.20.
Answer:
Before starting the timer for the first time, determine the current time, add 22 ms to this and save the value as T. This is the desired expiration time.
Set the timer to expire in 22 ms.
In the signal handler, determine the current time, t. Set the timer to expire in time (T - t + 22 ms). Add 22 ms to T so that T represents the next desired expiration time.
If the timer resolution is 30 ms, then the time at the beginning of step 3 is approximately t = T + 30 ms, and the timer is set to expire in 12 ms. No matter how long the program runs, the total timer drift will be less than 10 ms.
expiration number | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
---|---|---|---|---|---|---|---|---|---|---|---|
time | 0 | 30 | 50 | 70 | 90 | 110 | 140 | 160 | 180 | 200 | 220 |
drift | 0 | 8 | 6 | 4 | 2 | 0 | 8 | 6 | 4 | 2 | 0 |
desired expiration | 22 | 44 | 66 | 88 | 10 | 132 | 154 | 176 | 198 | 220 | 242 |
timer set for | 22 | 14 | 16 | 18 | 20 | 22 | 14 | 16 | 18 | 20 | 22 |
rounded to resolution | 30 | 20 | 20 | 20 | 20 | 30 | 20 | 20 | 20 | 20 | 30 |
The procedure of Exercise 9.21 assumes that the value (T - t + 22 ms) is never negative. You cannot set a timer to expire in the past. A negative value means that a timer expiration has been missed completely. This is called a timer overrun. A timer overrun also occurs when the timer is set to automatically restart and a new signal is generated before the previous one has been handled by the process.
The POSIX:TMR timers can make it easier to use absolute time, and they can keep track of timer overruns. POSIX:TMR does not queue signals generated by the same timer. The timer_getoverrun
function can be called from within the timer signal handler to obtain the number of missed signals. The flags
parameter of timer_settime
can be set to TIMER_ABSOLUTE
to signify that the time given in the it_value
member of the *value
parameter represents the real time rather than a time interval. The time is related to the clock from which the timer was generated.
Example 9.22.
Outline the procedure for using POSIX:TMR timers with absolute time to solve the problem of Exercise 9.21.
Answer:
The procedure for using absolute time with POSIX:TMR timers is as follows.
Before starting the first timer for the first time, determine the current time by using clock_gettime
and add 22 ms to this. Save this value as T.
Set the timer to expire at time T. Use the TIMER_ABSOLUTE
flag.
In the timer signal handler, add 22 ms to T and set the timer to expire at time T.
The abstime
program of Program 9.13 demonstrates various scenarios for using the POSIX:TMR timer facility. Program 9.13 has three modes of operation: absolute time, relative time and automatic periodic reset. Use the abstime
program as follows.
abstime -a | -r | -p [inctime [numtimes [spintime]]]
The first command-line argument must be -a
, -r
or -p
specifying absolute time, relative time or automatic periodic reset. The optional additional arguments (inctime
, numtimes
and spintime
) control the sequence in which timer expirations occur. The program generates numtimes SIGALARM
signals that are inctime
seconds apart. The signal handler wastes spintime
seconds before handling the timer expiration.
The abstime
program uses a POSIX:TMR timer that is created with timer_create
and started with timer_settime
. For absolute times, the abstime
program sets the TIMER_ABSTIME
flag in timer_settime
and sets the it_value
member of value
field to the current absolute time (time since January 1, 1970) plus the inctime
value. When the timer expires, abstime
calculates a new absolute expiration time by adding inctime
to the previous expiration time. If relative time is set, the program sets it_value
to the value specified by inctime
. When the timer expires, the handler uses inctime
to restart the timer. For periodic time, abstime
sets relative time and automatically restarts the timer so that the handler does not have to restart it. The program calculates the time it should take to finish numtimes
timer expirations and compares the calculated value with the actual time taken.
Program 9.14 is a header file that defines a data type and the prototypes of the functions in Program 9.15 that are used in the main
program of Program 9.13. You must link these files with Program 9.13 to run the abstime
program.
Example 9.23.
The following command uses abstime
with absolute time. It simulates a signal handler that takes 5 milliseconds to execute and does 1000 iterations with a time interval of 22 milliseconds. If the timing were exact, the 5 milliseconds of spin time would not affect the total running time, which should be 22 seconds.
abstime -a 0.022 1000 0.005
Example 9.24.
The command of Example 9.23 uses absolute time. Are there differences in output when it is run with relative time instead?
Answer:
For an execution of
abstime -a 0.022 1000 0.005
the output might be the following.
pid = 12374 Clock resolution is 10000.000 microseconds or 0.010000 sec. Using absolute time Interrupts: 1000 at 0.022000 seconds, spinning 0.005000 Total time: 22.0090370, calculated: 22.0000000, error = 0.0090370
For an execution of
abstime -r 0.022 1000 0.005
the output might be the following.
pid = 12376 Clock resolution is 10000.000 microseconds or 0.010000 sec. Using relative time Interrupts: 1000 at 0.022000 seconds, spinning 0.005000 Total time: 30.6357934, calculated: 22.0000000, error = 8.6357934
When absolute timers are used, the error is much less than 1 percent, while relative timers show the expected drift corresponding to the amount of processing time and timer resolution.
The resolution of the clock is displayed by means of a call to clock_getres
. A typical value for this might be anywhere from 1000 nanoseconds to 20 milliseconds. The 20 milliseconds (20,000,000 nanoseconds or 50 Hertz) is the lowest resolution allowed by the POSIX:TMR Extension. One microsecond (1000 nanoseconds) is the time it takes to execute a few hundred instructions on most fast machines. Just because a system has a clock resolution of 1 microsecond does not imply that a program can use timers with anything near this resolution. A context switch is often needed before the signal handler can be entered and, as Table 1.1 on page 5 points out, a context switch can take considerably longer than this.
Example 9.25.
The following command uses Program 9.13 to estimate the effective resolution of the hardware timer on a machine by calling abstime
with an inctime
of 0, default numtimes
of 1 and default spintime
of 0. The abstime
program displays the clock resolution and starts one absolute time clock interrupt to expire at the current time. The timer expires immediately.
abstime -a 0
Example 9.26.
The following command uses Program 9.13 to determine the maximum number of timer signals that can be handled per second by starting 1000 timer interrupts with an inctime
of 0. These should all expire immediately. The abstime
program then displays the minimum time for 1000 interrupts.
abstime -a 0.0 1000 0.0
Program 9.13 illustrates some other useful tips in using POSIX:TMR timers. Information about the timer that generated the signal is available in the signal handler. When a timer is created, an integer or a pointer can be stored in the sigev_value
member of the struct sigevent
structure. If the signal handler is to restart that timer or if multiple timers are to share a signal handler, the signal handler must have access to the timer ID of the timer that generated the signal. If the signal handler was set up with the SA_SIGINFO
flag, it can access the value that timer_create
stored in sigev_value
through its second parameter. The timer_create
cannot directly store the timer ID in its sigev_value
because the ID is not known until after the timer has been created. It therefore stores a pointer to the timer ID in the sival_ptr
member of union sigval
.
Example 9.13. abstime.c
The abstime
program illustrates POSIX:TMR timers with absolute time. Program 9.14 and Program 9.15 are called.
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #include "abstime.h" #define INCTIME 0.01 #define NUMTIMES 1 #define SPINTIME 0.0 int main(int argc, char *argv[]) { struct sigaction act; struct timespec clockres, currenttime; timer_data data; struct sigevent evp; sigset_t sigset; double tcalc, tend, tstart, ttotal; data.exitflag = 0; data.inctime = INCTIME; data.numtimes = NUMTIMES; data.spintime = SPINTIME; data.type = -1; if (argc > 1) { if (!strcmp(argv[1], "-r")) data.type = TYPE_RELATIVE; else if (!strcmp(argv[1], "-a")) data.type = TYPE_ABSOLUTE; else if (!strcmp(argv[1], "-p")) data.type = TYPE_PERIODIC; } if ( (argc < 2) || (argc > 5) || (data.type < 0) ){ fprintf(stderr, "Usage: %s -r | -a | -p [inctime [numtimes [spintime]]] ", argv[0]); return 1; } if (argc > 2) data.inctime = atof(argv[2]); if (argc > 3) data.numtimes = atoi(argv[3]); if (argc > 4) data.spintime = atof(argv[4]); fprintf(stderr, "pid = %ld ", (long)getpid()); act.sa_flags = SA_SIGINFO; act.sa_sigaction = timehandler; if ((sigemptyset(&act.sa_mask) == -1) || (sigaction(SIGALRM, &act, NULL)) == -1) { perror("Failed to set handler for SIGALRM"); return 1; } evp.sigev_notify = SIGEV_SIGNAL; evp.sigev_signo = SIGALRM; evp.sigev_value.sival_ptr = &data; if (timer_create(CLOCK_REALTIME, &evp, &data.timid) < 0) { perror("Failed to create a timer"); return 1; } if (clock_getres(CLOCK_REALTIME, &clockres) == -1) perror("Failed to get clock resolution"); else fprintf(stderr, "Clock resolution is %0.3f microseconds or %0.6f sec. ", D_MILLION*time_to_double(clockres), time_to_double(clockres)); data.tvalue.it_interval.tv_sec = 0; data.tvalue.it_interval.tv_nsec = 0; data.tvalue.it_value = double_to_time(data.inctime); data.flags = 0; if (clock_gettime(CLOCK_REALTIME, ¤ttime) == -1) { perror("Failed to get current time"); return 1; } tstart = time_to_double(currenttime); if (data.type == TYPE_ABSOLUTE) { data.tvalue.it_value.tv_nsec += currenttime.tv_nsec; data.tvalue.it_value.tv_sec += currenttime.tv_sec; if (data.tvalue.it_value.tv_nsec >= BILLION) { data.tvalue.it_value.tv_nsec -= BILLION; data.tvalue.it_value.tv_sec++; } data.flags = TIMER_ABSTIME; fprintf(stderr,"Using absolute time "); } else if (data.type == TYPE_RELATIVE) fprintf(stderr,"Using relative time "); else if (data.type == TYPE_PERIODIC) { data.tvalue.it_interval = data.tvalue.it_value; fprintf(stderr,"Using periodic time "); } fprintf(stderr, "Interrupts: %d at %.6f seconds, spinning %.6f ", data.numtimes, data.inctime, data.spintime); if (timer_settime(data.timid, data.flags, &data.tvalue, NULL) == -1){ perror("Failed to start timer"); return 1; } if (sigemptyset(&sigset) == -1) { perror("Failed to set up suspend mask"); return 1; } while (!data.exitflag) sigsuspend(&sigset); if (clock_gettime(CLOCK_REALTIME, ¤ttime) == -1) { perror("Failed to get expiration time"); return 1; } tend = time_to_double(currenttime); ttotal=tend - tstart; tcalc = data.numtimes*data.inctime; fprintf(stderr, "Total time: %1.7f, calculated: %1.7f, error = %1.7f ", ttotal, tcalc, ttotal - tcalc); return 0; }
Example 9.14. abstime.h
The abstime.h
include file contains constants, type definitions, and prototypes used by abstime
and abstimelib
.
#define BILLION 1000000000L #define D_BILLION 1000000000.0 #define D_MILLION 1000000.0 #define TYPE_ABSOLUTE 0 #define TYPE_RELATIVE 1 #define TYPE_PERIODIC 2 typedef struct { timer_t timid; int type; int flags; int numtimes; int exitflag; double inctime; double spintime; struct itimerspec tvalue; } timer_data; struct timespec double_to_time(double tm); double time_to_double(struct timespec t); void timehandler(int signo, siginfo_t* info, void *context);
Example 9.15. abstimelib.c
The abstimelib
module contains the signal handler and utility routines used by abstime
.
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #include "abstime.h" static struct timespec add_to_time(struct timespec t, double tm) { struct timespec t1; t1 = double_to_time(tm); t1.tv_sec = t1.tv_sec + t.tv_sec; t1.tv_nsec = t1.tv_nsec + t.tv_nsec; while (t1.tv_nsec >= BILLION) { t1.tv_nsec = t1.tv_nsec - BILLION; t1.tv_sec++; } return t1; } static int spinit (double stime) { /* loops for stime seconds and returns */ struct timespec tcurrent; double tend, tnow; if (stime == 0.0) return 0; if (clock_gettime(CLOCK_REALTIME, &tcurrent) == -1) return -1; tnow = time_to_double(tcurrent); tend = tnow + stime; while (tnow < tend) { if (clock_gettime(CLOCK_REALTIME, &tcurrent) == -1) return -1; tnow = time_to_double(tcurrent); } return 0; } /* ------------------------- Public functions -------------------------- */ double time_to_double(struct timespec t) { return t.tv_sec + t.tv_nsec/D_BILLION; } struct timespec double_to_time(double tm) { struct timespec t; t.tv_sec = (long)tm; t.tv_nsec = (tm - t.tv_sec)*BILLION; if (t.tv_nsec == BILLION) { t.tv_sec++; t.tv_nsec = 0; } return t; } void timehandler(int signo, siginfo_t* info, void *context) { timer_data *datap; static int timesentered = 0; timesentered++; datap = (timer_data *)(info->si_value.sival_ptr); if (timesentered >= datap->numtimes) { datap->exitflag = 1; return; } if (spinit(datap->spintime) == -1) { write(STDERR_FILENO, "Spin failed in handler ", 23); datap->exitflag = 1; } if (datap->type == TYPE_PERIODIC) return; if (datap->type == TYPE_ABSOLUTE) datap->tvalue.it_value = add_to_time(datap->tvalue.it_value, datap->inctime); if (timer_settime(datap->timid, datap->flags, &datap->tvalue, NULL) == -1) { write(STDERR_FILENO, "Could not start timer in handler ",33); datap->exitflag = 1; } }
Example 9.16. timesignals.c
A program that calculates the time to receive 1000 SIGALRM
signals.
#include <signal.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/time.h> #define COUNT 1000 #define MILLION 1000000L static int count = 0; /* ARGSUSED */ static void handler(int signo, siginfo_t *info, void *context) { count++; } int main(void) { struct sigaction act; sigset_t sigblocked, sigunblocked; long tdif; struct timeval tend, tstart; act.sa_flags = SA_SIGINFO; act.sa_sigaction = handler; if ((sigemptyset(&act.sa_mask) == -1) || (sigaction(SIGALRM, &act, NULL) == -1)) { perror("Failed to set up handler for SIGALRM"); return 1; } if ((sigemptyset(&sigblocked) == -1) || (sigemptyset(&sigunblocked) == -1) || (sigaddset(&sigblocked, SIGALRM) == -1) || (sigprocmask(SIG_BLOCK, &sigblocked, NULL) == -1)) { perror("Failed to block signal"); return 1; } printf("Process %ld waiting for first SIGALRM (%d) signal ", (long)getpid(), SIGALRM); sigsuspend(&sigunblocked); if (gettimeofday(&tstart, NULL) == -1) { perror("Failed to get start time"); return 1; } while (count <= COUNT) sigsuspend(&sigunblocked); if (gettimeofday(&tend, NULL) == -1) { perror("Failed to get end time"); return 1; } tdif = MILLION*(tend.tv_sec - tstart.tv_sec) + tend.tv_usec - tstart.tv_usec; printf("Got %d signals in %ld microseconds ", count-1, tdif); return 0; }
Although the timer resolution might be as large as 10 ms, signals may be processed at a much higher rate than timer signals can be generated. Program 9.16 waits for SIGALRM
signals and calculates the time to receive 1000 signals after the first one arrives. You can use Program 9.17 to send signals to a process. It takes two command-line arguments: a process ID and a signal number. It sends the signals as fast as it can until the process dies. A reasonably fast machine should be able to handle several thousand signals per second.
Example 9.17. multikill.c
The multikill
program continually sends signals to another process until the process dies.
#include <signal.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int pid; int sig; if (argc != 3) { fprintf(stderr, "Usage: %s pid signal ", argv[0]); return 1; } pid = atoi(argv[1]); sig = atoi(argv[2]); while (kill(pid, sig) == 0) ; return 0; }
Realtime issues promise to become more important in the future. The book POSIX.4: Programming for the Real World by Gallmeister [39] provides a general introduction to realtime programming under the POSIX standard. POSIX.4 was the name of the standard before it was approved. It is now an extension of the POSIX standard referred to as POSIX:RTS. The POSIX:TMR Extension is one of the required components for systems supporting POSIX:RTS.