In any type of program, we mostly are interested in two different time:
-
Real (Calendar) time: This time is measured from a standard point (generally, UTC, 1 January 1970). Obtaining the calendar time is primarily source of what you see at screen of your computer.
-
Process time: This is the amount of CPU time used by a process. Measuring process time is useful for checking or optimizing the performance of a program or algorithm.
Every computer has a dedicated built-in hardware clock that enables the kernel to measure real and process time.
Calendar Time and Time Conversion
To handle real time, the GNU libraries give us many functions as below:
These are also function signatures that we use when handling time in the program:
time_t time(time_t *tloc);
char *asctime(const struct tm *tm);
char *ctime(const time_t *timep);
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
time_t mktime(struct tm *tm);
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
The time() system call returns the number of seconds since the Epoch (1 January 1970) and then this number of seconds are stored in time_t (typedef long time_t
) data type.
To convert this number of seconds in printable format, we use the ctime() function.
We can also convert this number of seconds into a so-called broken-down time (struct tm
) by using gmtime() and localtime() functions. After that, we see the year, month, week, hours and so on.
The mktime() function does vise-verse, translates a broken-down time into number of seconds.
We also use asctime() to convert broken-down time to printable format.
I put here a simple program that shows the how to use these functions I’ve described:
/* Retrieving and converting calendar times */
#include "../linux.h"
#define SECONDS_IN_TROPICAL_YEAR (365.24219 * 24 * 60 * 60)
void main(int argc, char *argv[])
{
time_t t;
struct tm *gmp, *locp;
struct tm gm, loc;
struct timeval tv;
t = time(NULL);
printf("Seconds since the Epoch: %ld", (long) t);
printf(" (about %6.3f years)n", t / SECONDS_IN_TROPICAL_YEAR);
if (gettimeofday(&tv, NULL) == -1)
syscall_error();
printf(" gettimeofday() returned %ld secs, %ld microsecsn",
(long) tv.tv_sec, (long) tv.tv_usec);
gmp = gmtime(&t);
if (gmp == NULL)
syscall_error();
gm = *gmp; /* Save local copy */
printf("Broken down by gmtime():n");
printf(" year=%d, mon=%d, mday=%d, hour=%d, min=%d, sec=%d ",
gm.tm_year, gm.tm_mon, gm.tm_mday, gm.tm_hour, gm.tm_min, gm.tm_sec);
printf(" wday=%d, yday=%d, isdst=%dn", gm.tm_wday, gm.tm_yday, gm.tm_isdst);
locp = localtime(&t);
if (locp == NULL)
syscall_error();
loc = *locp; /* Save local copy */
printf("Broken down by localtime():n");
printf(" year=%d, mon=%d, mday=%d, hour=%d, min=%d, sec=%d ",
loc.tm_year, loc.tm_mon, loc.tm_mday, loc.tm_hour, loc.tm_min, loc.tm_sec);
printf(" wday=%d, yday=%d, isdst=%dn", loc.tm_wday, loc.tm_yday, loc.tm_isdst);
printf("asctime() formats the gmtime() value as: %sn", asctime(&gm));
printf("ctime() formats the time() value as: %sn", ctime(&t));
printf("mktime() of gmtime() value: %ld secsn", (long) mktime(&gm));
printf("mktime() of localtime() value: %ld secsn", (long) mktime(&loc));
exit(EXIT_SUCCESS);
}
The strftime() function provides us with more precise control when converting a broken-down into printable form. We just put pre-defined specifiers into third argument of strftime() function. These specifiers:
Below is the another program that shows the function usage:
/* A function that returns a string containing the current time */
#include "../linux.h"
#define BUF_SIZE 1000
/* Return a string containing the current time formatted according to
the specification in 'format' (see strftime(3) for specifiers).
If 'format' is NULL, we use "%c" as a specifier (which gives the
date and time as for ctime(3), but without the trailing newline).
Returns NULL on error. */
char *currTime(const char *format)
{
static char buffer[BUF_SIZE];
time_t t;
size_t s;
struct tm *tm;
t = time(NULL);
tm = localtime(&t);
if (tm == NULL)
return NULL;
s = strftime(buffer, BUF_SIZE, (format != NULL) ? format : "%c", tm);
return (s == 0) ? NULL : buffer;
}
Timezones
Different countries operate on different timezones and DST regimes. Programs that input and output times must take care of this.
Timezone information tends to be both voluminous and volatile. So rather than encoding it directly into libraries, the system maintains this information in files under /usr/share/zoneinfo.
To specify a timezone when running a program, we set the TZ
environment variable to a string consisting of a colon followed by one of the timezone names (like TZ=”:Pacific/Auckland”).
The tzset() function is used to obtain the current timezone setting. This function initializes three global variables:
char *tzname[2];
int daylight;
long timezone;
This function first checks the TZ
environment variable. If this variable is not set, then the timezone is initialized to the default defined in the timezone file /etc/localtime. If the TZ
environment variable is defined with a value that can’t be matched to a timezone file, or it is an empty string, then UTC is used.
Locales
Different countries use different conventions for displaying information such as numbers, currency amounts, dates, and times.
Ideally, all programs designed to run in more than one location should deal with locales in order to display and input information in the user’s preferred language and format. This constitutes the complex subject of internationalization (I18N).
Like timezone information, locales are maintained under /usr/share/locale. Under each locale subdirectory is a standard set of files that specify the conventions for this locale:
The setlocale() function is used to both set and query program’s current locale. Below is the its function signature:
char *setlocale(int category, const char *locale);
There are two different methods of setting the locale. The locale argument may be a string specifying one of the locales defined under /usr/lib/locale or more conveniently selocale(LC_ALL, "")
meaning that locale settings should be taken from environment variables.
Process Time
Process time is the amount of CPU time used by a process since it was created. For recording purposes, the kernel separates CPU time into the following two components:
-
User CPU time (amount of time spent executing in user mode)
-
System CPU time (amount of time spent executing in kernel mode)
Sometimes, we refer to process time as the total CPU time consumed by the process.
When dealing with process time, times() and clock() functions are used. Signatures are below:
clock_t times(struct tms *buf);
clock_t clock(void);
The times() system call retrieves process time information (both user and system time separately) and store it into struct tms
.
Otherwise, clock() function is simpler and returns the total amount of CPU time. Below is the basic program that shows both of these functions.
/* Retrieving process CPU times */
#include "../linux.h"
void displayProcessTimes(const char *msg)
{
struct tms t;
clock_t clockTime;
static long clockTicks = 0;
if (msg != NULL)
printf("%s", msg);
if (clockTicks == 0) { /* Fetch clock ticks on first call */
clockTicks = sysconf(_SC_CLK_TCK);
if (clockTicks == -1)
syscall_error();
}
clockTime = clock();
if (clockTime == -1)
syscall_error();
printf("clock() returns: %ld clocks-per-sec (%.2f secs)n",
(long) clockTime, (double) clockTime / CLOCKS_PER_SEC);
if (times(&t) == -1)
syscall_error();
printf("times() yields: user CPU: %.2f; system CPU: %.2fn",
(double) t.tms_utime / clockTicks,
(double) t.tms_stime / clockTicks);
}
void main(int argc, char *argv[])
{
int numCalls, i;
printf("CLOCKS_PER_SEC: %ld sysconf(_SC_CLK_TCK): %ldnn",
(long) CLOCKS_PER_SEC, sysconf(_SC_CLK_TCK));
displayProcessTimes("At program start:n");
numCalls = (argc > 1) ? atoi(argv[1]) : 100000000;
for (i = 0; i < numCalls; i++)
(void) getppid();
displayProcessTimes("After getppid() loop:n");
exit(EXIT_SUCCESS);
}