introduction to real-time software systems draft...

41
Introduction to Real-time software systems Draft Edition Jan van Katwijk Janusz Zalewski DRAFT VERSION of November 2, 1998

Upload: others

Post on 02-Jan-2021

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

Introduction to

Real-time software systems

Draft Edition

Jan van KatwijkJanusz Zalewski

D R A F T V E R S I O Nof

November 2, 1998

Page 2: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

Chapter 3

Time, clocks and timers

3.1 Introduction

The examples we have seen so far are not very typical for real-time programs. Asstated in the introduction of these notes, the class of real-time systems we want toconsider is ‘real-time’ , where real − time indicates that the response times of thesystems - aand therefore the constraints on the software - are bound by real-worldtime.

In real-time software, time is used for a variety of purposes:

• printing and otherwise manipulating calendar time;

• suspending the execution of the current thread (or process) for either a givenduration or until a calendar time arrives;

• signaling (other) processes after a given time or on a given calendar time.

The second point, suspending the execution of a process for a given time oruntil a calendar time is reached, is typically used in specifying periodical activitiesin cyclic tasks. The basic structure would look something like

....

loop

delay until the next iteration should start

do what is to be done

end loop

....

The third point, issuing a signal after a given duration or when a given calendartime is reached is typically used for issuing scheduled commands and for deadlineoverrun indication. The basic structure would look something like

...

delay until the deadline of the other task

if the other task is still running, signal the other task

....

In this chapter, we discuss the notion of time, as it appears in Ada, POSIX,DOS and Java. However, first we briefly discuss the notion of time and the notionof clock and the relation between the two.

3.2 Clocks and time

47

Page 3: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

48 CHAPTER 3. TIME, CLOCKS AND TIMERS

3.2.1 Views on time

Time, as occuring in the real world, may be seen as some metric consisting of a setof dense time values. If we look at systems exhibiting discrete behaviour, a moresparse approach to time values may be suitable.

Actual time is based on either

Astronomical Time Metric This metric is based on celestial observations, andfinds its relevance in the use of conventional calendars.

External Standard Physical Time This metric is based on devices that exploitthe periodicity of physical oscillations in e.g. crystals. Most computers haveclocks and timers based on such an oscillating mechanism.

We often assume that time, as maintained by a computer clock and ‘real-time’ areidentical, which is obviously not by definition the case.

3.2.2 Clocks and time

We define real time as the domain: Θ

Clock time corresponds with the representations of time as shown on clocks.Computer clocks are often based on oscillating devices, running autonomously.

An abstract model of a clock

Unlike real time, the set of values for a clock is discrete and enumerable. A clocktherefore can be defined by

(T, zero, next,∆)

where T is the set of time values, zero is a distinguished element, and next is aunary function, the successor function that, when applied to a clock value t givesthe successor value.

∆ indicates the granularity of the clock in terms of real time. The distance (interms of Θ) between a pair of elements (c1, c2), where both c1 and c2 are of typeT , then denotes the real-time interval between two measured time values, i.e.

||(c1, c2)|| = ∆ ∗ |measure(c1)−measure(c2)|

where

measure(zero) = 0

measure(next(c)) = 1 +measure(c)

A clock is able to map real time to an appropriate representation i.e. it can beconsidered a function from the real time to the clock time. We define C, a (partial)clock function then as

C : Θ→ T

Given such a function, we have the following requirements for clocks

1. clock time progresses with real time. I.e. for any pair of values (t1, t2) in Θ,it holds that

(t2 > t1 =⇒ C(t2) > C(t1))(C(t2) > C(t1) =⇒ t2 > t1)

in other words, the clock is monotonous.

Page 4: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.3. TIME IN ADA 49

2. the progression of the clock time must be relative to the progression of thereal time. I.e. it must hold for any two pairs (t1, t2) and (t3, t4) in Θ that

(t2 − t1 = t4 − t3) =⇒ (||(C(t2)− C(t1))|| − ||(C(t4) − C(t3))||) = x

where x is in (−∆, 0,∆).

In words: if two intervals in real time are the same, then in clock time theymight differ by at most 1.

3. ∆ is the smallest non-zero measure of time that can be observed using thisclock. This implies that when we are reading the clock in real time tr, thereading we get is the same as in any other value in some interval

(tr − α, tr + β)

where α+ β = δ.

4. The function C is partial in that it is only obliged to function properly in afinite interval of time, where the interval starts with some t0.

5. Clocks must make progress in at least every ∆ units in real time, i.e.

C(t+ ∆) > C(t)

Accuracy of computer clocks For an accurate clock, it holds that the progressof the clock is the same as the progress of the real time. In order to discuss therelation between the progression of the real time vs progression of the real time, weintroduce the notion of required accuracy of a clock ∇.

Item 5 from the list above then should be reformulated for an ideal clock:∀i.C(t + i ∗ ∇) = C(t) + i ∗∆

In words: the clock reading of a time t+ i ∗∇ is the same as the reading of theclock for the time t plus i ∗∆.

The accuracy of a clock then can be given by∇∆

1. If ∇∆ > 1, then the clock is slow, i.e. in some point of time more real time willhave been passed than shows in the clock reading;

2. If ∆∇ < 1 then the clock is fast, i.e. in some point of time clock reading will

advance real time.

3.3 Time in Ada

3.3.1 Plain Time

Ada provides a package Calendar that provides the basic functionality for dealingwith time.

package Ada. Calendar is

type Time is private;

subtype Year_Number is Integer range 1901 .. 2099;

subtype Month_Number is Integer range 1 .. 12;

subtype Day_Number is Integer range 1 .. 31;

subtype Day_Duration is Integer range 0.0 .. 86_400.0;

function Clock return Time;

Page 5: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

50 CHAPTER 3. TIME, CLOCKS AND TIMERS

function Year (Date : Time) return Year_Number;

function Month (Date : Time) return Month_Number;

function Day (Date : Time) return Day_Number;

function Seconds (Date : Time) return Day_Duration;

function Time_Of (Year : Year_Number;

Month : Month_Number;

Day : Day_Number;

Seconds : Day_Duration := 0.0);

function "+" (Left : Time;

Right : Duration) return Time;

function "-" (Left : Time;

Right : Duration) return Time;

function "-" (Left : Time;

Right : Time ) return Duration;

...

end Ada. Calendar;

The type time represents the calendar time, the type duration represents valuesfor time differences. The representation of calendar time is not visible, however,there are plenty of operations for injection and projection. Furthermore, values oftype duration can be added to and substracted from T ime, while the differencebetween times is also defined and expressed in terms of type duration.

The value Day Duration′small has the role of ∆ (see section 3.2) here, it definesthe successor function for values of the type.

We do not have a possibility to determine the accuracy of time in an Adaaprogram, other than comparing readings of the time performed in an Ada programwith readings from the real time.

Reading calendar time In Ada, calendar time is obtained by executing thefunction Clock. The result value is of type time. Projections of time can be madeto printable values.

with Ada. Calendar; use Ada. Calendar;

with text_io; use text_io;

procedure print_now is

now: time := Clock;

begin

put_line ("Current date is " &

Year_Number’ image (Year (now)) & " " &

Month_Number’ image (Month (now)) & " " &

Day_Number’ image (Day (now)) );

put_line ("Current time (in seconds from midnight) is " &

Day_Duration ’ image (Seconds (now)) );

end;

The output of this programme is something like

Current date is 1997 8 4

Current time (in seconds from midnight) is 62969.000232943

3.3.2 Measuring durations between events

The standard way in Ada to measure a duration is by twice measuring the timeand by taking the difference, as in the following example

procedure diff is

...

pre_time := Calendar. Clock;

-- do what is to be done

post_time := Calendar. Clock;

Ada. Text_Io. Put_Line ("It took " &

Day_Duration’ image (post_time - pre_time));

...

Page 6: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.3. TIME IN ADA 51

A serious drawback with the current Gnat implementation under DOS is thataccuracy of clock readings is limited to 100 msec. Accuracy of the durations aretherefore of the same magnitude.

3.3.3 Suspending execution

Ada supports both a delay and a delay until construct to suspend the execution ofthe current task. The delay takes a duration as argument, the delay until takes acalendar time as argument.

delay 10.0;

The execution of the task, executing this statement, is delayed for at least 10seconds. Although, after passing the delay, the task is made runnable again, it isthe availability of a processor that determines whether or not the task will actuallyrun.

delay until some_time;

where some time is some calendar time. The execution of the task executingthis statement is delayed until the clock reaches the specified time, after which thetask is made runnable again. Again, whether or not the task will actually rundepends on the availability of a processor.

Given the delay and delay until construct, a periodical task is easy to construct.

task p;

task body p is

-- declarations

starting_time : calendar.time := ...;

next_start : calendar.time := starting_time;

begin

while some_condition loop

delay until next_start;

-- do what is to be done

next_start := next_start + PERIOD;

end loop;

end;

Using a simple plain delay is obviously wrong, as shown below

.....

procedure badloop is

period : day_duration := 10.0;

dummy : integer;

begin

loop

delay period;

Put_Line ("Execution starts at "&

Day_Duration’ image (Seconds (Clock)));

for i in 1 .. 100_000 loop

for j in 1 .. 100 loop

dummy := i + j;

end loop;

end loop;

end loop;

end;

Even a more elaborate loop, recalculating the subsequent starting times maycause jitter.

....

procedure jitter is

period : Day_Duration := 5.0;

Page 7: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

52 CHAPTER 3. TIME, CLOCKS AND TIMERS

starting_time : time := Clock;

our_delay : Day_Duration := 0.0;

dummy : integer;

begin

for i in 1 .. 10 loop

delay our_delay;

Put_Line ("Execution starts at "&

Day_Duration’ image (Seconds (Clock)));

for j in 1 .. 100_000 loop

for k in 1 .. 100 loop

dummy := i + j + k;

end loop;

end loop;

our_delay := starting_time + i * period - Clock;

end loop;

end;

The variable our delay contains the difference in time between the next startingpoint and the current time. If the task, executing this code, is for whatever reasonsuspended before the value of our delay is calculated, but after the clock value iscomputed, the specified delay time does not accurately reflect what was meant.

3.3.4 Asynchronous Transfer of control

The main construct for asynchronous signaling in Ada is the ATC, already met inchapter 2. The construct look like

....

select

delay until some_calendar_time;

-- do here the cleaning up actions

then abort

-- action to be killed at a given time

end select

...

If in the above example the clock reaches the specified time before terminatingthe execution of the abort part the execution of the abort part is indeed aborted.Notice that if the execution of the abort part was already terminated, execution ofthe whole constructed was already terminated, so no further killing is needed.

The construct is typically used to build a signaling mechanism, where one threadsignals another when a timer alarms. As an example, we build a safe task, that isa task that ensures that there is no deadline overrun. A more or less standardizedstructure for a task responsible for managing its own overrun is given below

with Ada. Calendar;

use Ada. Calendar;

with text_io; use text_io;

procedure safetask is

task safe_task;

task body safe_task is

my_period : day_duration := 2.0;

my_deadline : day_duration := 1.5;

start_time : time := Clock;

dummy : integer;

begin

for i in 1 .. 10 loop

delay until start_time;

put_line ("Current time (in seconds from midnight) is " &

Day_Duration ’ image (Seconds (Clock)) );

select

delay until start_time + my_deadline;

Put_Line("Too bad, task missed deadline");

Page 8: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.3. TIME IN ADA 53

then

abort

for j in 1 .. 100_000 loop

for k in 1 .. 100 loop

dummy := 1 + j + k;

end loop;

end loop;

end select;

start_time := start_time + my_period;

end loop;

end;

begin

null;

end;

For data transfer between the periodical actor task and the rest of the world,we want to avoid using the rendez-vous mechanism, since the periodicity of theperiodical task may be influenced as is shown below.

task body wrong_task is

....

begin

accept start;

loop

select

delay until next_period;

do useful periodical work

or

accept data_output (f: out data) do

.....

end;

end select;

end loop;

end;

We therefore prefer the use of a buffer, that will be implemented using protectedobjects. The template for a periodical task that makes data available to others usinga buffer is given below. Since we want control over the execution of the task, weadd additional entries for starting and stopping the task.

protected buffer is

procedure put_data (d: data);

entry get_data (d: out data);

private

available : boolean := false;

buf: data;

end;

protected body buffer is

procedure put_data (d: data) is

begin

buf := d;

available := True;

end;

entry get_data (d: out data) when available is

begin

d := buf;

available := False;

end;

end;

task periodical is

entry start;

entry stop;

end;

task body periodical is

looping: Boolean := True;

Page 9: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

54 CHAPTER 3. TIME, CLOCKS AND TIMERS

begin

accept start;

while looping loop

select

delay until next_period;

do something useful;

buffer. put_data (...);

or

accept stop do

looping := False;

end;

end select;

end loop;

end;

Ada 83 and ATC Although Ada 83 did not support the ATC construct, we canuse a very crude mechanism to obtaining some of the effects. We create two tasks,one task will act as a watch dog for the other, killing the other if the triggeringevent occurs.with Ada. Calendar;

use Ada. Calendar;

with text_io; use text_io;

procedure ada83 is

task type under_control;

type taskp is access under_control;

task killer;

task body under_control is

dummy : integer;

begin

put_line ("Current time (in seconds from midnight) is " &

Day_Duration ’ image (Seconds (Clock)) );

for j in 1 .. 1000 loop

for k in 1 .. 10000 loop

dummy := 1 + j + k;

delay 0.0;

end loop;

end loop;

end;

task body killer is

my_period : day_duration := 2.0;

my_deadline : day_duration := 1.5;

start_time : time := Clock;

x : taskp;

begin

for i in 1 .. 10 loop

delay until start_time;

x := new under_control;

delay until start_time + my_deadline;

if not x’terminated

then

abort x. all;

Put_Line ("Alas, I had to kill the task ");

end if;

start_time := start_time + my_period;

end loop;

end;

begin

null;

end;

The thread of the killer task suspends execution until a certain time is reached. Ifpriority of the killer task is sufficiently high, it will regain control over the processorand it will kill the task x.all. The attribute terminated indicates whether or notthe task, on which the attribute is applied, is still alive in which case abortion ofthe task will take place.

Page 10: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.4. TIME IN POSIX.1 55

3.4 Time in POSIX.1

3.4.1 Introduction

In this section we discuss the calendar time as maintained by POSIX, we discusstiming the execution of a process, suspending the execution of a process for a giventime, and we discuss some timers as they appear in POSIX. The examples we giveare encoded in C.

3.4.2 Calendar time in POSIX

The time call is standard in POSIX, the function returns the number of secondssince the world - according to UNIX - was created, what happened at january 119701.

#include <time.h>

time_t time (time_t *what_time_it_is);

The value returned is the number of seconds since january 1 1970, the samevalue is stored in the location, pointed to by the parameter. The type time t is arenaming of long. So, printing the number of seconds since 1970 is by e.g.

# /* time2.c */

#include <time.h>

main() {

printf ("The time is %d\n", time (NULL));

}

The function gettimeofday extends the classic ‘time’ function, it fills a structuretimeval that has two fields, a second field, giving the number of seconds since 1970,and a microsecond field.

struct timeval {

time_t tv_sec; /* seconds since starting of the world */

time_t tv_usec; /* micro seconds */

};

In the following program fragment, we use both time and gettimeofday.

# /* time3.c */

#include <time.h>

#include <sys/time.h>

main() {

struct timeval t;

printf ("The time is %d\n", time (NULL));

gettimeofday (&t, NULL);

printf ("The time is %d %d\n", t. tv_sec, t. tv_usec);

}

which gives results like

The time is 870720825

The time is 870720825 116516

Not all POSIX implementations provide a micro-second level granularity.

1If we assume a 32 bit value for time values, the Y2K problem does not occur, there will be aproblem somewhere around 2030 however

Page 11: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

56 CHAPTER 3. TIME, CLOCKS AND TIMERS

Conversion of representation To ease life a little bit, POSIX provides functionsto convert times from binary to ASCI representations and vice versa.• char *ctime (const time_t *timep);

ctime translates the time, passed as number of seconds since 1970, into anASCI string;

• struct tm *gmtime (const time_t *timep)

gmtime translates the time, passed as number of seconds since 1970, into abroken-down time structure

struct tm {

int tm_sec; // seconds

int tm_min; // minutes

int tm_hour; // houres (today)

int tm_mday; // day of month

int tm_mon; // month

int tm_year; // year

int tm_wday; // day of week

int tm_yday; // day of year

int tm_isdst; // daylight saving time

};

• char *asctime (const struct tm timeptr);

asctime takes a tm structure and translates it into an ASCI string;

• time_t mktime (struct tm *timeptr);

mktime takes a pointer to a tm structure and translates its value into a time tvalue, the number of seconds since januari 1970.

We exemplify the use of the functions by a small example. For a detailed de-scription of the various functions, the reader is referred to POSIX manual pages.#

/*

* demonstrating the use of various

* time functions

* h3.new/posix/asctime.c

*/

#include <stdio.h>

#include <time.h>

main () {

struct tm *p2;

time_t temp = time (NULL);

printf ("The time is %d\n", temp);

printf ("In ASCI is the time %s", ctime (&temp));

p2 = gmtime (&temp);

printf ("tm_sec = %d\ntm_min = %d\ntm_hours = %d\n",

p2 -> tm_sec, p2 -> tm_min, p2 -> tm_hour);

printf ("In ASCI is the time %s", asctime (p2));

printf ("In time_t is the time %d\n", mktime (p2));

}

which results in something likeThe time is 871834008

In ASCI is the time Sun Aug 17 15:06:48 1997

tm_sec = 48

tm_min = 6

tm_hours = 16

In ASCI is the time Sun Aug 17 16:06:48 1997

In time_t is the time 871837608

Notice the difference in the two representations of the time. The tm structurehas an indication of the daylight saving time flag that is used in the conversion,using asctime.

Page 12: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.4. TIME IN POSIX.1 57

3.4.3 Measuring time differences

Measuring durations is as in e.g. Ada, by twice reading the time and computingthe time difference.

#

/*

* Measuring a duration

* C compiler: GCC

* h3/posix/duration.c

*/

#

#include <time.h>

#include <sys/time.h>

void timediff (struct timeval *first, struct timeval *last, struct timeval *res) {

if (last -> tv_usec > first -> tv_usec) {

res -> tv_usec = 1000000 + first -> tv_usec - last -> tv_usec;

res -> tv_sec = first -> tv_sec - last -> tv_sec - 1;

}

else {

res -> tv_usec = first -> tv_usec - last -> tv_usec;

res -> tv_sec = first -> tv_sec - last -> tv_sec;

}

}

main() {

int dummy;

struct timeval now, later, res;

gettimeofday (&now, NULL);

printf ("Time is %d %d\n", now. tv_sec, now. tv_usec);

for (i = 1; i <= 100000; i ++)

dummy = 1;

gettimeofday (&later, NULL);

printf ("Time is now %d %d\n", later. tv_sec, later. tv_usec);

timediff (&later, &first, &res);

printf ("Difference is %d %d \n", res. tv_sec, res. tv_usec);

}

3.4.4 Measuring process execution times

The above mentioned functions relate to the real-world time. POSIX provides afunction times that fills a buffer (of type struct tms) with four values related tothe process execution time used so far:

• the user time, i.e. the time spent in executing the process in user mode;

• the system time, i.e. the time spent in executing the process in system mode;

• the user time of all child processes of the process, and

• the system time of all child processes of the process.

The structure tms is defined as

struct tms {

time_t tms_utime; // user time

time_t tms_stime; // system time

time_t tms_cutime; // user time of children

time_t tms_cstime; // system time of children

};

times is a function that fills a structure of type tms, and returns the numberof clockticks that have elapsed since the system has been up, its profile reads asfollows:

Page 13: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

58 CHAPTER 3. TIME, CLOCKS AND TIMERS

#

#include <time.h>

#include <sys/times.h>

time_t times (struct tms *val);

An example of its use is given below:

#

/*

* Measuring a duration

* C compiler: GCC

* h3/posix/dur2.c

*/

#

#include <time.h>

#include <sys/times.h>

main() {

int dummy, i;

struct tms now, later;

dummy = times (&now);

printf ("times gives %d \n", dummy);

printf ("User (system) time sofar is %d (%d)\n", now. tms_utime, tms_stime);

for (i = 1; i <= 100000; i ++)

dummy = 1;

dummy = times (&now);

printf ("times gives %d \n", dummy);

printf ("User (system) time sofar is %d (%d)\n", now. tms_utime, tms_stime);

}

The results of execution are

time gives 1015940

User (system) time sofar is 1 (0)

time gives 1015941

User (system) time sofar is 2 (0)

Notice that the actual value of a ‘clock-tick’ is system defined.

3.4.5 Suspending execution in POSIX.1

POSIX.1 provides a sleep function to suspend execution for a given period. Thespecification reads

#include <unistd.h>

unsigned int sleep (unsigned int seconds);

The sleep function causes the current process to be suspended until either thenumber of seconds specified is passed, or until a signal arrives (see section 3.4.6).In the former case, it returns 0, in the latter case, it returns the number of secondsleft. An implementation of a delay function, that delays execution of the currentprocess for a given amount of time, regardless of the signals that arrive, then is

void Delay (int d) {

while ((d = sleep (d)) > 0);

}

If a process is to be suspended indefinitely, a pause function is to be used. Thisfunction causes the process to be suspended until a signal arrives.

Page 14: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.4. TIME IN POSIX.1 59

3.4.6 Intermezzo: signal handling in POSIX

Signals Signals is a mechanism in POSIX with which it is possible to implementasynchronous events. In general, a signal is sent to a process, and depending onthe reaction specified, the signal is handled. As an example, it is fairly common tohave a process execution being stopped on the signal associated to pressing Ctrl-Con the keyboard.

A variety of signals POSIX defines a variety of signals, an overview is given intable 3.1.

Name Cause Default actionSIGHUP Dial-in line hangs ExitSIGINT Terminal interrupt (Ctrl-C) ExitSIGKILL Kill process command ExitSIGSEGV memory reference fault AbortSIGALRM Time expired ExitSIGUSR1 User defined ExitSIGBUS Bus Error AbortSIGFPE Arithmetic exception AbortSIGCHLD Cild terminates IgnoreSIGILL Illegal instruction AbortSIGSTOP Process suspension StopSIGCONT Process resumption Continue

Table 3.1: POSIX signals

Exit as action indicates that the process receiving the signal will terminate,abort indicates that the process will terminate and a core dump of its process spaceis made. Ignore indicates that the process will just ignore the signal. Stop andContinue are actions to be used in debugging, a Stop signal will cause the processto stop, making the process space available for inspection and modification of textand data, while Continue will cause a stopped process to continue.

Handling signals A user might specify that rather than letting an action asspecified in table 3.1 is performed, a user defined action is to be executed. Bindinga user-defined signal to an action is done through the sigaction call, it’s profilereads

#include <signal.h>

int sigaction (int signum, const struct sigaction *act, struct sigaction *oldact);

If act is non-null, the new action for signal signum is installed from the sigactionstructure, pointed to by act. If oldact is a non-void pointer, the previous action issaved in the sigaction structure pointed to by oldact. The sigaction structure isdefined as

struct sigaction {

....

void (*sa_handler)(int);

sigset_t sa_mask;

int sa_flags;

void (*sa_restorer)(void);

};

Page 15: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

60 CHAPTER 3. TIME, CLOCKS AND TIMERS

The sa handler field is a pointer to the function that will be called when thesignal occurs. Typical predefined handler functions are

#define SIG_DFL (void *())0

#define SIG_IGN (void *())1

SIG DFL represents the default action (specified in table 3.1) to be taken onoccurrence of the signal. SIG IGN indicates that the signal should be ignored.

User-defined handlers are implemented as simple void functions. A handlerwhether it is a user-defined handler or not, is called whenever the signal occurs.A handler gets as parameter value the numerical representation of the signal, thehandler may use that value.

void a_handler (int signo) {

// do something useful

}

In the structure sigaction, sa mask specifies the set of signals to be blockedfor the duration of the execution of the signal handler. To avoid recursive signalhandling, the signal that is being handled is put in this set. The set can be clearedby using the operation sigemptyset. Signals can be added to the set with theoperation sigaddset

sigemptyset (&my_set);

sigaddset (&my_set, my_signal);

sa flags is the bitwise OR of zero or more flags, taken from a set to which thefollowing two belong:

SA ONESHOT Restore the signal action to the default state, once the signalhandler has been called. This is the default behaviour.

SA INTERRUPT Do not restore the signal action once the handler is called.This behaviour is typical for periodical tasks.

If we assume that s is a structure, sufficiently initialized, the standard way ofcalling sigaction is

if (sigaction (SIGALRM, &s, NULL) == -1) {// something went wrong

perror ("Sigaction"); //report the fault

exit (1);

}

So, a user program may set a handler for catching a signal, and it even maychange the handler. We demonstrate the use of handlers in a program where thebehaviour on the occurrence of a Ctrl-C signal will be dynamically changed. Inthis program, first the Ctrl-C signal will be ignored, then bound to a user definedhandler and finally bound to the original handler.

#

/*

* Simple demonstration for signal handlers

* h3/posix/handlers1.c

*/

#include <stdio.h>

#include <signal.h>

The handler itself is kept simple

void my_handler (int signo) {

printf ("I caught Ctrl-C %d\n", signo);

}

Page 16: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.4. TIME IN POSIX.1 61

The main program consists of several parts. In the first part variables aredeclared. Signal handling is still unaffected.

void main () {

struct sigaction action1,

action2;

int i, j;

printf ("If you hit Ctrl-C in 5 seconds, I’m gone\n");

sleep (5);

Next, a handler for handling Ctrl-C is installed that causes the Ctrl-C to beignored. Notice that the old handler is stored in action2.

sigemptyset (&action1. sa_mask);

action1. sa_handler = SIG_IGN;

action1. sa_flags = SA_INTERRUPT; // keep restarting

if (sigaction (SIGINT, &action1, &action2) == -1) { // something went wrong

perror ("sigaction");

exit (1);

}

printf ("Control C is out for 5 seconds\n");

sleep (5);

Execution of the next action causes a user-defined handler to be bound to thesignal. This handler will cause a message to be printed to the screen.

sigemptyset (&action1. sa_mask);

action1. sa_handler = &my_handler;

action1. sa_flags = SA_INTERRUPT; // keep restarting

if (sigaction (SIGINT, &action1, NULL) == -1) { // something went wrong

perror ("sigaction");

exit (1);

}

printf ("Control C is now caught by a user handler\n");

sleep (5);

Next, the original handler, saved in action2 is restored.

if (sigaction (SIGINT, &action2, NULL) == -1) {

perror ("sigaction");

exit (1);

}

printf ("Control C is back again\n");

sleep (5);

printf ("That’s all folks\n");

}

3.4.7 Timers: Signaling an Alarm

POSIX.1 supports a simple alarm timer that issues a signal SIGALRM when itexpires.

#include <unistd.h>

#include <signal.h>

int alarm (int interval);

The standard way of setting the alarm timer is

if (alarm (INTERVAL) == -1) { // error occurred

perror ("Alarm");

exit (1); // most likely unrecoverable

}

Page 17: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

62 CHAPTER 3. TIME, CLOCKS AND TIMERS

In the following program, we use the alarm timer for generating an interruptonce every INTERVAL seconds. The handler itself re-sets the timer to achieverepetition.

#

/*

* demonstration program for alarm

* timer

* h3/posix/ctimer1.c

*/

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

#define INTERVAL 5

void TimeHandler (int sigNo) {

alarm (INTERVAL);

printf ("From handler with signal %d\n", sigNo);

}

In the main program, we set the alarm. We use the SA INTERRUPT flag toindicate that it is more than a one shot operation.

void main () {

struct sigaction action;

int i, j;

sigemptyset (&action. sa_mask);

action. sa_handler = (void *)TimeHandler;

action. sa_flags = SA_INTERRUPT; // keep restarting

if (sigaction (SIGALRM, &action, 0) == -1) { // something went wrong

perror (sigaction);

return 1;

}

if (alarm (INTERVAL) == -1) {

perror ("alarm");

exit (1);

}

while (1) {

printf ("From main process\n");

for (i = 0; i < 200000; i ++)

j = i * i;

}

}

The setitimer The major drawbacks of alarm as timer are

• the resolution of the alarm time is coarse grained (1 second);

• only a single clock is available.

The ‘alarm’ existed already in early UNIX versions, later on additional timers weredefined in POSIX. Two imporant timer functions are getitimer and setitimer.

#include <sys/time.h>

int setitimer (int which, const struct itimerval *value, struct itimerval *ovalue);

int getitimer (int which, struct itimerval *cvalue);

which indicates the timer being used, it may take the following values

ITIMER REAL causes the timer to decrement in real-time. Expiration of thetimer causes a SIGALRM signal to be raised.

Page 18: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.4. TIME IN POSIX.1 63

ITIMER VIRTUAL causes the timer to decrement in virtual time, i.e. it runsonly when the process is executing. Expiration of the timer causes a SIGV-TALRM signal to be raised.

A POSIX process may simultaneously use an alarm timer and a timer for eachwhich value.

value, cvalue and ovalue are pointers to structures. In setitimer, value definesthe new value(s) for the timer, the function fills the structure pointed to by ovaluewith the old timer values. In getitimer the function fills the structure pointed toby cvalue with the remaining time for the timer to expire.

The itimerval struct contains membersstruct timeval it_value; // current value

struct timeval it_interval; // timer interval

Recall that the timeval structure itself consists of

struct timeval {

time_t tv_sec; /* seconds since the start of the universe */

time_t tv_usec; /* micro seconds */

};

it value, when set to a non-zero value, gives the time (either relative or absolute,depending on the setting of some flags) when the timer expires for the first time. Ifit value is zero, however, the timer is off.

it interval, when set to a non-zero value, gives the time (always relative) whenthe timer expires for the second, third etc times. If it interval is set to zero, norepetition in experiration will occur.

Whether or not it makes sense to have a value larger than 1000000 in the tv usecfields depends on the underlying system.

An example As example of the use of timers, we create a program where we settwo timers, one for the IT IMER REAL clock and one for the IT IMER V IRTUALclock. We define a single handler to catch the signals, the handler itself will dispatchthe appropriate actions based on the signals.

#

/*

* h3/posix/ctimer2.c

*/

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

#include <sys/time.h>

#define INTERVAL 5

void TimeHandler (int sigNo) {

if (sigNo == SIGALRM) {

printf ("Got a SIGALRM signal (%d)\n", sigNo);

}

else {

printf ("Got a SIGVTALRM signal (%d)\n", sigNo);

}

}

The handler gets as parameter the signal number, that is being used in dispatch-ing the right action.

void main () {

struct itimerval val1, val2;

struct sigaction action1,

action2;

int i, j;

Page 19: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

64 CHAPTER 3. TIME, CLOCKS AND TIMERS

Below, we set the two signals. One relates to SIGALRM , the other to SIGV TALRM .

sigemptyset (&action1. sa_mask);

action1. sa_handler = (void *)TimeHandler;

action1. sa_flags = SA_INTERRUPT; // keep restarting

if (sigaction (SIGALRM, &action1, 0) == -1) { // something went wrong

perror ("sigaction");

exit (1);

}

sigemptyset (&action2. sa_mask);

action2. sa_handler = (void *)TimeHandler;

action2. sa_flags = SA_INTERRUPT; // keep restarting

if (sigaction (SIGVTALRM, &action2, 0) == -1) {

perror ("sigaction");

exit (2);

}

Next, we set the timers. The initial value for expiration (relative) is set toINTERVAL− 1 for the IT IMER REAL clock, and to INTERV AL+ 1 for theIT IMER V IRTUAL clock. The periodic expiration is set to the same values.

val1. it_interval. tv_sec = INTERVAL - 1;

val1. it_interval. tv_usec = 0;

val1. it_value. tv_sec = INTERVAL - 1;

val1. it_value. tv_usec = 0;

setitimer (ITIMER_REAL, &val1, 0);

val2. it_interval. tv_sec = INTERVAL + 1;

val2. it_interval. tv_usec = 0;

val2. it_value. tv_sec = INTERVAL + 1;

val2. it_value. tv_usec = 0;

setitimer (ITIMER_VIRTUAL, &val2, 0);

Now, the timers are running, the only thing we have to ensure is that the processremains running long enough to be be interrupted by both timers.

while (1) {

printf ("From main process\n");

for (i = 0; i < 200000; i ++)

j = i * i;

}

}

Sending a signal Timer signals are typically kept within a process. However, aprocess can send a signal to (another) process using the function kill

int kill (pid_t pid, int sig);

A process, implementing a time out for another process can then be implementedas follows:

void killer (int signo) {

if (signo == SIGALRM) {

kill (OTHER_PROCESS, SIGINT);

done = TRUE;

}

}

void main () {

struct itimerval val1;

struct sigaction action1;

int done = FALSE;

sigemptyset (&action1. sa_mask);

action1. sa_handler = (void *)killer;

action1. sa_flags = SA_ONESHOT;

Page 20: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.4. TIME IN POSIX.1 65

if (sigaction (SIGALRM, &action1, 0) == -1) {

perror ("sigaction");

exit (1);

}

val1. it_interval. tv_sec = DELAY_VALUE;

val1. it_interval. tv_usec= 0;

setitimer (ITIMER_REAL, &val1, 0);

pause ();

}

Waiting for a signal A pause call causes the current process to wait until a signalis received. A more elaborate mechanism for delaying execution until a signal arrivesis by applying the function sigsuspend

int sigsuspend (const sigset_t *sigmasks)

So, in the example above, a more refined specification would be

sigset_t help;

...

sigemptyset (&help);

sigaddset (&help, SIGALRM);

...

sigsuspend (&help);

After execution of this function, execution of the current process is suspendeduntil one of the signals, specified in the signal set arrives.

3.4.8 Building periodical processes in POSIX.1

As we have seen in the Ada case (section 3.3.4), the construction of a safe periodicalprocess requires two constructs to be available. We need to be able to specify a delayin execution, preferably related to calendar time, and we need to be able to interruptthe execution of the process when it overruns its deadline.

Intermezzo: Influencing control flow Asynchronous transfer of control can besimulated in POSIX using a combination of signal handling and a longjmp/setjmpconstruct. The longjmp/setjmp provides the functionality of a non-local goto.

A direct call to setjmp causes the context of the call to setjmp to be storedin a buffer. A call to longjmp causes the execution to continue at the contextavailable in the buffer passed as parameter. This context represents a call to setjmp,so execution of a longjmp call causes a second return from a setjmp call. Todistinguish between the ‘normal’ return and the return caused by the execution ofthe longjmp, the former returns a zero value, the latter returns a non-zero returnvalue.

// declare something to contain context information

jmp_buf buffer;

....

if (setjmp (buffer) == 0) {// we now just filled the buffer

// here we end up in a normal execution sequence

....

longjmp (buffer, 1);

}

else

// we end up here after a longjump is executed

Page 21: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

66 CHAPTER 3. TIME, CLOCKS AND TIMERS

The example shows the use of the return value of the setjmp function. Alongjump has as target the return of the setjmp that filled the buffer, with a returnvalue of 1.

Combining the longjmp/setjmp with a signal gives us the required functionalityas is demonstrated in the following periodical task. In this task, reaching thedeadline while running the code of the body causes the handler timeouthandler tobe activated, This handler will cause a longjmp to take place to clean up code. Ifexecution of the code of the body terminates before the deadline is reached, thetimer is switched off, so no handler will be activated.

Execution of the process will be delayed until the arrival time is reached, thedelay is effectuated by a function Delay as defined earlier.

#

/*

* modeling a safe periodical task

* h3/posix/timeout.c

*/

#include <stdio.h>

#include <unistd.h>

#include <setjmp.h>

#include <signal.h>

#include <sys/time.h>

jmp_buf timeoutpoint;

// Timeout handler

void timeouthandler (int signo) {

longjmp (timeoutpoint, 1);

}

int task_to_be_timed (int period, int deadline, int maxruns) {

struct itimerval val;

struct sigaction action1, action2;

int i, j;

int start_time;

time_t now = time (NULL);

for (i = 1; i <= maxruns; i ++) {

start_time = now + (i - 1) * period;

sigemptyset (&action1. sa_mask);

action1. sa_handler = (void *)timeouthandler;

action1. sa_flags = SA_ONESHOT; // just for this iteration

sigaction (SIGALRM, &action1, &action2);

// compute deadline - now

val. it_interval. tv_sec = 0; // do not bother

val. it_interval. tv_usec = 0;

val. it_value. tv_sec = start_time + deadline;

val. it_value. tv_usec = 0;

setitimer (ITIMER_REAL, &val, 0);

Delay (start_time - time (NULL));

// now we set up the handler

if (setjmp (timeoutpoint) == 0) { // normal thread

// do the task, we simulate it:

.....

// yes, we made it, reset handler

sigaction (SIGALRM, &action2, 0);

// and go for the next iteration

}

else { // we came here through a long jump,

// so we missed our deadline

printf ("We missed our deadline in iteration %d\n", i);

//restore state here for next iteration

}

}

}

Page 22: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.5. PROGRAMMABLE TIMERS IN POSIX.1B 67

3.4.9 Sleeping small periods

Apart from sleep, that suspends execution for a given number of seconds, POSIXprovides a function for sleeping a smaller amount of time, the nanosleep function.nanosleep and sleep are essentially the same, although nanosleep has a finer gran-ularity.

struct timespec rest, time_left;

int i;

i = nanosleep (&rest, &time_left);

timespec resembles timeval, the second field indicates the number of nanosec-onds rather than the number of microseconds.

struct timespec {

long tv_sec;

long tv_nsec;

};

The process executing the nanosleep will sleep for the amount of time spent inrest. If, due to a signal, the sleep is interrupted, the second argument is filled withthe value indicating the remaining time to rest. In this case, the return value of thefunction is −1, and errno is set to EINTR.

Similar to what we did earlier, a Delay can be constructed that causes theprocess to sleep for the specified amount of time.

void Delay (int seconds, int nanoseconds) {

struct timespec time_left;

time_left. tv_sec = seconds;

time_left. tv_nsec = nanoseconds;

while (time_left. tv_sec != 0 || time_left. tv_nsec != 0) {

nano_sleep (&time_left, &time_left);

}

}

3.5 Programmable timers in POSIX.1b

3.5.1 Clocks and functionality

POSIX.1b defines a new set of timer functions on one or more clocks. POSIX sys-tems are obliged to provide at least the clock CLOCK REALTIME. POSIX.1bfunctionality defines functions for getting and setting these clocks and it providesfunctionality for the construction and the use of timers using these clocks. A sum-mary is given below

#include <signal.h>

#include <time.h>

int clock_settime (clockid_t clock_id, const struct timespec *current_time);

int clock_gettime (clockid_t clock_id, struct timespec *current_time);

int clock_getres (clockid_tt clock_id, struct timespec *resolution)

int timer_create (clockid_t clock_id,

const struct sigevent *signal_specification,

timer_t *timer_id);

int timer_settime (timer_t timer_id, int flags,

const struct itimerspec *new_interval,

Page 23: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

68 CHAPTER 3. TIME, CLOCKS AND TIMERS

struct itimerspec *old_interval);

int timer_gettime (timer_t timer_id, struct itimerspec *cur_interval);

int timer_getoverrun (timer_t timer_id);

int timer_delete (timer_t timer_id);

int nanosleep (const timespec *requested_time_interval,

struct timespec *time_remaining);

What time is it To get the time according to a given clock, we call the functionclock gettime.

#include <time.h>

struct timespec current_time;

(void) clock_gettime (CLOCK_REALTIME, &current_time);

Recall that timespec was defined as

struct timespec {

long tv_sec;

long tv_nsec;

};

Accuracy of clocks The resolution of the clock can be obtained by clock getres,defined as

int clock_getres (clockid_t clock_id, struct timespec *resolution);

Consider as an example

main () {

struct timespec resolution;

clock_getres (CLOCK_REALTIME, &resolution);

printf ("The resolution on our system is %d micro seconds\n",

resolution. tv_nsec);

}

Setting the time If we want to set the time of a particular clock, we apply thefunction clock settime, which was defined as

int clock_settime (clockid_t clockid, const struct timespec *new_time);

So, setting the clock at 12.00 , january 24 1999, takes the following:

struct tm my_time;

my_time. tm_sec = 0;

my_time. tm_min = 0;

my_time. tm_hour = 12;

my_time. tm_mday = 24;

my_time. tm_mon = 1;

my_time. tm_year = 1999;

clock_settime (CLOCK_REALTIME, mktime (&my_time));

3.5.2 POSIX interval timers

POSIX interval timers extend the getitimer and setitimer functionality that wehave seen before. The functional interface to the timers was already given, and is

Page 24: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.5. PROGRAMMABLE TIMERS IN POSIX.1B 69

int timer_create (clockid_t clock_id,

const struct sigevent *signal_specification,

timer_t *timer_id);

int timer_settime (timer_t timer_id, int flags,

const struct itimerspec *new_interval,

struct itimerspec *old_interval);

int timer_gettime (timer_t timer_id, struct itimerspec *cur_interval);

int timer_getoverrun (timer_t timer_id);

int timer_delete (timer_t timer_id);

The functions use a struct itimerspec rather than the struct itimerval that wehave been using before.

itimerspec is defined as

struct itimerspec {

struct timespec it_value;

struct timespec it_interval;

};

Recall that timespec was defined as

struct timespec {

long tv_sec;

long tv_nsec;

};

where the tv nsec field indicates a nano-second accuracy rather than a microsecond accuracy.

As in the case of the getitimer example, it value and it interval have as mean-ing:

• time until the first time the timer expires,

• time between the n− th and (n+ 1)− th time the timer expires.

Creating timers The function timer create allows the dynamic creation of new(interval) timers, based on a given clock. The clock on which the timer is createdis passed as first argument. The second argument is a pointer to a struct sigeventwhich is used to define the signal to be raised on expiration of the timer. The thirdargument is a pointer to a structure containing the id of the newly created timer.

#include <signal.h>

#include <time.h>

....

int i;

timer_t new_timer;

struct sigevent the_signal;

...

the_signal. sigev_signo = SIGALRM;

the_signal. sigev_value. sival_int = 1;

i = timer_create (CLOCK_REALTIME, &the_signal, &new_timer);

In this code, we specify that a timer is to be created. Once set, a signalSIGALRM will be issued on expiration of the timer, the associated value of thesignal is 1.

Setting the alarm Once we have a timer, we can set it. If we assume that thetime is encoded (as a tm structure) in the structure do time, the following sequencesets a timer value.

Page 25: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

70 CHAPTER 3. TIME, CLOCKS AND TIMERS

val.it_value.tv_sec = mktime(&do_time) - timer (NULL); /* ANSI C for abs time */

val.it_value.tv_nsec = 0;

val.it_interval.tv_sec = 15;

val.it_interval.tv_nsec = 0;

printf("Timer will go off in: %d\n", val. it_interval. tv_sec);

if (timer_settime(t_id, 0, &val, NULL) == -1) {

perror("timer_settime()");

return 2;

}

These timer settings indicate that the first time the timer expires after a com-puted interval, then every 15 seconds.

The value 0, as second parameter in timer settime tells that the time is takento be relative. if we create a timer with the flags set to TIMER ABSTIME, thesystem interprets the interval timer setting as an absolute rather than a relativevalue.

val.it_value.tv_sec = mktime(&do_time); /* ANSI C for abs time */

val.it_value.tv_nsec = 0;

val.it_interval.tv_sec = 15;

val.it_interval.tv_nsec = 0;

printf("Timer will go off at: %s\n", ctime (&val. it)value. tv_sc));

if (timer_settime(t_id, TIMER_ABSTIMER, &val, NULL) == -1) {

perror("timer_settime()");

return 2;

}

The timer will go off at do time+ 15 seconds.

An example The use of timers is exemplified in the following example. In theexample, we set a timer to go off at noon, february 10 1998, and every 15 secondsthen.

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

#include <time.h>

void timeHandler(int sigNo);

main() {

struct sigaction sigv;

struct sigevent sigx;

struct itimerspec val;

struct tm do_time;

timer_t t_id;

int i;

sigemptyset(&sigv.sa_mask);

sigv.sa_handler = (void (*))timeHandler;

sigv. sa_flags = SA_INTERRUPT;

Now we bind the handler to the signal

if (sigaction(SIGUSR1, &sigv, 0) == -1) {

perror("sigaction");

return 1;

}

Page 26: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.6. TIME IN DOS 71

Next we actually create the timer

sigx. sigev_signo = SIGUSR1;

if (timer_create(CLOCK_REALTIME, &sigx, &t_id) == -1) {

perror("timer_create()");

return (-1);

}

Now we have a timer, stored in the t id structure. The next step is to computethe time of expiration. We start with a calendar time and we compute the duration.

/* Set timer to go off at Feb. 10, 1998, 10:00 */

do_time.tm_hour = 12;

do_time.tm_min = 0;

do_time.tm_sec = 0;

do_time.tm_mon = 1;

do_time.tm_year = 98;

do_time.tm_mday = 10;

val.it_value.tv_sec = mktime(&do_time); /* ANSI C for abs time */

val.it_value.tv_nsec = 0;

val.it_interval.tv_sec = 15;

val.it_interval.tv_nsec = 0;

printf("Timer will go off at: %s\n", ctime(&val.it_value.tv_sec));

if (timer_settime(t_id, TIMER_ABSTIME, &val, NULL) == -1) {

perror("timer_settime()");

return 2;

}

The timer is running now, we wait for it to expire (the simple way)

pause ();

And we delete the timer

if (timer_delete(t_id) == -1) {

perror("timer_delete()");

return 3;

}

}

3.6 Time in DOS

3.6.1 Introduction

Dos provides several functions with which calendar time can be read, a synchronousdelay can be executed and with which an asynchronous event can be raised. In thissection we briefly discuss some of these features and exemplify their use in C andAda programs.

3.6.2 Binding Ada and C to DOS

The use DOS features depends - obviously - on the binding provided by the languageprocessor. We use the Gnu C compiler, gcc, and therefore the GNU binding. Inthe implementation, we are confronted with the original 20 bit address space of thePC architecture. Back in the early eighties, The PC used a 8088 processor witha 1 Mbyte, 20 bit, address space. Addresses therefore were always within memorybetween 0 and 1 M.

The first 640 K were available for user programs, addresses between 640 K and1 M were used for special purposes (video hardware etc). Although in later versions

Page 27: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

72 CHAPTER 3. TIME, CLOCKS AND TIMERS

of the PC more memory was added (64 Mbyte seems to be the current standard)the first physical Mbyte is always occuppied this way.

DOS, the operating system, operates within the 640 K and works in real-mode.BIOS addresses are real-mode addresses within the first M. Gcc and therefore Gnatoperate in so-called protected mode, using 32 bit addresses. These addresses arevirtual in that there is no linear mapping between the 32 bits addresses and thephysical addresses in memory. References to BIOS entities, to addresses in the firstMbyte in general, and to ‘ports’ can not be made directly, some mapping has to bedone.

Support for the 32- bit virtual memory is through the Dos P rotected ModeInterface, the DPMI. This DPMI furthermore provides functionality to addressphysical elements and ports.

The DPMI/C binding provides a possibility for binding addresses and for simu-lating registers by a union structure available in the library. The structure definesthe registerset of the 80X86 family, the 8, 16 and 32 bit elements of the family.

bx register So, similar to the ‘real’ registers, bl and bh are a pair of 8-bit registers,together forming the 16-bit bx register.

ax register Similarly, the (16 bit) ax register is composed of the 8 bit registers al(the low byte) and ah (the high byte).

ebx register The 32 bit ebx register contains the registers bx (the low order 16bits) and bx hi, the high order 16 bits.

A GNU C program that uses interrupt-calls directly usually declares a set ofsimulated registers. The appropriate include files:

#include <dos.h>

#include <dpmi.h>

#include <pc.h>

static _go32_dpmi_registers r;

Initialization of the registers with blanks is through the call

memset (&r, 0, sizeof (r));

As an example of the use of the simulated registers, issuing an interrupt, say1Ah, function 05h, would look like:

r. h. ah = 5; // function code

r. h. .. // other parameters

int86 (0x1A, &r, &r);

3.6.3 Calendar time

Reading calendar time from the BIOS can be done directly (see section 3.8), andthrough BIOS and DOS calls. The BIOS calls refer directly to the data in thereal-time clock, the DOS calls do some addditional processing.

• function 0x02 in BIOS interrupt 1Ah reads the real-time from the real timeclock.

r. h. ah = 2; // function code

int86 (0x1A, &r, &r);

hours = r. h. ch;

minutes = r. h. cl;

seconds = r. h. dh;

Page 28: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.6. TIME IN DOS 73

The values returned are encoded as hexadecimal numbers, so the result needsome further processing before it is of further use.

• function 0x04 in BIOS interrupt 1Ah reads the date from the real-time clock

r. h. ah = 4;

int86 (0x1A, &r, &r);

century = r. h. ch;

year = r. h. cl;

month = r. h. dh;

day = r. h. dl;

• function 0x2A, in DOS interrupt int21 gives the system date

r. h. ah = 42;

int86 (0x1A, &r, &r);

year = r. x. cx;

month = r. h. dh;

day = r. h. dl;

weekday = r. h. al;

• function 0x2C, in DOS interrupt 21, gives the system time

r. h. ah = 44;

int86 (0x1A, &r, &r);

hours = r. h. ch;

minutes = r. h. cl;

seconds = r. h. dh;

fraction = r. h. dl;

fraction indicates the number of hudredths of seconds.

3.6.4 Delaying execution

BIOS function 0x15 causes the execution of the program to suspend for a givennumber of microseconds

r. h. ah = 0x86;

r. x. cx = hi word of the number of microseconds

r. x. dx = lo word of the number of microseconds

int86 (0x15, &r, &r);

3.6.5 Timers, issuing a signal

The 18.2 Hz timer, the ‘0x1C’ interrupt The BIOS provides a 55 msec, an18.2 Hz, timer. A program that wishes to be interrupted with this frequency, bindsan interrupt handler to vector address 0x1C.

Most C compilers provide a possibility for binding a function to an interruptvector. As mentioned earlier, we use the GNU C compiler for DOS, so for bindingfunctions to interrupt vectors we follow the convention of the GNU C compiler. Inorder to bind an interrupt to an address, we

• save the current contents of the vector address in order to be able to restorethe contents;

• put the new handler address in the vector;

• chain the old handler to the new handler. In a number of cases, the oldhandler takes care of restoring registers etc. on exit, we want to retain thatfunctionality.

Page 29: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

74 CHAPTER 3. TIME, CLOCKS AND TIMERS

In the following fragment we demonstrate how a handler is bound to interruptvector 0x1C.

#include <dos.h>

#include <dpmi.h>

#include <pc.h>

static _go32_dpmi_seginfo old_handler, new_handler;

.....

_go32_dpmi_get_protected_mode_interrupt_vector (ADDRESS, &old_handler);

new_handler. pm_offset = & my_handler;

new_handler. pm_selector = _go32_my_cs ();

_go32_dpmi_chain_protected_mode_interrupt_vector (ADDRESS, &new_handler);

Restoring the original handler is then by just setting the old handler back

_go32_dpmi_set_protected_mode_interrupt_vector (ADDRESS, &old_handler);

As an example, a simple program handling the interrupt bound to this vector isthen

#

/*

* simple demonstration programme for handling the 0x1C interrupt

* h3/dos/1c.c

*/

#include <dos.h>

#include <dpmi.h>

#include <pc.h>

int flag = 0;

static _go32_dpmi_registers r;

static _go32_dpmi_seginfo old_handler, new_handler;

void handler () {

printf ("Yes, I’m called\n");

flag ++;

}

void main () {

// save old handler

_go32_dpmi_get_protected_mode_interrupt_vector (0x1C, &old_handler);

// address new handler

new_handler. pm_offset = &handler;

new_handler. pm_selector = _go32_my_cs ();

// chain new handler

_go32_dpmi_chain_protected_mode_interrupt_vector (0x1C, &new_handler);

while (flag < 1000) {

// this will be interrupted, and after 1000 * 55 msec, flag

// will be a 1000 and we fall through

;

}

// and restore handler

_go32_dpmi_set_protected_mode_interrupt_vector (0x4A, &old_handler);

}

The program is interrupted every 55 msec and the value of flag is incrementedat each interrupt. After 1000 interrupts, taking app 55 msec, the value of flag issuch that the condition stops to hold, and the program will terminate.

Finally, the BIOS provides a function that returns the number of 18.2 ticks sinceeither midnight or computer restart, whichever was last.

Page 30: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.6. TIME IN DOS 75

r. h. ah = 0;

int86 (0x1A, &r, &r);

highword = r. x. cx;

lowword = r. x. dx;

A programmable timer The real time clock and its supporting software do havepossibilities for an alarm to be triggered at a given (calendar) time using the 1Ahinterrupt.

r. h. ah = 6;

r. h. ch = hours;

r. h. cl = minutes;

r. h. dh = seconds;

int86 (0x1A, &r, &r);

As soon as the alarm time is reached, the function pointed to by the addressin the fixed interrupt location 4Ah is executed. The alarm can be turned off byexecuting function code 0x7 with the 0x1A interrupt. In the following programmewe demonstrate the use of this feature. In the program we activate an interrupt tobe raised at noon.

#

/*

* testprogramme for async in DOS

* C compiler: GCC

* h3/dos/async.c

*/

#include <dos.h>

#include <dpmi.h>

#include <pc.h>

int flag = 0;

static _go32_dpmi_registers r;

static _go32_dpmi_seginfo old_handler, new_handler;

void handler () {

printf ("Yes, I’m called\n");

flag = 1;

}

void main () {

int hours, minutes, seconds;

// save old handler

_go32_dpmi_get_protected_mode_interrupt_vector (0x4A, &old_handler);

// address new handler

new_handler. pm_offset = &handler;

new_handler. pm_selector = _go32_my_cs ();

// chain new handler

_go32_dpmi_chain_protected_mode_interrupt_vector (0x4A, &new_handler);

// we clean the alarm timer

memset (&r, 0, sizeof (r));

r. h. ah = 0x7;

int86 (0x1A, &r, &r);

// and set it to a new value

r. h. ah = 0x6;

r. h. ch = 0x12;

r. h. cl = 0x00;

r. h. dh = 0x00;

int86 (0x1A, &r, &r);

while (!flag) {

;

}

printf ("Alarm is raised at %x %x %x\n", hours, minutes, seconds);

Page 31: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

76 CHAPTER 3. TIME, CLOCKS AND TIMERS

// turn alarm off

r. h. ah = 0x7;

int86 (0x1A, &r, &r);

// and restore handler

_go32_dpmi_set_protected_mode_interrupt_vector (0x4A, &old_handler);

}

3.6.6 Measuring time differences

Measuring durations though standard DOS and BIOS calls can be done with anaccuracy of hundredths of seconds, i.e. units of 10 msecs, using DOS call 0x2A.

#

/*

* testprogramme for measuring durations

* C compiler: GCC

*/

#include <dos.h>

#include <dpmi.h>

#include <pc.h>

void main () {

struct my_time {

int hours, minutes, seconds, fraction;

} now, later;

memset (&r, 0, sizeof (r));

// first we want to have the time

r. h. ah = 0x2A;

int86 (0x21, &r, &r);

now. hours = r. h. ch;

now. minutes = r. h. cl;

now. seconds = r. h. dh;

now. fraction = r. h. dl;

// then we do what we want to do

for (i = 1; i <= 100; i ++) {

....

}

// and we measure the time again

r. h. ah = 0x2A;

int86 (0x21, &r, &r);

later. hours = r. h. ch;

later. minutes = r. h. cl;

later. seconds = r. h. dh;

later. fraction = r. h. dl;

printf ("Time difference is %d %d %d %d\n",

later. hours - now. hours,

later. minutes - now. minutes,

later. seconds - now. seconds,

later. fraction - now. fraction);

}

3.6.7 Measuring in micro seconds

Introduction We can get a more accurate reading of time by directly manipulat-ing the hardware of the PC. One of the possibilities, also used by many applicationprograms, is by directly reading the 8253 timer. In this section, we show, as anexample, how the chip can be read.

The 8253 Common PC’s contain a timer chip, the 8253, that is used for a varietyof purposes. It is a three channel 16 bit timer/counter with an input frequency of

Page 32: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.6. TIME IN DOS 77

Refresh

Request

Ti IRQ 0 on 8259A

Driver

And

Divideby2

IO Address 0x61.1

Crystal 2.38 Mhz

+5 V

Address

0x61.0

Gate 0

Clock 0 in

Gate 1

Clock 1 in

Gate 2

Clock 2 in

Clock 0 out

Clock 1 out

Clock 2 out

Figure 3.1: The 8253

1.1931817 Mhz (see figure 3.1):

• clock 0 is used for generating an interrupt every 55 milliseconds, the BIOStakes care of updating the time of day clock and setting and resetting therequired values in the control register.

• clock 1 is used to generate requests for refreshing the (dynamic) memory ofthe system once every 15 microseconds, we rather not touch this one.

• clock 2 is used for the ‘sound’ subsystem.

The input to the clock is a signal with a frequency of 1.1931817 MHz, obtained bya crystal oscillator of 2.38 MHz, the output of which is divided by 2.

Clock 0 is continuously running, decreasing the clock counter. If the clockcounter passes 0, an interrupt is generated which is handled by the BIOS. TheBIOS reloads the control register, causing the counter to start for its next 64Kticks. Furthermore, the BIOS maintains a register that keeps the number of timer0 interrupts since midnight. The library used provides a function biostime, thatgives the number of ticks since midnight. B.t.w., a simple calculation reveals goingfrom 0 to 0 downwards, i.e. to handle 64K ticks, takes 55 milliseconds.

For measuring times with a high precision, we can build a 32 bit ‘time’ in whichthe lowest 16 bits indicate the state of the timer, i.e. how many cycles have passedsince this run of the clock began, just by reading the value of clock 0 of the 8253.These low 16 bits, preceded by the number of timer 0 interrupts since midnight, givesa fairly precise reading of the time since midnight expressed in 1

1.19microseconds

units. Given values, obtained this way, we can compute time differences with areasonable accuracy.

A timer interface As an example, we build a simple timer package providinga timer value in Ada, implemented by binding to a C function. The specificationbelow shows how C functions can be imported in Ada program systems.

Page 33: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

78 CHAPTER 3. TIME, CLOCKS AND TIMERS

with Interface; use Interface;

with Interface. C; use Interface. C;

package micro_timer is

TimerResolution : constant := 1193181.667;

function read_time return unsigned_32;

pragma Import (C, read_time, "readtimer");

end micro_timer;

The interface simply states that function read time is made available and that inlink time, the C function readtimer is used to implement the required functionality.

Implementation

Access to peripheral chips in common PC’s is through ‘ports’. A port is a devicethat (i) connects the CPU to the peripheral chip, and (ii) is addressable through ‘in’and ’out’ instructions of the X86 family of processors. Reading a port is throughthe function inportb, writing a port is through the function outportb.

int inportb (port address);

void outportb (port address, value)

The timer chip is addressable through ports 0x40 to 0x43. 0x43 is the portthrough which control information is fed into the chip, 0x40 is the port specificallyfor clock 0 and contains values read from (and put into) timer 0. The structure ofthe control byte for port 0x43 is as follows:

bit 0 = 0 count binary

= 1 count decimal, we take 0

bits 1-3 = mode number (between 000-101)

bits 4,5 = 00 latch current count for reading

= 01 read/load low byte

= 10 read/load high byte

= 11 read low byte, then read high byte

bits 6,7 = counter number (0, 1, 2)

It shows that byte value 0x34 means read from counter 0 (bits 6,7), two succes-sive bytes in the order low byte first, then high byte (bits 4, 5), be in mode 4 (bits1, 2, 3) and count binary (bit 0).

Mode 0x36 then means read from counter 0 two successive bytes in the orderlow byte first, then high byte, and be in mode 5.

The structure of the implementation is as follows:

• we first turn off the interrupts.

disable ();

• Next, we signal the chip that the value should be buffered (latched).

outportb (0x43, 0x00)

• Next, we obtain the low- and high byte of the value in that order using thecommands

inportb (0x40); -- low order

inportb (0x40); -- high order

• We have to take care of one possible race condition. If, between issuing thedisable command and the command that tells the chip to latch the currentcounter value, an interrupt has occurred, the high-order 16 bits kept in the

Page 34: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.6. TIME IN DOS 79

BIOS is not updated yet. Since handling the interrupt will be postponed untilwe turn on the interrupt again, we must check whether or not the interruptoccurred. In case it happened, our reading of the number of clock ticks sincemidnight is to be updated by one. As soon as interrupts are turned on again,the BIOS will catch and handle the interrupt.

We use three functions here, inportb, to read a byte from a given port, outportb,to write a byte to a port, and biostime, to obtain the number of 18.2 Hz ticks sincemidnight2.

#include <dos.h>

#include <dpmi.h>

#include <bios.h>

#include <pc.h>

#include "pctimer.h"

/*

* for details see the datasheet for the 8253 timer chip

*/

#define TimerResolution 1193181.667

#define WORD unsigned short

// The dpmi registers as defined below provide the opportunity to

//manipulate simulated registers in real-mode.

static _go32_dpmi_registers r;

long int readtimer(void)

{

disable(); // disable all interrupts

memset(&r, 0, sizeof(r)); // clear the register area

// In di we keep the irr, the interrupt request register

// which tells us whether or not

// an interrupt from the 8253 chip is pending. If so, we

// have to add 1 to the BIOS time, to get a correct result

outportb(0x20, 0x0A); //out to PIC

r.x.di = inportb(0x20); //in al,dx

// Now we tell the 8253 that we want to get the current

// value, since we have to fetch twice a byte, we want him to

// latch

outportb(0x43, 0x00); //tell chip to latch

r.h.bl = inportb(0x40); //low 8 bits

r.h.bh = inportb(0x40); //high 8 bits

// That’s the trick, bx contains bh and bl

// The timer runs downwards, Therefore, we want the complement

r.x.bx = ~r.x.bx; //not bx

// Basically that should have been it, but the interrupts!!

r.h.al = inportb(0x21); //in al,021h

r.x.si = r.x.ax; //save interrupt mask register

r.h.al = 0x00FF; //mov al,00FFh

outportb(0x21, r.h.al); //out 021h,al

// Fetch high 16 bits of time from the bios area

r.x.bx_hi = biostime (0, (long)0) & 0xFFFF;

// if there was there an interrupt pending, this

// value has to be updated with one

if (r.x.di & 0x01) //test occurrence of interrupt

r.x.bx_hi ++;

// Restore mask register

r.x.ax = r.x.si; //mov ax,si

outportb (0x21, r.h.al); //out 021h,al

// and let those interrupts come

enable(); //sti

// The final value is composed of the components.

2Note that this implies that this counter will not work correctly when between two measure-ments the midnight border is passed.

Page 35: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

80 CHAPTER 3. TIME, CLOCKS AND TIMERS

return r.d.ebx;

}

Making microseconds

Converting from ticks to microseconds is by a simple calculation on the number ofticks.

function from_ticks_to_msec (a: in unsigned_32) return unsigned_32 is

begin

return unsigned_32 (float (a) * 1000_000.0 /timer. TimerResolution);

end;

A simple example

Having a timer with a microsecond accuracy makes timing fun and easy. We justmeasure twice the time, take the difference and calculate the result in microseconds.

with Ada.Text_Io; use Ada.Text_Io;

with Interfaces; use Interfaces;

with Interfaces. C; use Interfaces. C;

with timer;

procedure test1 is

start_time : unsigned_32;

stop_time : unsigned_32;

dummy : unsigned_32;

function from_ticks_to_msec (a: in unsigned_32) return unsigned_32 is

begin

return unsigned_32 (float (a) * 1000_000.0 /timer. TimerResolution);

end;

begin

start_time := timer. read_time;

stop_time := timer. read_time;

Ada. text_Io. Put_Line (" total time = "

& unsigned_32’ image (

from_ticks_to_msec (stop_time - start_time)));

start_time := timer. read_time;

dummy := from_ticks_to_msec (dummy);

stop_time := timer. read_time;

Ada. text_Io. Put_Line (" total time = "

& unsigned_32’ image (

from_ticks_to_msec (stop_time - start_time)));

end test1;

Running the example above gives two printed values. The first one indicates theoverhead of the invocation and execution of the timer function itself. On the 100Mhz Pentium of the author the measured time, i.e. the execution of the timer task,took 170 microsecond. Apparently the overhead induced by the timer routines(especially the BIOSTIME call) is fairly large. In the second measurement, wemeasure the time needed to execute a conventional procedure call. The measuredvalue is 180 microsec and since this includes the overhead of the timer functions,the actual overhead for a procedure call takes app 10 microsec.

As a next, small example, we measure the time involved in handling an entrycall to a protected object

declare

protected semaphore is

entry lock;

procedure unlock;

Page 36: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.7. TIME IN JAVA 81

private

locks: integer := 0;

end semaphore;

task task2 is

pragma priority (6);

end;

protected body semaphore is

entry lock when locks > 0 is

begin

locks := locks - 1;

end;

procedure unlock is

begin

locks := locks + 1;

end;

end semaphore;

task body task2 is

begin

for i in 1 .. 5 loop

start_time := timer. read_time;

semaphore. lock;

stop_time := timer. read_time;

Ada. text_Io. Put_Line (" Handling a protected entry call = "

& unsigned_32’ image (

from_ticks_to_msec (stop_time - start_time - corr)));

end loop;

end;

begin

for i in 1 .. 5 loop

semaphore. unlock;

end loop;

end;

The results are given below

Handling a protected entry call = 80

Handling a protected entry call = 75

Handling a protected entry call = 75

Handling a protected entry call = 74

Handling a protected entry call = 75

3.7 Time in Java

3.7.1 Printing calendar time

Time is part of date information in Java. The current date in Java can be obtainedby instantiating an object of the class Date. On objects of this class, a variety ofmethods is defined.

The method toString translates the date, stored in an object of class Date intoa string

/*

*/

import java. util. Date;

public class printtime {

public static void main (String args []) {

System. out. println ("It is now " + new Date (). toString ());

}

}

Instantiated, the execution gave

It is now Mon Oct 27 09:07:15 PST 1997

Page 37: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

82 CHAPTER 3. TIME, CLOCKS AND TIMERS

which clearly states the time these lecture notes were written.Furthermore, methods are defined to compare two dates, after(Date) and before(Date)

have their intuitive meaning.The class Date also provides a getT ime method that relates a date to januari 1

1970, and returns the number of milliseconds since then. As in UNIX, time starts in1970, so, if we are interested in how many milliseconds passed since 1970 we couldwrite the program

import java. util. Date;

public class print2 {

public static void main (String args []) {

System. out. println ("It is now " + new Date (). getTime () + " milliseconds since 1970");

}

}

which gave the result

It is now 877972364965 milliseconds since 1970

A constructor function Date, applied to a number, creates a date object with asvalue the date obtained by interpreting the number as number of milliseconds since1970.

3.7.2 Suspending execution

The execution of a thread is suspended by executing the wait or sleep method inthe thread that is to be suspended. These methods take the number of millisecondsto be suspended as parameter.

A sleepmethod causes the thread, executing the method, to sleep for the numberof milli-seconds passed as parameter. I.e., a sleep for 10 seconds is encoded as

Thread. sleep (10000);

An alternative approach is by using the wait method, as we have seen in chapter2.

try {

Thread. wait (10000);

} catch (InterruptedException ex) {}

If this construct terminates, it holds that either 10000 milliseconds passed orthat an exception InterruptException occurred.

Timed joins Java allows a thread to wait on the termination of another thread,using the join method. A value can be passed as a parameter to the join method,in which case the join will terminate if either the thread terminates, or if the timepasses, whichever happens first. The timed join is therefore an excellent vehicle forimplementing time out like constructs s shown below:

try {

Thread. join (1000);

} catch (Interrupted Exception ex) {}

if (Thread. isAlive ())

// do the time out code

else

// thread was already dead

Page 38: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.7. TIME IN JAVA 83

3.7.3 Signaling and periodical tasks

We are interested in building safe periodical tasks, i.e. tasks that ensure there willbe no deadline overrun. In Java, the approach is rather obvious, we build a classthat provides the required functionality and allows the user to fill in the parametersof the period and the code to be executed. A user then may derive particular classesfrom the safetask class that we provide along the lines that we sketch below.

public class my_task extends safetask {

public my_task (String name, long starttime,

long period, long deadline) {

super (name, starttime, period, deadline);

}

public void the_body () {

// This is the code to be executed periodically

}

public void overrun () {

// This is the code to be executed in case of deadline overrun

}

}

We assume that the starttime is expressed in milliseconds since 1970, period anddeadline are expressed as a millisecond interval.

In the implementation, we create a separate thread for execution of the code ofthe body. Java provides a method join that causes the thread calling the methodto wait for at most a given period on the termination of the thread the method wassent to, as was shown above. If we assume that the thread safe body executes theperiodical code, control then is in the following parts

now = new Date (). getTime ();

// If the starting time is not yet reached, wait

if (starttime + deadline > new Date (). getTime ()) {

while (starttime > new Date (). getTime ()) {

try {

sleep (starttime - new Date (). getTime ());

} catch (InterruptedException ex) {};

}

}

// Now start the body

safe_body. start ();

// and wait for its termination for deadline milliseconds

try {

safe_body. join (deadline);

} catch (InterruptedException ex){}

// if the thread we were waiting for is still alive, kill it

if (safe_body. isAlive ()) {

safe_body. stop ();

overrun ();

}

The class defining safe periodical tasks then becomes as given below

/* Safe periodical task in Java

* Jan van Katwijk

* October 1997

*/

import java.util.Date;

import java. lang. Boolean;

public class safetask extends Thread {

long starttime;

long period;

long deadline;

int iteration;

Page 39: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

84 CHAPTER 3. TIME, CLOCKS AND TIMERS

public safetask (String name, long starttime,

long period, long deadline) {

super (name);

this. starttime = starttime;

this. period = period;

this. deadline = deadline;

}

public void the_body () {

}

public void overrun () {

}

private class body extends Thread {

public void run () {

the_body ();

}

}

The trick is in the implementation of the run method. For each invocation anew thread is created and instantiated. The run method furthermore maintains analarm timer that will go off whenever the deadline of the thread expires.

public void run () {

int i;

long now;

body safe_body;

this. setPriority (this. MAX_PRIORITY);

iteration = 0;

while (java. lang. Boolean. TRUE. booleanValue ()) {

iteration = iteration + 1;

safe_body = new body ();

now = new Date (). getTime ();

if (starttime + deadline > new Date (). getTime ()) {

while (starttime > new Date (). getTime ()) {

try {

sleep (starttime - new Date (). getTime ());

} catch (InterruptedException ex) {};

}

}

safe_body. start ();

try {

safe_body. join (deadline);

} catch (InterruptedException ex){}

if (safe_body. isAlive ()) {

safe_body. stop ();

overrun ();

}

starttime = starttime + period;

}

}

}

A safe periodical task is now constructed as an object, inheriting from safetask.

/*

* The specification of my private safe task

*/

import java. util. Date;

public class my_task extends safetask {

public my_task (String name, long starttime,

long period, long deadline) {

super (name, starttime, period, deadline);

}

Page 40: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

3.8. THE PC REAL TIME CLOCK 85

public void the_body () {

int i;

int c = iteration;

synchronized (System. out) {

System. out. println (getName () + " period = " + period +

" deadline = " + deadline);

System. out. println (getName () + " is doing iteration " +

c + " at " +

new Date (). getTime () );

}

for (i = 0; i > 0; i ++);

}

public void overrun () {

synchronized (System. out) {

System. out. println (getName () + " has overrun in iteration " +

iteration);

}

}

}

3.8 The PC real time clock

3.8.1 Introduction

Time is derived from some clock mechanism. In our PC we have a real-time clockthat maintains the time, even when the PC is turned off. In this section we brieflydiscuss the structure of the real-time clock as found is IBM compatible PC’s andwe give an example of its use.

3.8.2 The hardware

The PC/AT and its successors have a battery-fed real-time clock, originally basedon the MC146818 chip. The chip contains, apart from the clock a small memory(64 bytes of CMOS RAM) that contains information on the time as well as systemconfiguration data. The CMOS ram is addressible through port 0x70. The clockuses the locations 0 until 13 of this CMOS ram, as depicted in table 3.2

Cel function0 Current second1 Alarm second2 current minute3 alarm minute4 current hour5 alarm hour6 day of week7 day of month8 month9 year10 statusregister A11 status register B12 status register C13 status register D

Table 3.2: Contents of CMOS Ram

Reading a cell from the CMOS Ram is indirect, we notify the port that we wantto read a cell through port 0x70, we actually read the value through port 0x71. As

Page 41: Introduction to Real-time software systems Draft Editionitech.fgcu.edu/faculty/zalewski/CEN4930/PDF/Katwijk... · 2005. 9. 27. · Chapter 3 Time, clocks and timers 3.1 Introduction

86 CHAPTER 3. TIME, CLOCKS AND TIMERS

an example, reading status register A is through

#define STATUSA 10

..

outportb (0x70, STATUSA);

val = inportb (0x71);

Similarly, writing a cell in the CMOS ram is by writing into port 0x70 andsubsequently writing through port 71h.

#define DAYOFWEEK 6

...

outportb (70h, DAYOFWEEK);

outportb (71h, WEDNESDAY);

The following program reads the cells 0 to 13 of the CMOS ram and prints theirvalues octal, decimal and hexadecimal.

#include <dos.h>

#include <dpmi.h>

#include <bios.h>

#include <pc.h>

#include <stdio.h>

#define RAMPORT 0x70

unsigned char read_cell (int offset) {

outportb (RAMPORT, offset);

return inportb (RAMPORT + 1);

}

void main () {

int i;

for (i = 0; i <= 13; i ++) {

printf ("value of cell %d is %o (%d) (%x)\n", i,

read_cell (i), read_cell (i), read_cell (i));

}

}

When run while writing, the output was

value of cell 0 is 50 (40) (28)

value of cell 1 is 370 (248) (f8)

value of cell 2 is 30 (24) (18)

value of cell 3 is 177 (127) (7f)

value of cell 4 is 21 (17) (11)

value of cell 5 is 104 (68) (44)

value of cell 6 is 240 (160) (a0)

value of cell 7 is 23 (19) (13)

value of cell 8 is 10 (8) (8)

value of cell 9 is 227 (151) (97)

value of cell 10 is 46 (38) (26)

value of cell 11 is 2 (2) (2)

value of cell 12 is 0 (0) (50)

value of cell 13 is 200 (128) (80)