将应用程序从 OS/2 移植到 Linux 上: 第 1 部分,线程、互斥锁、信号量POSIX、OS/2 以及如何在两者之间架起桥梁 |
级别: 初级 Dinakar Guniguntala, 资深软件工程师, IBM 2004 年 4 月 01 日 Linux 是新千年里最杰出的操作系统,而传统的操作系统,如 OS/2,现在正在逐渐淘汰出局。本系列文章是为那些正经受移植痛苦的开发人员撰写的,可以帮助他们将 OS/2 系统驱动和应用程序移植到 Linux 上。文中介绍了如何将各种 OS/2 调用一一映射到 Linux 上,涉及的内容包括线程、IPC、内存管理、定时器处理、文件处理等。此外,文中还介绍了一些可以从 OS/2 映射到 Linux 上的预处理器指令和编译器/链接器选项。本系列文章总共三篇,本文是其中的第一篇。 本文介绍了有关线程和同步原语映射方面的知识。本系列的后续文章将会介绍系统调用、网络调用和其他编程指令的情况。 线程是在 OS/2 上的基本执行单元,也是 OS/2 进程内部的可调度单元。线程的调度及优先级是完全在内核中处理的。 Linux 内核使用的是进程模型,而不是线程模型。然而,Linux 内核为创建线程提供了一种轻量级的进程框架,而线程的真正实现是在用户空间中。目前,可用的线程库有很多种(LinuxThreads、NGPT、NPTL,等等)。本文的研究是以 LinuxThreads 库为基础进行的,但是其内容对 Rad Hat 的 Native POSIX Threading Library(本地 POSIX 线程库,NPTL)也同样适用。 本节介绍 OS/2 和 Linux 中的线程机制。内容涉及创建线程、设置线程属性以及改变线程优先级等所使用的调用。OS/2 与 Linux 中这些调用的对应关系如下表所示:
类别一列表示该 OS/2 指令是可映射的,还是特定于上下文的:
在 OS/2 中,我们用
Linux 则调用 pthread 库中的
在 OS/2 系统调用 在 Linux 库调用 在 OS/2 中,系统调用 在 Linux 中,库调用 OS/2 系统调用 在 Linux 中,堆栈大小是在属性对象中设置的;也就是说,我们将
请注意,所有形如
在 OS/2 中并不存在显式地维护与线程终止有关的线程状态。不过,我们可以通过调用 在 Linux 中,缺省情况下线程是以一种可连接(joinable)状态被创建的。在可连接状态下,其他线程可以在一个线程终止时对其进行同步,然后用 OS/2 用
Linux 用
在分离(detached)状态下,线程的资源在线程终止时会被立即释放。您可以通过对线程属性对象调用
以可连接状态创建的线程以后还可以使用
在 OS/2 中,系统调用 Linux 中的等价库调用为
OS/2 用系统调用
在本例中,scope 是一个进程的
其中,参数 Linux 中提供了很多调用,可以用来修改或改变线程的优先级。您应该根据应用程序的上下文环境选择使用不同的调用。 Linux 系统调用 在 OS/2 中,优先级的范围是从 0 (优先级最低)到 31 (优先级最高)。但是在 Linux 中,普通的非实时进程优先级范围都是从 -20(优先级最高)到 +20(优先级最低)。在使用之前,必须对优先级进行映射。
Linux 系统调用
参数 清单 1. 修改调度优先级 struct sched_param { ... int sched_priority; ... }; 此处, 在 Linux 中,对于一种已知的调度策略来说,我们也可以使用系统调用
参数
下面这个例子可以非常清楚地说明创建线程和修改优先级的实现从 OS/2 到 Linux 的映射方式。 在这个 OS/2 的例子中,thread1 是一个普通的线程,而 thread2 则是一个时间关键型实时线程。 清单 2. OS/2 线程代码
main ()
...
{
enum ...{StackSize = 120*1024}; /**//* stack size set to 120 K */ /**//* create a thread */ int thread1 = _beginThread (RegularThread, NULL, StackSize, NULL); int thread2 = _beginThread (CriticalThread, NULL, StackSize, NULL); } /**/ /* Normal /Regular Priority Thread */ static void RegularThread ( void * ) ... { /**//* Set the priority of the thread. 0 is passed as an argument to identify the current thread */ int iRc = DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, 20, 0); _endThread(); } /**/ /* Realtime time critical Priority Thread */ static void CriticalThread ( void * ) ... { int iRc = DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 20, 0); _endThread(); } 下面是与上面这段 OS/2 代码等价的 Linux 代码: 清单 3. 等价的 Linux 线程代码
#define
STATIC 0
static void * RegularThread ( void * ); static void * CriticalThread ( void * ); /**/ /* Regular non-realtime Thread function */ static void * RegularThread ( void * d) ... { int priority = 10; //0 for Regular, +20 for Idletime /**//* Set the priority - normal non-realtime priority */ int irc = setpriority(PRIO_PROCESS, 0, priority); pthread_exit(NULL); } /**/ /* Time Critical Realtime Thread function */ static void * CriticalThread ( void * d) ... { if (STATIC == 0) ...{ /**//* change the thread priority dynamically */ struct sched_param param; // scheduling priority int policy = SCHED_RR; // scheduling policy /**//* Get the current thread id */ pthread_t thread_id = pthread_self(); /**//* To set the scheduling priority of the thread */ param.sched_priority = 99; int irc = pthread_setschedparam(thread_id, policy, ¶m); } pthread_exit(NULL); } int main ( void ) ... { pthread_t thread1, thread2; // thread identifiers pthread_attr_t threadAttr1, threadAttr2; // thread attributes struct sched_param param; // scheduling priority int policy = SCHED_RR; // scheduling policy - real time int irc, rc; rc = pthread_attr_init(&threadAttr1); /**//* init the attr 1*/ rc = pthread_attr_init(&threadAttr2); /**//* init the attr 2*/ /**//* Set the stack size of the thread */ irc = pthread_attr_setstacksize(&threadAttr1, 120*1024); irc = pthread_attr_setstacksize(&threadAttr2, 120*1024); /**//* Set thread to detached state. No need for pthread_join*/ irc = pthread_attr_setdetachstate(&threadAttr1, PTHREAD_CREATE_DETACHED); irc = pthread_attr_setdetachstate(&threadAttr2, PTHREAD_CREATE_DETACHED); if (STATIC == 1) ...{ /**//* priority is set statically */ /**//* Set the policy of the thread to real time*/ irc = pthread_attr_setschedpolicy(&threadAttr2, policy); /**//* Set the scheduling priority of the thread - max priority*/ param.sched_priority = 99; irc = pthread_attr_setschedparam(&threadAttr2, ¶m); } /**//* Create the threads */ irc = pthread_create(&thread1, &threadAttr1, RegularThread, NULL); irc = pthread_create(&thread2, &threadAttr2, CriticalThread, NULL); /**//* Destroy the thread attributes */ irc = pthread_attr_destroy(&threadAttr1); irc = pthread_attr_destroy(&threadAttr2); }
在互斥锁的映射中,要考虑以下几点:
互斥锁的映射如下表所示:
在 OS/2 中,系统调用
参数 在 Linux 中,我们可以使用 pthread 库的
在 Linux 中,有“三种”互斥锁。互斥锁的“类型”决定了如果一个线程试图对一个已经使用
我们可以使用两种方法来设置互斥锁的“类型”。静态的设置方法如下:
另外一种设置互斥锁“类型”的方法是使用一个互斥锁的属性对象。要实现这种功能,就要在调用
参数“kind”允许的值如下所示:
属性对象可以使用
OS/2 使用
参数 在 Linux 中,pthread 库调用
在这两个调用中,第一个 而 注意,在 Linux 中并没有超时选项。在一个循环(该循环计数超时值)的延时期间执行一个非阻塞的 OS/2 使用
Linux 使用
注意,互斥函数并不是异步信号安全的,也不应该在信号处理函数中调用。通常,在一个信号处理函数中调用 在 OS/2 中,
在 Linux 中,
在 OS/2 中,
在 Linux 中,我们并不能使用
清单 4. OS/2 互斥锁代码
hmtx hmtxSem;
//
semaphore handle
unsigned long ulRc; // return code /**/ /* Create a un named mutex semaphore */ ulRc = DosCreateMutexSem (NULL, & hmtxSem, 0 , FALSE); ulRc = DosRequestMutexSem (hmtxSem, (unsigned long ) SEM_INDEFINITE_WAIT); /**/ /* Access the shared resource */ ... /**/ /* Release the mutex */ ulRc = DosReleaseMutexSem(hmtxSem); ... /**/ /* Closes the semaphore */ ulrc = DosCloseMutexSem (hmtxSem); 请试着将这段代码与下面的 Linux 代码比较一下: 清单 5. Linux 互斥锁代码
/**/
/* Declare the mutex */
pthread_mutex_t mutex; /**/ /* Attribute for the mutex */ pthread_mutexattr_t mutexattr; /**/ /* Set the mutex as a recursive mutex */ pthread_mutexattr_settype( & mutexattr, PTHREAD_MUTEX_RECURSIVE_NP); /**/ /* create the mutex with the attributes set */ pthread_mutex_init( & mutex, & mutexattr); /**/ /* lock the mutex */ pthread_mutex_lock ( & mutex); /**/ /* access the shared resource */ .. /**/ /* unlock the mutex */ pthread_mutex_unlock ( & mutex); ... /**/ /* destroy the attribute */ pthread_mutexattr_destroy( & mutexattr) /**/ /* Close/destroy the semaphore */ irc = pthread_mutex_destroy ( & mutex); 下面这个例子说明了在试图获取一个互斥锁时,如何模拟超时功能: 清单 6. 在 Linux 中模拟超时功能
#define
TIMEOUT 100 /* 1 sec */
struct timespec delay; /**/ /* Declare the mutex */ pthread_mutex_t mutex; while (timeout < TIMEOUT ) ... { delay.tv_sec = 0; delay.tv_nsec = 1000000; /**//* 1 milli sec */ irc = pthread_mutex_trylock(&mutex); if (!irc) ...{ /**//* we now own the mutex */ break; } else ...{ /**//* check whether somebody else has the mutex */ if (irc == EPERM ) ...{ /**//* sleep for delay time */ nanosleep(&delay, NULL); timeout++ ; } else...{ /**//* error */ } } }
除了 pthread 条件变量之外,Linux 还提供了 POSIX 信号量来映射 OS/2 的事件信号量构造(Linux 还提供了 SVR 兼容的 semop、semctl 等类似的调用,但是本文的目的仅限于介绍 POSIX 和 LinuxThreads 实现)。它们都有自己的优点和缺点,您需要根据应用程序的逻辑来决定到底使用哪种技术。在对事件信号量进行映射时,要考虑以下几点:
总之,只有在不需要超时和广播唤醒的情况下才可以考虑 POSIX 信号量。如果应用程序逻辑需要广播唤醒和超时,可以使用 pthread 条件变量。
OS/2 使用
此处, 在 Linux 中,系统调用
其中 Linux pthreads 使用
在 OS/2,
此处参数 Linux POSIX 信号量使用
在 POSIX 信号量中,没有超时功能。这可以在一个循环中执行非阻塞的
Linux pthreads 使用
另一方面,如果调用线程需要被阻塞一段指定的时间,就可以使用
此处, OS/2 使用
Linux POSIX 信号量使用
注意,条件函数不是异步信号安全的,不应该在信号处理函数中调用。在特殊情况下,如果在信号处理函数中调用 OS/2 使用
Linux POSIX 信号量使用
在 Linux pthreads 中,
清单 7. OS/2 信号量代码
HEV hevIpcInterrupt;
unsigned long ulPostCnt = 0 ; unsigned long ulrc; // return code unsigned long ulTimeout = 10 ; // timeout value /**/ /* create event semaphore */ DosCreateEventSem (NULL, & hevIpcInterrupt, 0 , TRUE); /**/ /* In Thread A */ /**/ /* Wait forever for event to be posted */ DosWaitEventSem (hevIpcInterrupt, (unsigned long ) SEM_INDEFINITE_WAIT); /**/ /* immediately unblocked as the semaphore is already posted */ /**/ /* Waits until the semaphore is posted */ DosWaitEventSem (hevIpcInterrupt, (unsigned long ) SEM_INDEFINITE_WAIT); /**/ /* In Thread B */ DosPostEventSem(hevIpcInterrupt); /**/ /* Close the semaphore */ ulrc = DosCloseEventSem (hevIpcInterrupt); 下面的 Linux 例子使用 pthread 条件变量,用来在两个线程 A 和 B 之间进行同步: 清单 8. Linux 条件变量
pthread_mutex_t mutex
=
PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condvar = PTHREAD_COND_INITIALIZER; struct timeval tvTimeNow; // Absolute current time struct timezone tzTimeZone; // Timezone struct timespec tsTimeOut; // Input for timedwait /**/ /* In Thread A */ ... pthread_mutex_lock( & mutex); /**/ /* signal one thread to wake up */ pthread_cond_signal( & condvar); pthread_mutex_unlock( & mutex); /**/ /* this signal is lost as no one is waiting */ /**/ /* In Thread B */ pthread_mutex_lock( & mutex); pthread_cond_wait( & condvar, & mutex); pthread_mutex_unlock( & mutex); /**/ /* Thread B blocks indefinitely */ /**/ /* --------------------------------------------------------------------- --------------------------------------------------------------------- */ /**/ /* One way of avoiding losing the signal is as follows */ /**/ /* In Thread B - Lock the mutex early to avoid losing signal */ pthread_mutex_lock ( & mutex); /**/ /* Do work */ ....... /**/ /* This work may lead other threads to send signal to thread B */ /**/ /* Thread A now tries to take the mutex lock to send the signal but gets blocked */ ... pthread_mutex_lock( & mutex); /**/ /* Thread B: Get the current time */ gettimeofday ( & tvTimeNow, & tzTimeZone); /**/ /* Calculate the absolute end time - 10 seconds wait */ tsTimeOut.tv_sec = tvTimeNow.tv_sec + 10 ; /**/ /* Thread B waits for the specified time for the signal to be posted */ pthread_cond_timedwait ( & condvar, & mutex, & tsTimeOut ); /**/ /* Thread A now gets the lock and can signal thread B to wake up */ pthread_cond_signal( & condvar */ pthread_mutex_unlock( & mutex); ... /**/ /* Thread B unblocks upon receipt of signal */ pthread_mutex_unlock ( & mutex); 下面的清单使用 POSIX 信号量来实现对线程 A 和 B 之间的同步: 清单 9. Linux POSIX 信号量
sem_t sem;
/**/
/* semaphore object */
int irc; /**/ /* return code */ /**/ /* Initialize the semaphore - count is set to 1*/ irc = sem_init (sem, 0 , 1 ); ... /**/ /* In Thread A */ /**/ /* Wait for event to be posted */ sem_wait ( & sem); /**/ /* Unblocks immediately as semaphore initial count was set to 1 */ ....... /**/ /* Wait again for event to be posted */ sem_wait ( & sem); /**/ /* Blocks till event is posted */ /**/ /* In Thread B */ /**/ /* Post the semaphore */ ... irc = sem_post ( & sem); /**/ /* Destroy the semaphore */ irc = sem_destroy( & sem);
本文介绍了从 OS/2 到 Linux 的映射中有关线程、互斥锁和事件信号量的知识。在将任何程序从 OS/2 移植到 Linux 上时,您都可以参考这些内容。本文中给出的提示和警告有助于简化您移植程序的设计;有关本文中所提到的所有 Linux 调用的更详细内容,您可以参考相关的手册页。本系列文章的下一篇将介绍有关内存管理、文件处理和设备驱动接口的系统调用的映射。
|