本系列文章可以帮助您将 Win32 C/C++ 应用程序移植到 POWER 上的 Linux。高级程序员 Nam Keung 和 pSeries? Linux 技术顾问 Chakarat Skawratananond 从互斥(mutex)应用程序接口(application program interface,API)的角度阐述了从 Win32 到 Linux 的映射。本系列的 第 1 部分 集中关注的是 Win32 API 的映射。
介绍
本文关注的是互斥原语(primitives)。建议您在继续阅读之前先回顾本系列 第 1 部分 中的下列章节:
互斥
如下面的 表 1 所示,互斥提供线程间资源的独占访问控制。它是一个简单的锁,只有持有它的线程才可以释放那个互斥。它确保了它们正在访问的共享资源的完整性(最常见的是共享数据),因为在同一时刻只允许一个线程访问它。
Win32 | Linux |
CreateMutex(0, FALSE, 0); |
pthread_mutex_init(&mutex, NULL)) |
CloseHandle(mutex); |
pthread_mutex_destroy(&mutex) |
WaitForSingleObject(mutex, INFINITE)) |
pthread_mutex_lock(&mutex) |
ReleaseMutex(mutex); |
pthread_mutex_unlock(&mutex) |
创建互斥
在 Win NT/Win2K 中,所有互斥都是递归的。
在 Win32 中,CreateMutex()
为当前进程中的线程提供资料的独占访问控制。此方法让线程可以串行化对进程内资源的访问。创建了互斥句柄(mutual exclusion handle)后,当前进程中的所有线程都可以使用它(见下面的 清单 1)。
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lMutexAttributes, BOOL lInitialOwner, LPCTSTR lName ) |
Linux 使用 pthread 库调用 pthread_mutex_init()
来创建互斥,如下面的 清单 2 所示。
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); |
Linux 有三种类型的互斥。互斥类型决定了在 pthread_mutex_lock 中线程尝试锁定一个它已经持有的互斥时所发生的情形:
pthread_mutex_lock()
去锁定互斥时,进行调用的线程会永远挂起。
pthread_mutex_lock()
立即返回成功返回代码。
pthread_mutex_lock()
立即返回错误代码 EDEADLK。
可以以两种方式设置互斥的类型。清单 3 介绍了设置互斥的静态方法。
/* For Fast mutexes */ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /* For recursive mutexes */ |
您可以使用这个函数来锁定互斥:pthread_mutex_lock(pthread_mutex_t *mutex)
。这个函数会获得一个指向它正在尝试锁定的互斥的指针。当互斥被锁定或者发生错误时,函数返回。那个错误不是归咎于被锁定的互斥。函数会等待互斥被解锁。
设置互斥的另一种方式是使用互斥属性对象。为此,要调用 pthread_mutexattr_init()
来初始化对象,然后调用 pthread_mutexattr_settype()
来设置互斥的类型,如下面的 清单 4 所示。
int pthread_mutexattr_init(pthread_mutexattr_t *attr); int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind); |
使用下面的函数解开对互斥的锁定(见 清单 5):
这里是创建互斥的示例代码(见下面的 6 和 7)。
pthread_mutex_unlock(pthread_mutex_t *mutex)) |
清单 6. Win32 示例代码
HANDLE mutex; mutex = CreateMutex(0, FALSE, 0); if (!(mutex)) { return RC_OBJECT_NOT_CREATED; } |
清单 7. 相应的 Linux 代码
pthread_mutexattr_t attr; pthread_mutex_t mutex; pthread_mutexattr_init (&attr); if (rc = pthread_mutex_init(&mutex, &attr)) { return RC_OBJECT_NOT_CREATED; } |
销毁互斥
在 Win32 中,CloseHandle()
方法(见 清单 8)可以删除为当前进程中资源提供独占访问控制的对象。删除那个对象后,那个互斥对象就会无效,直到 CloseHandle()
方法通过调用 CreateMutex
重新初始化它。
当不再对资源进行独占访问后,您应该调用这个方法销毁它。如果您需要放弃那个对象的所有权,那么应该调用 ReleaseMutex()
方法。
在 Linux 中,pthread_mutex_destroy()
会销毁互斥对象,这会释放它可能会持有的资源。它还会检查互斥在那个时刻是不是解除锁定的(见清单 9)。
if(WaitForSingleObject(mutex, (DWORD)0) == WAIT_TIMEOUT ) return RC_NOT_OWNER; CloseHandle(mutex); |
清单 9. Linux 代码
if (pthread_mutex_destroy(&mutex) == EBUSY) return RC_NOT_OWNER; |
锁定互斥
在 Win32 中,WaitForSingleObject()
(见 清单 10)会阻塞对当前进程内资源的独占访问的请求。进程可以通过下面的方式阻塞请求:
Linux 使用 pthread_mutex_lock()
(见 清单 11)。
您还可以使用 pthread_mutex_trylock()
来测试某个互斥是否已经被锁定,而不需要真正地去锁定它。如果另一个线程锁定了那个互斥,则 pthread_mutex_trylock
将不会阻塞。它会立即返回错误代码 EBUSY。
if ((rc = WaitForSingleObject(mutex, INFINITE)) == WAIT_FAILED) return RC_LOCK_ERROR; |
清单 11. Linux 代码
if (rc = pthread_mutex_lock(&mutex)) return RC_LOCK_ERROR; |
释放或者解锁互斥
Win32 使用 ReleaseMutex()
(见 清单 12)来释放对资源的独占访问。如果进行调用的线程并不拥有那个互斥对象,则这个调用可能会失败。
Linux 使用 pthread_mutex_unlock()
来释放或者解锁互斥(见清单 13)。
If (! ReleaseMutex(mutex)) { rc = GetLastError(); return RC_UNLOCK_ERROR; } |
清单 13. Linux 示例代码
if (rc = pthread_mutex_unlock(&mutex)) return RC_UNLOCK_ERROR; |
Mutex 示例代码
这里是获得进程内互斥的 Win32 示例代码(见 Listing 14):
#include <stdio.h> #include <stdlib.h> #include <windows.h> void thrdproc (void *data); //the thread procedure (function) to be executed HANDLE mutex; int main( int argc, char **argv ) { int hThrd; unsigned stacksize; HANDLE *threadId1; HANDLE *threadId2; int arg1; DWORD rc; if( argc < 2 ) arg1 = 7; else arg1 = atoi( argv[1] ); printf( "Intra Process Mutex test.\n" ); printf( "Start.\n" ); mutex = CreateMutex(0, FALSE, 0); if (mutex==NULL) return RC_OBJECT_NOT_CREATED; printf( "Mutex created.\n" ); if ((rc = WaitForSingleObject(mutex, INFINITE)) == WAIT_FAILED) return RC_LOCK_ERROR ; printf( "Mutex blocked.\n" ); if( stacksize < 8192 ) stacksize = 8192; else stacksize = (stacksize/4096+1)*4096; hThrd = _beginthread( thrdproc, // Definition of a thread entry NULL, stacksize, "Thread 1"); if (hThrd == -1) return RC_THREAD_NOT_CREATED); *threadId1 = (HANDLE) hThrd; hThrd = _beginthread( thrdproc, // Definition of a thread entry NULL, stacksize, Thread 2"); if (hThrd == -1) return RC_THREAD_NOT_CREATED); *threadId2 = (HANDLE) hThrd; printf( "Main thread sleeps 5 sec.\n" ); Sleep( 5*1000 ); if (! ReleaseMutex(mutex)) { rc = GetLastError(); return RC_UNLOCK_ERROR; } printf( "Mutex released.\n" ); printf( "Main thread sleeps %d sec.\n", arg1 ); Sleep( arg1 * 1000 ); if( WaitForSingleObject(mutex, (DWORD)0) == WAIT_TIMEOUT ) return RC_NOT_OWNER; CloseHandle(mutex); printf( "Mutex deleted. (%lx)\n", rc ); printf( "Main thread sleeps 5 sec.\n" ); Sleep( 5*1000 ); printf( "Stop.\n" ); return 0; } void thread_proc( void *pParam ) { DWORD rc; printf( "\t%s created.\n", pParam ); if ((rc = WaitForSingleObject(mutex, INFINITE)) == WAIT_FAILED) return RC_LOCK_ERROR; printf( "\tMutex blocked by %s. (%lx)\n", pParam, rc ); printf( "\t%s sleeps for 5 sec.\n", pParam ); Sleep( 5* 1000 ); if (! ReleaseMutex(mutex)) { rc = GetLastError(); return RC_UNLOCK_ERROR; } printf( "\tMutex released by %s. (%lx)\n", pParam, rc ); } |
相应的获得进程内互斥的 Linux 示例代码(见 清单 15):
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <pthread.h> void thread_proc (void * data); pthread_mutexattr_t attr; pthread_mutex_t mutex; int main( int argc, char **argv ) { pthread_attr_t pthread_attr; pthread_attr_t pthread_attr2; pthread_t threadId1; pthread_t threadId2; int arg1; int rc = 0; if( argc < 2 ) arg1 = 7; else arg1 = atoi( argv[1] ); printf( "Intra Process Mutex test.\n" ); printf( "Start.\n" ); pthread_mutexattr_init( &attr ); if ( rc = pthread_mutex_init( &mutex, NULL)) { printf( "Mutex NOT created.\n" ); return RC_OBJECT_NOT_CREATED; } printf( "Mutex created.\n" ); if (rc = pthread_mutex_lock (&mutex)) { printf( "Mutex LOCK ERROR.\n" ); return RC_LOCK_ERROR; } printf( "Mutex blocked.\n" ); if (rc = pthread_attr_init(&pthread_attr)) { printf( "pthread_attr_init ERROR.\n" ); return RC_THREAD_ATTR_ERROR; } if (rc = pthread_attr_setstacksize(&pthread_attr, 120*1024)) { printf( "pthread_attr_setstacksize ERROR.\n" ); return RC_STACKSIZE_ERROR; } if (rc = pthread_create(&threadId1, &pthread_attr, (void*(*)(void*))thread_proc, "Thread 1" )) { printf( "pthread_create ERROR.\n" ); return RC_THREAD_NOT_CREATED; } if (rc = pthread_attr_init(&pthread_attr2)) { printf( "pthread_attr_init2 ERROR.\n" ); return RC_THREAD_ATTR_ERROR; } if (rc = pthread_attr_setstacksize(&pthread_attr2, 120*1024)) { printf( "pthread_attr_setstacksize2 ERROR.\n" ); return RC_STACKSIZE_ERROR; } if (rc = pthread_create(&threadId2, &pthread_attr2, (void*(*)(void*))thread_proc, "Thread 2" )) { printf( "pthread_CREATE ERROR2.\n" ); return RC_THREAD_NOT_CREATED; } printf( "Main thread sleeps 5 sec.\n" ); sleep (5); if (rc = pthread_mutex_unlock(&mutex)) { printf( "pthread_mutex_unlock ERROR.\n" ); return RC_UNLOCK_ERROR; } printf( "Mutex released.\n" ); printf( "Main thread sleeps %d sec.\n", arg1 ); sleep(arg1); pthread_mutex_destroy(&mutex); printf( "Main thread sleeps 5 sec.\n" ); sleep( 5 ); printf( "Stop.\n" ); return 0; } void thread_proc( void *pParam ) { int nRet; printf( "\t%s created.\n", pParam ); if (nRet = pthread_mutex_lock(&mutex)) { printf( "thread_proc Mutex LOCK ERROR.\n" ); return RC_LOCK_ERROR; } printf( "\tMutex blocked by %s. (%lx)\n", pParam, nRet ); printf( "\t%s sleeps for 5 sec.\n", pParam ); sleep(5); if (nRet = pthread_mutex_unlock(&mutex)) { printf( " thread_proc :pthread_mutex_unlock ERROR.\n" ); return RC_UNLOCK_ERROR; } printf( "\tMutex released by %s. (%lx)\n", pParam, nRet ); } |
这里是获得进程间互斥的另一 Win32 示例代码。
互斥是系统范围内对象,可以由多个进程使用。如果程序 A 创建一个互斥,则程序 B 可以使用同一个互斥。互斥有名称,并且,一个给定名称的互斥在同一机器上同一时刻只能存在一个。如果您创建了一个名为“My Mutex” 的互斥,则任何其他程序都不能使用这个名称创建互斥,如下面的清单 16 和 18 所示。
#include <stdio.h> #include <windows.h> #define WAIT_FOR_ENTER printf( "Press ENTER\n" );getchar() int main() { HANDLE mutex; DWORD rc; printf( "Inter Process Mutex test - Process 1.\n" ); printf( "Start.\n" ); SECURITY_ATTRIBUTES sec_attr; sec_attr.nLength = sizeof( SECURITY_ATTRIBUTES ); sec_attr.lpSecurityDescriptor = NULL; sec_attr.bInheritHandle = TRUE; mutex = CreateMutex(&sec_attr, FALSE, "My Mutex"); if( mutex == (HANDLE) NULL ) return RC_OBJECT_NOT_CREATED; printf( "Mutex created.\n" ); WAIT_FOR_ENTER; if ( WaitForSingleObject(mutex, INFINITE) == WAIT_FAILED) return RC_LOCK_ERROR; printf( "Mutex blocked.\n" ); WAIT_FOR_ENTER; if( ! ReleaseMutex(mutex) ) { rc = GetLastError(); return RC_UNLOCK_ERROR; } printf( "Mutex released.\n" ); WAIT_FOR_ENTER; CloseHandle (mutex); printf( "Mutex deleted.\n" ); printf( "Stop.\n" ); return OK; } |
在此,Linux 实现使用的是 System V Interprocess Communications(IPC)函数,如清单 17 和 19 所示。
#include <sys/sem.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #define WAIT_FOR_ENTER printf( "Press ENTER\n" );getchar() union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* array for GETALL, SETALL */ struct seminfo __buf; /* buffer for IPC info */ }; main() { int shr_sem; key_t semKey; struct sembuf semBuf; int flag; union semun arg; printf( "Inter Process Mutex test - Process 1.\n" ); printf( "Start.\n" ); flag = IPC_CREAT; if( ( semKey = (key_t) atol( "My Mutex" ) ) == 0 ) return RC_INVALID_PARAM; flag |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; shr_sem = (int) semget( semKey, 1, flag ); if (shr_sem < 0) return RC_OBJECT_NOT_CREATED; arg.val = 1; if (semctl(shr_sem, 0, SETVAL, arg) == -1) return RC_OBJECT_NOT_CREATED; printf( "Mutex created.\n" ); WAIT_FOR_ENTER; semBuf.sem_num = 0; semBuf.sem_op = -1; semBuf.sem_flg = SEM_UNDO; if (semop(shr_sem, &semBuf, 1) != 0) return RC_LOCK_ERROR; printf( "Mutex blocked.\n" ); WAIT_FOR_ENTER; semBuf.sem_num = 0; semBuf.sem_op = 1; semBuf.sem_flg = SEM_UNDO; if (semop(shr_sem, &semBuf, 1) != 0) return RC_UNLOCK_ERROR; printf( "Mutex released.\n" ); WAIT_FOR_ENTER; semctl( shr_sem, 0, IPC_RMID ); printf( "Mutex deleted.\n" ); printf( "Stop.\n" ); return 0; |
清单 18. Win32 进程间示例代码 Process 2
#include <stdio.h> #include <windows.h> int main() { HANDLE mutex; printf( "Inter Process Mutex test - Process 2.\n" ); printf( "Start.\n" ); SECURITY_ATTRIBUTES sec_attr; sec_attr.nLength = sizeof( SECURITY_ATTRIBUTES ); sec_attr.lpSecurityDescriptor = NULL; sec_attr.bInheritHandle = TRUE; mutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, “My Mutex"); if( mutex == (HANDLE) NULL ) return RC_OBJECT_NOT_CREATED; printf( "Mutex opened. \n"); printf( "Try to block mutex.\n" ); if ( WaitForSingleObject(mutex, INFINITE) == WAIT_FAILED) return RC_LOCK_ERROR; printf( "Mutex blocked. \n" ); printf( "Try to release mutex.\n" ); if( ! ReleaseMutex(mutex) ) return RC_UNLOCK_ERROR; printf( "Mutex released.\n" ); CloseHandle (mutex); printf( "Mutex closed. \n"); printf( "Stop.\n" ); return OK; } |
清单 19. 相应的 Linux 示例代码 Process 2
#include <stdio.h> #include <sys/sem.h> #include <sys/stat.h> #include <sys/ipc.h> #include <unistd.h> int main() { int mutex; key_t semKey; struct sembuf semBuf; int flag; int nRet=0; printf( "Inter Process Mutex test - Process 2.\n" ); printf( "Start.\n" ); flag = 0; if( ( semKey = (key_t) atol( "My Mutex" ) ) == 0 ) return RC_INVALID_PARAM; flag |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; mutex = (int) semget( semKey, 1, flag ); if (mutex == -1) return RC_OBJECT_NOT_CREATED; printf( "Mutex opened \n"); printf( "Try to block mutex.\n" ); semBuf.sem_num = 0; semBuf.sem_op = -1; semBuf.sem_flg = SEM_UNDO; if (semop(mutex, &semBuf, 1) != 0) return RC_LOCK_ERROR; printf( "Mutex blocked. \n"); printf( "Try to release mutex.\n" ); semBuf.sem_num = 0; semBuf.sem_op = 1; semBuf.sem_flg = SEM_UNDO; if (semop(mutex, &semBuf, 1) != 0) return RC_UNLOCK_ERROR; printf( "Mutex released. \n"); printf( "Mutex closed. \n"); printf( "Stop.\n" ); return 0; } |
结束语
在本文中,我们介绍了互斥 API 从 Win32 到 Linux 的映射。我们还引用了一系列互斥示例代码来帮助您进行从 Win32 到 Linux 的迁移行动