CS 3210 - Lab 10 - Time and Random Numbers

It cannot be seen, cannot be felt,
Cannot be heard, cannot be smelt.
It lies behind starts and under hills,
And empty holes it fills.
It comes first and follows after,
Ends life, kills laughter.

This lab is all about getting the date/time, setting timer intervals and generating random numbers.

The chapters correponding to this lab are Chapters 17 and 18.

Getting and Displaying the Current Time

In the Shell

From the shell, you can get the system time with the date command (man date). The system administrator can use this program to set the current time. The default output looks something like:

Mon Jul  8 11:13:29 MDT 2002

If you want to format the time a little differently, you can pass date a format string (man date for the details). Example:

$ date +"%d %b %Y %I:%M:%S"
08 Jul 2002 11:13:29

In a C Program

There are a variety of ways to get tye system time in a C program. These are explained in your book on pgs. 357-360. Here's a synopsis of the various functions and the reasons why you would use each method. The first two (time_t and struct tm) are probably the most useful. They can all be used by #include'ing time.h.

function / struct what why use it displaying converting to
time() / time_t (p.357) Returns the number of seconds since the "epoch", Jan 1, 1970 (when the first version of Unix was released for public consumption). Good for making quick comparisons or passed to srand() (see below) ctime() mktime(struct tm) (local)
timegm(struct tm) (Greenwich)
localtime(time(NULL)) / struct tm (p.359) Breaks down time into human readable terms. Use this one if you like human readable time. asctime(), strftime(), strptime() the two str* functions work like date formatting. localtime(time_t) (local)
gmtime(time_t) (Greenwich)
gettimeofday(), settimeofday() / struct timeval, struct timezone (p.358) Like time() / time_t but features higher resolution (handles microseconds) If you need higher resolution or need to set the time. None, do it by hand using the previous None, do it by hand using the previous
struct timespec (p.358) Like struct timeval but features even higher resolution (handles nanoseconds) If you need to sleep for N nanoseconds (man nanosleep) None, do it by hand using the previous. None, do it by hand using the previous.

Why are there so many ways to get the time? Answer: like so many other things in the computer industry, progress is made by realizing the limitations of our previous attempts. You'll notice on the above chart that there is a progression to higher and higher resolutions (seconds, microseconds, nanoseconds), more easily parsed data (i.e. using structs instead of a single integer value), and easier ways to set the time, as well as get it.

Example Program: daytime.c - Shows how to get / display time using gettimeofday() and time_t. Note that this example illustrates that Unix does not have a year 2000 problem, but a year 2038 problem (on 32-bit architectures) (see p. 365).

Timer Intervals

You might want to wait or be notified after a certain amount of time has elapsed.

Sleeping

The way to wait for a little while is to use sleep() (seconds), usleep() (microseconds), or nanosleep() (nanoseconds). See p. 367. The select() syscall is actually more portable for sleeping for microseconds. See pgs. 211-212 for a discusson on using select() for waiting.

Example Program: usecsleep.c - This program shows how to make a portable function that will sleep at the granularity of microseconds using select.

One-Shot Timers

This type of timer is one that you set and it alerts you after a specified interval has elapsed. Unix uses the signal mechanism to send these notifications. Setting a one-shot timer in Unix is straightforward: you set up a signal handler for the SIGALRM signal and then you call alarm(nsec) (where nsec is the number of seconds until an alarm will be sent.

Example Program: alarm.c - This program installs a signal handler for SIGALRM and then calls alarm(5) both in main() and in the signal handler to create a recurring alarm. (Without the call to alarm in alrm_handler, it would be a one-shot timer.)

You can also use the setitimer function to set up a one-shot timer. Your book on p 368 explains it a little bit. The man page is actually clearer. This documentation is pretty good, too. The nuts and bolts of it are:

struct itimerval tv;

tv.it_value.tv_sec = 5; /* just like the '5' we passed to alarm() */
tv.it_value.tv_usec = 0;
tv.it_interval.tv_sec = 0;
tv.it_interval.tv_usec = 0;
setitimer (ITIMER_REAL, &tv, NULL);

Of course, you also need to set up a signal handler for SIGALRM beforehand as with alarm().

Recurring Timers

Occasionally, you want to be notified repeatedly after a certain amount of time has elapsed. Again, Unix uses the signal mechanism to send these notifications. The setup is similar to the previous:

struct itimerval tv;

tv.it_value.tv_sec = 5; /* how long until the first signal is sent */
tv.it_value.tv_usec = 0;
tv.it_interval.tv_sec = 5; /* interval between subsequent firings */
tv.it_interval.tv_usec = 0;
setitimer (ITIMER_REAL, &tv, NULL);

Your book on p. 368 says that you can use setitimer to handle three different types of signals. The only one I've found that works successfully on the lab server is ITIMER_REAL, so use that one.

Random Numbers

Random numbers have a variety of applications: cryptography, math (number theory), chemistry (stochastic simulations / enzymatic reactions), games (dice rolling, card shuffling, A.I.), and numerous other uses. Linux includes two special files: /dev/random and /dev/urandom for generating random numbers of higher entropy. It uses environmental noise from device drivers and other system sources into an entropy pool which is saved between reboots. It manages to generate pseudorandom numbers over an even distribution. (man urandom)

To get a random number in C, you basically do the following:

  1. Seed the random number generator by calling srand with some non-constant value. The current time and the current process id are commonly used values.
  2. Call rand() thereafter as many times as needed to retrieve a pseudo-random number.

Example Program: random.c - This program seeds the random number generator and then prints 10 random numbers.

Further Study

Date and Time TOC entry of the GNU C library.

Sleeping from the GNU C library documentation.

Setting an Alarm from the GNU C library documentation.

random.org - Offers true random numbers to anyone on the Internet. (Check out their web-based form.)

Your book, Chapters 17 and 18

Example programs on Gautama: date, rolldice

Lab 10

This will be a lab on timers and random numbers.

Description / Requirements

For this lab, I want you to write a program that prints a random number at regular intervals. I realize this is a contrived way of bringing together these two disparate topics. Do it anyway. Here's some steps to get you started:

Save this in a file called timer.c. The whole file should be about 30 or so lines long, including whitespace and curlies. Be sure to have a Makefile as well. Put both files in a directory called lab10.

Output

If you follow the directions above, you should get some output that looks like this: (lines in parentheses are not actually printed, but show how much time has passed.)

(one second passes)
SIGALRM: random number: 3
(five seconds pass)
SIGALRM: random number: 7
(five seconds pass)
SIGALRM: random number: 5
(five seconds pass)
SIGALRM: random number: 10
(five seconds pass)
SIGALRM: random number: 7
...

Tips

A note on srand(): A common misconception is that you can get really random numbers by seeding the random number generator again before every call to rand(). Please do not do this. The purpose of seeding the random number generator is to give it a starting point, and then allow it to generate pseudorandom numbers across an even distribution. By constantly re-seeding, you actually will produce numbers that are less random because they will not fall evenly across the distribution.

A note on alarm(): Do not call the alarm() syscall in your program at all. Calling setitimer() will install both the first and the recurring timer, obviating the need to call alarm().

Files

Here is a list of the files created in this lab.

Please turn in just the timer.c file with your name, lab # and class # at the top.