9.1 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.
9.1.1 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.
Exercise 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\n", 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.
9.1.2 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\n
Example 9.3 timeprint.c
The following program prints the date and time. The printf format did not include '\n' 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;}
Exercise 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\n", 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\n", tbuffer.tm_yday);
9.1.3 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.
Exercise 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).
Program 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 1000000Lvoid 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\n"); return 1; } function_to_time(); /* timed code goes here */ if (gettimeofday(&tpend, NULL)) { fprintf(stderr, "Failed to get end time\n"); return 1; } timedif = MILLION*(tpend.tv_sec - tpstart.tv_sec) + tpend.tv_usec - tpstart.tv_usec; printf("The function_to_time took %ld microseconds\n", 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.
9.1.4 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.
Program 9.2 gettimeofdaytest.c
A program to test the resolution of gettimeofday.
#include <stdio.h>#include <sys/time.h>#define MILLION 1000000L#define NUMDIF 20int 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.\n"); return 1; } while (numdone < NUMDIF) { numcalls++; if (gettimeofday(&tthis, NULL)) { fprintf(stderr, "Failed to get a later gettimeofday.\n"); 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:\n", NUMDIF); printf("%d calls to gettimeofday were required\n", numcalls); for (i = 0; i < NUMDIF; i++) { printf("%2d: %10ld microseconds\n", i, timedif[i]); sum += timedif[i]; } printf("The average nonzero difference is %f\n", 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 1000000Lvoid 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\n", 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.
Program 9.3 clockrealtimetest.c
A program to test the resolution of CLOCK_REALTIME.
#include <stdio.h>#include <time.h>#define BILLION 1000000000L#define NUMDIF 20int 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\n"); else printf("Clock resolution: %ld nanoseconds\n", (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:\n", NUMDIF); printf("%d calls to CLOCK_REALTIME were required\n", numcalls); for (i = 0; i < NUMDIF; i++) { printf("%2d: %10ld nanoseconds\n", i, timedif[i]); sum += timedif[i]; } printf("The average nonzero difference is %f\n", sum/(double)NUMDIF); return 0;}
9.1.5 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.
Program 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\n", clockticks); if (clockticks == 0) { fprintf(stderr, "The number of ticks per second is invalid\n"); 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\n",cticks/clockticks); if ((tcend <= tcstart) || (tcend < 0) || (tcstart < 0)) { fprintf(stderr, "Tick time wrapped, couldn't calculate fraction\n); return 1; } printf("Fraction of CPU time used is %f\n", 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
Program 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\n", 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\n"); return 1; } cticks = tmend.tms_cutime + tmend.tms_cstime - tmend.tms_utime - tmend.tms_stime; printf("%s used %ld clock ticks or %f seconds\n", argv[1], (long)cticks, cticks/clockticks); return 0;}
文章来源于领测软件测试网 https://www.ltesting.net/