Chinaunix首页 | 论坛 | 博客
  • 博客访问: 231925
  • 博文数量: 56
  • 博客积分: 2480
  • 博客等级: 大尉
  • 技术积分: 475
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-28 10:57
文章分类

全部博文(56)

文章存档

2012年(36)

2011年(4)

2010年(2)

2009年(14)

我的朋友

分类:

2009-10-26 10:12:07

将 Win32 C/C++ 应用程序迁移到 POWER 上的 Linux,第 2 部分: 互斥

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

将打印机的版面设置成横向打印模式

将此页作为电子邮件发送

将此页作为电子邮件发送


级别: 初级

Nam Keung (), 高级程序员, IBM 
Chakarat Skawratananond (), pSeries Linux 技术顾问, IBM 

2005 年 2 月 10 日
更新 2005 年 4 月 21 日

本系列文章可以帮助您将 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 中线程尝试锁定一个它已经持有的互斥时 所发生的情形:

Fast mutex:
当尝试使用 pthread_mutex_lock() 去锁定互斥时,进行调用的线程会永远挂起。
Recursive mutex:
pthread_mutex_lock() 立即返回成功返回代码。
Error check mutex:
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):

这里是创建互斥的示例代码(见下面的 67)。



pthread_mutex_unlock(pthread_mutex_t *mutex))



HANDLE mutex;
mutex = CreateMutex(0, FALSE, 0);
if (!(mutex))
{
return RC_OBJECT_NOT_CREATED;
}



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



if (pthread_mutex_destroy(&mutex) == EBUSY)
return RC_NOT_OWNER;





回页首


在 Win32 中,WaitForSingleObject()(见 清单 10) 会阻塞对当前进程内资源的独占访问的请求。进程可以通过下面的方式阻塞请求:

  1. 如果独占访问请求的资源没有被锁定,则这个方法锁定它。
  2. 如果独占访问的资源已经被锁定,则此方法阻塞那个调用线程,直到那个资源被解除锁定。

Linux 使用 pthread_mutex_lock()(见 清单 11)。

您还可以使用 pthread_mutex_trylock() 来测试某个互斥是否已经被锁定,而不需要真正 地去锁定它。如果另一个线程锁定了那个互斥,则 pthread_mutex_trylock 将不会阻塞。 它会立即返回错误代码 EBUSY。



if ((rc = WaitForSingleObject(mutex, INFINITE)) == WAIT_FAILED)
return RC_LOCK_ERROR;



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



if (rc = pthread_mutex_unlock(&mutex))
return RC_UNLOCK_ERROR;





回页首


这里是获得进程内互斥的 Win32 示例代码(见 Listing 14):



#include 
#include
#include
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 
#include
#include
#include
#include
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” 的互斥,则任何其他程序都不能使用这个名称创建互斥,如下面的清单 1618 所示。



#include 
#include
#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)函数,如清单 1719 所示。



#include 
#include
#include
#include
#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;



#include 
#include
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;
}



#include 
#include
#include
#include
#include
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 的迁移行动。本系列的下一篇文章将阐述信号量。

IBM Corporation 1994-2005。保留所有权利。

本文档中对 IBM 产品或服务的引用并不表示 IBM 想要让它们在所有国家都可用。

IBM、eServer 和 pSeries 是 IBM Corporation 在美国和/或其他国家或地区的商标。

Microsoft、Windows、Windows NT 和 Windows 徽标是 Microsoft Corporation 在美国和/或其他国家或地区的商标或注册商标。

Intel、Intel Inside(logos)、MMX 和 Pentium 是 Intel 公司在美国和/或其他国家或地区的商标。

UNIX 是 The Open Group 在美国和其他国家或地区的注册商标。

Linux 是 Linus Torvalds 在美国和/或其他国家或地区的商标。

其他公司、产品或服务名称可能是其他公司的商标或服务标记。

信息都是“按原样”发布,没有任何类型的保证。

所描述的所有的客户示例只是为了说明那些客户如何使用 IBM 产品,以及它们可能获得的结果。 不同客户所得到的实际环境代价和性能特性可能会不同。

涉及非 IBM 产品的信息可从这些产品的供应商、其出版说明或其他可公开获得的资料中获取, 并不构成 IBM 对此产品的认可。非 IBM 价目及性能数字资源取自可公开获得的信息, 包括供应商的声明和供应商的全球主页。 IBM 没有对这些产品进行测试,也无法确认其性能的精确性、 兼容性或任何其他关于非 IBM 产品的声明。有关非 IBM 产品性能的问题应当向这些产品的供应商提出。

所有关于 IBM 未来方向或意向的声明都可随时更改或收回,而不另行通知, 它们仅仅表示了目标和意愿而已。联系您本地的 IBM 办公人员或者 IBM 授权的转销商, 以获得特定的 Statement of General Direction 的全文。

这里所包含的信息可能陈述了预期的未来功能。上述信息并不打算作为对任何未来产品的特定性能级别、 功能或交付时间表的明确承诺。这样的承诺只会在 IBM 产品中作出。这里出现的信息用于表明 IBM 当前的投资和发展活动,作为一种信任,来帮助我们的客户规划未来。

性能是在受控环境中使用标准的 IBM 基准程序测试和估算的。任何用户实际的吞吐量或性能可能各不相同, 这取决于需要考虑的事项,例如用户作业流中的多道程序设计总量、I/O 配置、存储器配置和处理的工作负载。 因此,我们不能担保,个别用户所获得的吞吐量或性能改善等同于这里所列的值。

阅读(1209) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~