 | Level: Advanced Dinakar Guniguntala (dgunigun-at-in.ibm.com), Staff Software Engineer, IBM Sathyan Doraiswamy (dsathyan-at-in.ibm.com), Staff Software Engineer, IBM Anand Santhanam (asanthan-at-in.ibm.com), Software Engineer, IBM Srinivasan Muthuswamy (smuthusw-at-in.ibm.com), Software Engineer, IBM Rinson Antony (arinson-at-in.ibm.com), Software Engineer, IBM Brahmaiah Vallabhaneni (bvallabh-at-in.ibm.com), Software Engineer, IBM
24 Aug 2004 Linux is evolving as the predominant operating system of the new millennium, and legacy operating systems such as OS/2® are being gradually phased out. This series of articles helps the developers involved in the tedious process of migrating/porting the OS/2 system drivers and applications to Linux. In this last of three installments, the authors focus on how to do timer calls and DLL calls in OS/2 and Linux, with a view to mapping between the two systems.
This article series is intended as a reference for migration between
OS/2 and Linux. You can refer to it at any time in the migration process, but
it is most helpful in the planning stages, as it provides tips and caveats
you will want to know as you plan your migration design and strategies.
This time you will examine timer calls and DLL calls in OS/2 and in Linux, plus mapping between the two systems.
In Parts 1 and 2 of this series, you looked at threads, synchronization mechanisms like semaphores and mutexes, memory management, various IPC mechanisms, and file management.
Timer calls
First, we will look at calls for several types of timers:
- Interval timers
- Start timers
- Stop timers
- High resolution timers
Interval timers
In OS/2, the call DosStartTimer starts a
repeated interval timer that posts an event semaphore at every timer
interval. Linux does not provide a direct mappable call that repeatedly
posts a semaphore, but you can be achieve that functionality by using a
combination of the setitimer system call and
the Linux signal handling mechanism.
In OS/2, the timer value parameter is in milliseconds. On
Linux, this information is specified with an object of type struct itimerval, which has both seconds and
microseconds resolution.
Table 1. Interval timer calls mapping table
| OS/2 | Linux | Classification | | DosStartTimer | setitimer | Mappable | | DosStopTimer | setitimer
itimerclear | Mappable |
Start timer
In OS/2, the system call DosStartTimer() starts an asynchronous, repeated-interval timer:
APIRETDosStartTimer(ULONG msec, HSEM hsem, PHTIMER phtimer);
msec is the time, in milliseconds, that
will elapse between postings of the event semaphore.
hsem is the handle of the event semaphore
that is posted each time msec elapses.
phtimer is a pointer to the timer handle.
Linux does not have a direct mappable API call to signal a semaphore
at repeated intervals. To achieve this, use interval timers. An interval timer sends a SIGALRM
signal to the process. The event semaphore is posted in the signal
handler, or the code that is waiting for the semaphore to be posted is placed in the signal handler routine:
int setitimer (int mode, const struct itimerval *newvalue, struct itimerval
*oldvalue);
mode must be ITIMER_REAL so the timer decrements in real time, and delivers a SIGALRM upon expiration.
newvalue is a pointer to an itimerval structure containing the timer value to be
set.
oldvalue is a pointer to an itimerval structure to hold the old timer value.
On success, setitimer returns zero (and -1
on error) with errno set to the appropriate
value. A timer value is defined by the first parameter newvalue of type struct
itimerval. When newvalue->it_value is
non-zero, it indicates the time to the next timer expiration. When newvalue ->it_interval is non-zero, it specifies
the value to be used in reloading newvalue
->it_value when the timer expires.
Only use async-safe functions in signal
handlers. For instance, do not use pthread
conditional variables in the signal handler.
Stop timer
In OS/2, DosStopTimer() stops an
asynchronous timer:
APIRETDosStopTimer(HTIMER htimer);
htimer is the handle of the timer to stop. It returns zero
on success and non-zero on failure.
In Linux, to stop an interval timer, first call the macro timerclear to clear the it_value field of struct itimerval. Then call setitimer with a cleared itimerval.
Setting it_value to 0 disables the timer,
regardless of the value of it_interval. Setting
it_interval to 0 disables a timer after its
next expiration (assuming it_value is
non-zero).
timerclear is a macro defined (in
sys/time.h) as:
#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
The code in Listing 1 starts an asynchronous timer that repeatedly posts an
event semaphore at every timer interval.
Listing 1. OS/2 Interval timers example
int main(VOID)
{
HEV hevEvent1 = 0; /* Event semaphore handle */
HTIMER htimerEvent1 = 0; /* Timer handle */
APIRET rc = NO_ERROR; /* Return code */
ULONG ulPostCount = 0; /* Semaphore post count */
ULONG i = 0; /* A loop index */
/*
* Create an even semaphore that will be posted on timer interval
*/
rc = DosCreateEventSem(NULL, /* Unnamed semaphore */
&hevEvent1, /* Handle of semaphore
* returned
*/
DC_SEM_SHARED, /* Indicate a shared semaphore */
FALSE); /* Put in RESET state */
if (rc != NO_ERROR) {
printf("DosCreateEventSem error: return code = %u\n", rc);
return 1;
}
/*
* Start the timer setting a interval at 1 sec
*/
rc = DosStartTimer(1000L, /* 1 second interval */
(HSEM) hevEvent1, /* Semaphore to post */
&htimerEvent1); /* Timer handler (returned) */
if (rc != NO_ERROR) {
printf("DosStartTimer error: return code = %u\n", rc);
return 1;
}
for (i = 1 ; i < 6 ; i++)
{
DosWaitEventSem(hevEvent1, SEM_INDEFINITE_WAIT); /* Wait indefinitely */
DosResetEventSem(hevEvent1, /* Reset the semaphore */
&ulPostCount); /* And get count
* (should be 1)
*/
printf("Iteration %u: ulPostCount = %u\n", i, ulPostCount);
} /* for loop */
rc = DosStopTimer(htimerEvent1); /* Stop the timer */
if (rc != NO_ERROR)
{
printf("DosStopTimer error: return code = %u\n", rc);
return 1;
}
DosCloseEventSem(hevEvent1); /* Get rid of semaphore */
return NO_ERROR;
}
|
The code in Listing 2 starts an asynchronous timer that repeatedly posts an
event semaphore at every timer interval.
Listing 2. Linux interval timer example
sem_t sem;
/* callback to handle SIGALRM */
void f (int signo)
{
/* Here you can post on a semaphore */
sem_post(&sem);
}
int main ()
{
int i;
int rc;
/* Declare an itimer object to start a timer */
struct itimerval itimer;
/* Initialize the semaphore. */
sem_init(&sem,0,0);
/* timer interval is 1 sec. */
itimer.it_interval.tv_sec = 1;
itimer.it_interval.tv_usec = 0;
/* Time to the next timer expiration */
itimer.it_value.tv_sec = 1;
itimer.it_value.tv_usec = 0;
/* Install a signal handler to handle SIGALRM */
signal (SIGALRM, f);
/* Start a timer with the ITIMER_REAL clock */
i = setitimer (ITIMER_REAL, &itimer, 0);
if (i == -1)
{
perror ("timer st failed ...\n");
exit (0);
}
for (i = 1 ; i < 6 ; i++)
{
rc = sem_wait(&sem);
if (rc != 0)
{
perror ("sem_wait:");
return 1;
}
printf("Iteration %d: \n", i);
} /* for loop */
/* Set the timer object value with zero, by using the macro
* timerclear and
* set the ITIMER_REAL by calling setitimer.
* This essentially stops the timer.
*/
timerclear(&itimer.it_value);
setitimer (ITIMER_REAL, &itimer, 0);
return 0;
}
|
High resolution timers
Next, we will show you how to get the current timer count and frequency
from a high resolution timer. On OS/2, DosTmrQueryTime() returns the time stamp counter from
intel 8254 timer (programmable interrupt timer). Linux has no APIs
available to get this information. You can use the inline function get_cycles() defined in the
<asm/timex.h> kernel header.
Use the time stamp counter in conjunction with the
frequency of the clock, as the ratio of counter to frequency remains the same on different clocks.
Table 2. Timer query calls mapping table
| OS/2 | Linux | Classification | | DosTmrQueryTime | get_cycles | Mappable | | DosTmrQueryFreq | | Unmappable,
but you can obtain the clock frequency from /proc/cpuinfo; see Listing
3. |
In OS2, the system call DosTmrQueryTime() gets a snapshot of the high-resolution timer count from the IRQ0
high-resolution timer (Intel 8254):
APIRET DosTmrQueryTime(PQWORD pqwTmrTime);
pqwTmrTime is the output parameter where timer count value
is returned.
In Linux, the inline function get_cycles()
returns the time stamp counter of the system clock, which is a 64-bit
value. The function always succeeds (basically, it executes a machine
instruction):
static inline cycles_t get_cycles (void);
In OS2, the system call DosTmrQueryFreq()
gets the frequency of the IRQ0 high resolution timer (Intel
8254):
APIRET DosTmrQueryFreq (PULONG pulTmrFreq)
pulTmrFreq is the output parameter to which the timer
frequency is returned by DosTmrQueryFreq.
Linux has no equivalent to DosTmrQueryFreq, but you can obtain the clock frequency from /proc/cpuinfo.
Listing 3 shows a user-defined function to get the frequency of the CPU.
It opens the /proc/cpuinfo file, searches for the "cpu MHz" string, and
reads the CPU frequency. This function converts the read value to Hz and
returns the same.
Listing 3. Linux implementation for getting clock frequency
/*
* Macro to compare keywords
* strcmp is called only when the first characters are the same
*/
#define WordMatch(a, b) ( (*(a) == *(b)) && (strcmp(a, b) == 0) )
int GetClockFreq(unsigned long *pulCpuFreq)
{
char* pcFilename = "/proc/cpuinfo";
char pcReadLine[BUFSIZE];
char pcKeyword[BUFSIZE];
char pcKeyword2[BUFSIZE];
int iDone = 0; /* Flag to determine end of loop */
*pulCpuFreq = 0;
memset(pcReadLine,0,BUFSIZE);
memset(pcKeyword,0,BUFSIZE);
memset(pcKeyword2,0,BUFSIZE);
/*
* Open the /proc/cpuinfo file
*/
fstream fpCpuInfo(pcFilename, ios::in);
if (fpCpuInfo)
{
/*
* Read a line into the buffer
*/
while ((fpCpuInfo.getline(pcReadLine, BUFSIZE)) && (iDone == 0))
{
/*
* Instantiate istrmInput to translate input line into
* a stream object
*/
istrstream istrmInput(pcReadLine);
/*
* Get first 2 word from the input line and build the keyword
*/
istrmInput >> pcKeyword;
istrmInput >> pcKeyword2;
strcat(pcKeyword, " ");
strcat(pcKeyword, pcKeyword2);
if (WordMatch(pcKeyword, "cpu MHz"))
{
/*
* Get the Mhz value from input line by skipping
* past the colon.
*/
istrmInput >> pcKeyword;
istrmInput >> pcKeyword;
/*
* Convert the char* to float and multiply by 1000000 to
* get the clock in Hz
*/
*pulCpuFreq = (unsigned long)(atof(pcKeyword) * 1000000);
iDone = 1;
}
} /* end of while */
fpCpuInfo.close();
}
return ((iDone)?0:-1);
}
|
Listing 4 illustrates the use of the DosTmrQueryTime and DosTmrQueryFreq calls.
Listing 4. OS/2 timer query example
void GetTimeStampPair( )
{
struct QWORD qwStartTime, qwStopTime;
double diff;
Unsigned long ulTmrFreq;
DosTmrQueryTime(&qwStartTime);
/* perform an activity */
diff = 0;
DosTmrQueryTime(&qwStopTime);
/* Calculate the difference between two times */
diff = qwStopTime.ulLo - qwStartTime.ulLo;
/* Query the Intel chip's resolution */
DosTmrQueryFreq(&ulTmrFreq);
printf ("Timer Frequency is %ld\n Hz", ulTmrFreq);
/* calculate the time take for the assignment operation */
/* diff time/frequency */
printf(" Time taken for the activity %ld\n", diff/ulTmrFreq);
} /* end GetTimeStampPair */
|
Listing 5 shows how to measure time between two operations by mapping
the OS/2 DosTmrQueryTime and DosTmrQueryFreq calls.
Listing 5. Linux timer query example
/*
* This function uses the user defined function GetClockFreq()
*/
#define NANOSLEEP 10000000 /* 10 ms */
#define MSLEEP NANOSLEEP/1000000
int main (void)
{
unsigned long t1, t2, jitter, freq;
double accum;
int ret;
struct sched_param sp;
struct timespec ts,rem;
/* Set the scheduling priority to the real time */
sp.sched_priority = sched_get_priority_max(SCHED_RR);
sched_setscheduler(getpid(), SCHED_RR, &sp);
mlockall(MCL_FUTURE|MCL_CURRENT);
rem.tv_sec = 0;
ret = GetClockFreq (&freq);
if (ret)
{
/* error condition */
return -1;
}
rem.tv_sec = 0;
rem.tv_nsec = NANOSLEEP;
t1 = get_cycles();
do {
ts.tv_sec = rem.tv_sec;
ts.tv_nsec = rem.tv_nsec;
rem.tv_nsec = 0;
ret = nanosleep(&ts, &rem);
} while (rem.tv_nsec != 0);
t2 = get_cycles();
jitter = t2 - t1;
/* How much time elapsed. Number of ticks
* divided by frequency of the clock
*/
accum = (double)jitter/freq;
return 0;
}
|
Dynamic link library (DLL) calls
Dynamic linking defers much of the linking process until a program
starts running. It provides a variety of benefits that are hard to get
otherwise: it permits a program to load and unload routines at runtime, a
facility that is otherwise very difficult to provide.
In Linux, dynamic link libraries (DLL) are known as shared libraries
(.so). The system's dynamic loader looks for shared libraries in
system-specified directories (such as /lib, /usr/lib, /usr/X11/lib, and so on).
When you build a new shared library that is not part of the system, use
the LD_LIBRARY_PATH environment variable to
tell the dynamic loader to look in other directories. Supply the -ldl command-line option when
linking the modules that have dlopen, dlsym, and like calls.
Table 3. DLL calls mapping table
| OS/2 | Linux | Classification | | DosLoadModule | dlopen | Mappable | | DosQueryProcAddr | dlsym | Mappable | | DosFreeModule | dlclose | Mappable |
Loading DLLs
In OS/2, you can use DosLoadModule() to
load a dynamic link library explicitly:
APIRET DosLoadModule(PSZ pszName, ULONG cbName, PSZ pszModname,
PHMODULE phmod);
pszModname is a pointer to a variable
that contains the dynamic link module name.
phmod is a pointer to a variable in which
the handle for the dynamic link module is returned.
In Linux, you use the dlopen() library function to load a dynamic library. The function returns a handle for the
dynamic link library:
void * dlopen(const char *pathname, int mode);
pathname takes the name of the library to
be loaded. If it is NULL, then the handle for the main program is
returned.
mode can be either RTLD_LAZY, meaning resolve undefined symbols as code
from the dynamic library, or RTLD_NOW, meaning resolve all undefined symbols
before dlopen returns, and fail if this cannot be done. Optionally, you can "or" RTLD_GLOBAL with a flag, in which case the
external symbols defined in the library are made available to
subsequently loaded libraries.
Getting references to DLL functions
In OS/2, DosQueryProcAddr() gets
the address of the specified procedure within a dynamic link module:
APIRET DosQueryProcAddr(HMODULE hmod, ULONG ordinal, PSZ pszName, PFN
ppfn);
hmod refers to the module handle returned
by DosLoadModule.
pszName is the name of the function.
ppfn is the function pointer that is
returned.
In Linux, dlsym() returns the address of a
symbol when a handle of a dynamic library returned by dlopen and the null terminated symbol name are
supplied as parameters:
void * dlsym(void *handle, char *symbol);
handle is the handle of the loaded
library.
symbol is the symbol name. If the symbol
is not found, dlsym returns NULL.
Note that C++ function names are mangled to support function
overloading, so the function may have a different name in the library. You can figure out the mangling scheme and search for the mangled symbol
instead. The compiler uses C-style linkage when an extern "C" qualifier is specified in the declaration.
Unloading DLLs
In OS/2, DosFreeModule() frees
the reference to the dynamic link module for this process. If no process
uses the dynamic link module, the module is freed from system memory. The
module identified by the handle must have been loaded using DosLoadModule. If the handle is invalid, an error is
returned:
APIRET DosFreeModule (HMODULE hmod);
In Linux, dlclose() disassociates a shared
object previously opened by dlopen from the
current process. Once an object has been closed using dlclose, its symbols are no longer available to dlsym:
int dlclose(void *handle);
handle refers to the handle returned by
dlopen. If the referenced object is
successfully closed, dlclose returns 0. If the
object could not be closed, or if the handle does not refer to an open object,
dlclose returns a non-zero value.
Note that in Linux, the library function dlerror returns a null-terminated character string
(with no trailing newline) that describes the last error that occurred
during dynamic linking processing:
const char *dlerror(void);
The code in Listing 6 loads the dynamic link module DISPLAY.DLL, gets a reference
to function draw(), and then unloads it. It does
not show error handling.
Listing 6. OS/2 example for loading DLLs
int main(VOID) {
PSZ ModuleName = "C:\\OS2\\DLL\\DISPLAY.DLL"; /* Name of module */
UCHAR LoadError[256] = ""; /* Area for Load failure information */
HMODULE ModuleHandle = NULLHANDLE; /* Module handle */
PFN ModuleAddr = 0; /* Pointer to a system function */
ULONG ModuleType = 0; /* Module type */
APIRET rc = NO_ERROR; /* Return code */
/* Load the DLL */
rc = DosLoadModule(LoadError, /* Failure information buffer */
sizeof(LoadError), /* Size of buffer */
ModuleName, /* Module to load */
&ModuleHandle); /* Module handle returned */
/* Get the handle to the function draw() */
rc = DosQueryProcAddr(ModuleHandle, /* Handle to module */
1L, /* No ProcName specified */
"draw", /* ProcName (not specified) */
&ModuleAddr); /* Address returned */
/* Unload the DLL */
rc = DosFreeModule(ModuleHandle);
}
|
The code in Listing 7 loads the shared library libdisplay.so, gets a reference
to function draw(), and then unloads it.
Listing 7. Linux example for loading shared libraries
void* handle, *handle2; /* Handle to the symbols */
/* Get the handle to the shared library. Passing 0 or NULL as
* first parameter to dlopen returns the handle to "main" program
*/
handle = dlopen("libdisplay.so", RTLD_LAZY);
if (handle != NULL)
{
/* Get the handle to the symbol "draw" */
handle2 = dlsym(handle, "draw");
if (handle2 != NULL)
{
/* use the function */
}
/* When finished, unload the shared library */
dlclose(handle);
}
|
Conclusion
This article, the third in a series, covered the mapping of OS/2 to
Linux with respect to interval timers and shared libraries. Use this series
as a reference when you undertake any migration activity
involving OS/2 to Linux. With these tips and caveats, you can simplify your migration activity and arrive at a suitable migration design. Refer to the Linux man
pages for more details on the Linux calls mentioned here.
Be sure to read
the previous articles in this series, and do also send us news of your own migration worries, stories,
triumphs, and questions.
Resources
- Be sure to read the first two installments in this series, "Migrate
your apps from OS/2 to Linux: Part 1. Threads, mutexes, and
semaphores" (developerWorks, February 2004), and "Migrate
your apps from OS/2 to Linux: Part 2. Memory management, IPC, and file
handling" (developerWorks, May 2004).
- The Design of
OS/2 by H.M. Deitel and M.S. Kogan (Addison-Wesley, 1992) can
still be found at used book sites; the ISBN is 0-201-54889-5.
- Don't forget to check the Linux "info" and man pages for specific
calls for more details on programming calls in Linux.
- C++ programmers can refer to "Comparing
IBM Open Collection Classes and STL Collection Classes"
(developerWorks, April 2003), which covers the mapping between IBM Open
Collection Classes and Standard Template Library (STL) Collection Classes.
- IBM's OS/2
Strategy for 2004 recommends a phased transition to the WebSphere software
platform.
- Most of the IBM eServer
servers now come bundled with Linux.
- IBM Redbooks offers a OS/2
Server Transition guide for OS/2 administrators who are planning a
transition to Windows or Linux.
- IBM Global Services Linux
Application Porting provides OS/2-to-Linux custom application
assessment, porting, and testing services.
- "Porting
OS/2 applications to Linux (in C)" (developerWorks, March 2002)
covers issues encountered by the LAN Distributed Platform (LANDP) for
Linux team while porting LANDP from OS/2 to Linux.
- You can also use OS/2 and Linux together on the same machine. Check
out How to
use Linux and OS/2 Together for a list of resources from the Southern
California OS/2 User Group, and the SUSE Linux portal's OS/2 and
Linux with the OS/2 Boot Manager for easy directions on dual-booting.
-
Find more resources for Linux developers in the developerWorks Linux
section.
- Browse for books on these and other technical topics.
- Develop and test your Linux applications using the latest IBM tools and middleware with a developerWorks Subscription: you get IBM software from WebSphere®, DB2®, Lotus®, Rational®, and Tivoli®, and a license to use the software for 12 months, all for less money than you might think.
- Download no-charge trial versions of selected developerWorks Subscription products that run on Linux, including WebSphere Studio Site Developer, WebSphere SDK for Web services, WebSphere Application Server, DB2 Universal Database Personal Developers Edition, Tivoli Access Manager, and Lotus Domino Server, from the Speed-start your Linux app section of developerWorks. For an even speedier start, help yourself to a product-by-product collection of how-to articles and tech support.
About the authors  | |  | Dinakar Guniguntala holds a B.Eng. degree in Computer Engineering from
REC Surat. He has been employed with IBM Global Services, India, since
February, 1998, where he works on operating system internals. He has worked
on the OS/2 kernel, graphics engine, and filesytems, as well as
on embedded Linux, the Linux
kernel, and Linux thread libraries. He can be reached at
dgunigun-at-in.ibm.com. |
 | |  | Sathyan Doraiswamy holds a B.Eng. degree in Electronics and
Communications from University Visvesvaraya College of Engineering,
Bangalore. He joined IBM India in June, 1997. He works for Engineering
& Technology Services and specializes in firmware for embedded and
real time systems. He has vast programming experience on Windows
and Linux device drivers and on VxWorks. You can contact him at
dsathyan-at-in.ibm.com. |
 | |  | Anand K. Santhanam has a B.Eng. in Computer
Science from Madras University, India. He has been in IBM Global
Services (Software Labs), India, since July, 1999. He is a member of the
Linux Group at IBM, where he has worked with ARM-Linux,
character/X-based device drivers, power management in embedded
systems, PCI device drivers, and multithreaded programming in Linux.
Other areas of interest are OS internals and networking. You can
contact him at asanthan-at-in.ibm.com. |
 | |  | Srinivasan S. Muthuswamy has a B.Eng. in
Computer Engineering from the Government College of Technology, Coimbatore, India. He
has been in IBM Global Services, India since Aug 2000. He has worked
on multithreaded applications in Linux. He has worked on Web
applications using WSBCC/Java technologies/Websphere/MQSeries. He has
also worked on Crystal Reports and Lotus Domino. You can contact him
at
smuthusw-at-in.ibm.com. |
 | |  | Rinson Antony has a Bachelor of Engineering degree in Computer
Engineering from Bombay University, India. He has been in IBM Global
Services, India since July 2000. He has worked on multithreaded
applications in Linux and Web applications using Java
technologies/Websphere/XML. Other areas of interest are PCI device
drivers and networking. You can contact him at
arinson-at-in.ibm.com. |
 | |  | Brahmaiah Vallabhaneni has a B.Eng. degree from BITS,
Pilani, India. He has been in IBM Global Services, India, since August,
2002. He has worked on GNU C Compiler porting, character device
drivers, power management in embedded systems, PCI device drivers, and
multithreaded programming in Linux.
Other areas of interest are Linux internals and networking. You can
contact him at
bvallabh-at-in.ibm.com. |
Rate this page
|  |