Skip to main content

skip to main content

developerWorks  >  Linux  >

Migrate your apps from OS/2 to Linux: Part 3. Timer and DLL calls

High-resolution and interval timers, and shared and linked libraries

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


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;


   }



Back to top


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);

       }



Back to top


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



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


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top