Chapter 9. Times and Timers

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 Times

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.

Expressing time in seconds since the Epoch

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.

Displaying date and time

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.

Example 9.6. 

The following code segment prints the number of days since the beginning of the year, using the thread-safe localtime_r function.

struct tm tbuffer;

if (localtime_r(time(NULL), &tbuffer) != NULL)
   printf("%d days have elapsed since Jan 1
", tbuffer.tm_yday);

Using struct timeval to express time

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.

Using realtime clocks

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;
}

Contrasting elapsed time to processor time

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;
}

Sleep Functions

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.

errno

cause

EINTR

nanosleep interrupted by a signal

EINVAL

rqtp specifies a nanosecond value that is not in [0, 109)

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.

POSIX:XSI Interval Timers

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.

ITIMER_REAL:

decrements in real time and generates a SIGALRM signal when it expires.

ITIMER_VIRTUAL:

decrements in virtual time (time used by the process) and generates a SIGVTALRM signal when it expires.

ITIMER_PROF:

decrements in virtual time and system time for the process and generates a SIGPROF signal when it expires.

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;
}

Realtime Signals

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.

errno

cause

EAGAIN

system does not have resources to queue this signal

EINVAL

signo is an invalid or unsupported signal

EPERM

caller does not have the appropriate privileges

ESRCH

no process corresponds to pid

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();
}

POSIX:TMR Interval Timers

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.

errno

cause

EAGAIN

system does not have resources to honor request, or calling process already has maximum number of timers allowed

EINVAL

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;
}

Timer Drift, Overruns and Absolute Time

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:

  1. 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.

  2. Set the timer to expire in 22 ms.

  3. 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.

  1. 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.

  2. Set the timer to expire at time T. Use the TIMER_ABSOLUTE flag.

  3. 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, &currenttime) == -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, &currenttime) == -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;
}

Additional Reading

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.

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

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