Mutex 本身是支持进程内和进程间两种类型的,平时在使用的时候用到的最多的还是进程内同步,昨天在看android framework的时候,发现其在barrier上还是使用了Mutex的进程间同步。实现方式还是蛮简单的,将pthread_mutex_t数据结构保存到共享内存上,从而实现多个进程共享同一内存,不像windows上指定一个名字就搞定了,废话少说,上代码:
//Mutex.h
#ifndef CXX_MUTEX_H
#define CXX_MUTEX_H
#include <pthread.h>
#include <string>
using std::string;
class Mutex {
public:
enum {
MUTEX_PRIVATE = 0, //process private
MUTEX_SHARE = 1 //process shared
};
/**
* Constructor
* @param type: MUTEX_PRIVATE or MUTEX_SHARE
* @param name: if type=MUTEX_SHARE, name must be not null .
*/
Mutex(int type, const char* name);
~Mutex();
int type() const;
/**
* @warnning:
* if Mutex is shared in process, the process which create mutex must call this function
*/
bool init();
/**
* @warnning:
* If Mutex is shared in process, the process which create mutex must call this function
*/
bool destroy();
bool lock();
bool unlock();
bool trylock();
private:
bool createDir(const char* dir);
bool write(int fd, const char* buff, int len);
bool saveShareMutexToFile();
private:
int m_type;
int m_fd;
string m_name;
pthread_mutex_t* m_mutex;
};
#endif
//Mutex.cpp
#include "Mutex.h"
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define DBG_MSG(fmt, ...) printf(fmt, ##__VA_ARGS__)
static const char* s_MutexDir = "/tmp/mutex/";
static const char* s_MutexSuffix = ".process.mutex";
Mutex::Mutex(int type, const char* name)
: m_type(type)
, m_fd(-1)
, m_mutex(NULL)
{
if (MUTEX_PRIVATE == m_type) {
m_mutex = new pthread_mutex_t();
pthread_mutex_init(m_mutex, NULL);
}
else {
if (NULL == name) {
name = "default";
}
createDir(s_MutexDir);
m_name = s_MutexDir;
m_name += name;
m_name += s_MutexSuffix;
}
}
Mutex::~Mutex()
{
if ((NULL != m_mutex) && (MUTEX_PRIVATE == m_type)) {
pthread_mutex_destroy(m_mutex);
delete m_mutex;
m_mutex = NULL;
}
if (MUTEX_SHARE == m_type) {
if (NULL != m_mutex) {
munmap(m_mutex, sizeof(pthread_mutex_t));
m_mutex = NULL;
}
if (-1 != m_fd) {
close(m_fd);
m_fd = -1;
}
}//if
}
int Mutex::type() const
{
return m_type;
}
bool Mutex::init()
{
if(MUTEX_SHARE == m_type) {
if (0 != access(m_name.c_str(), F_OK) != 0) {
saveShareMutexToFile();
}
//open file and create shm
m_fd = open(m_name.c_str(), O_RDWR);
if (m_fd == -1) {
DBG_MSG("%s %d: error=%d\n", __FILE__, __LINE__, errno);
return false;
}
m_mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t), PROT_READ|PROT_WRITE, MAP_SHAREDr, m_fd, 0);
if (-1l == (long)m_mutex) {
DBG_MSG("%s %d: error=%d\n", __FILE__, __LINE__, errno);
return false;
}
}
return true;
}
bool Mutex::destroy()
{
if(NULL != m_mutex) {
pthread_mutex_destroy(m_mutex);
}
}
bool Mutex::lock()
{
if (NULL == m_mutex) {
DBG_MSG("%s %d: m_mutex is null \n", __FILE__, __LINE__);
return false;
}
return (0 == pthread_mutex_lock(m_mutex));
}
bool Mutex::unlock()
{
if (NULL == m_mutex) {
DBG_MSG("%s %d: m_mutex is null \n", __FILE__, __LINE__);
return false;
}
return (0 == pthread_mutex_unlock(m_mutex));
}
bool Mutex::trylock()
{
if (NULL == m_mutex) {
DBG_MSG("%s %d: m_mutex is null \n", __FILE__, __LINE__);
return false;
}
return (0 == pthread_mutex_trylock(m_mutex));
}
bool Mutex::createDir(const char* dir)
{
if(NULL == dir) {
return false;
}
if (0 != access(s_MutexDir, F_OK)) {
string cmd = "mkdir -p ";
cmd += dir;
system(cmd.c_str());
}
return true;
}
bool Mutex::write(int fd, const char* buff, int len)
{
int writeNum = 0;
while((len - writeNum) > 0) {
writeNum += ::write(fd, buff + writeNum, len - writeNum);
}
return true;
}
bool Mutex::saveShareMutexToFile()
{
//write data to file
pthread_mutexattr_t mutexattr;
pthread_mutex_t mutex;
pthread_mutexattr_init(&mutexattr);
pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&mutex, &mutexattr);
int fd = open(m_name.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
write(fd, (const char*)&mutex, sizeof(pthread_mutex_t));
close(fd);
pthread_mutex_destroy(&mutex);
}
推荐使用方式:通常一个系统是由多个process组成的,其中根据其角色又分为前期初始化、事务处理、后期清理。建议在前期初始化的时候讲所有的用于进程间共享的Mutex前部初始化(init),在系统后期清理掉时候再将所有的共享Mutex销毁(destroy)。