实现IPC的方式有很多,今天我们来一起看下 共享内存QSharedMemory
两个进程中的通讯,我们分为内存的申请者(服务端),和内存的访问者(客户端)
我们先来看服务端,简短的代码解释下:
m_pSharememory = new QSharedMemory(this);
m_pSharememory->setKey("sharememory");
if(m_pSharememory->isAttached())//盘算是否连接到了共享内存块,如果是 就先分离
{
m_pSharememory->detach();
}
if(!m_pSharememory->create(256,QSharedMemory::ReadWrite))
{
qDebug()<<"ShareMemory creat failed";
return;
}
m_pSharememory->lock();
char *sm = static_cast<char *>(m_pSharememory->data());
QByteArray buff = "hello shareMemory";
memcpy(sm,buff.data(),static_cast<unsigned int>(buff.size()));
m_pSharememory->unlock();
-
创建共享内存对象
-
设置识别key(sharememory)
-
申请内存,这个内存不属于创建的对象,应该归操作系统所有,由操作系统回收。
-
往内存里面写入一个默认值,这里 m_pSharememory->data();返回指向共享内存段(如果已连接)内容的指针。否则返回null。在读取或写入共享内存之前,请记住使用lock()锁定共享内存,完成后请记住使用unlock()释放锁。
再来看服务端,简短的代码解释下:
m_pSharedMemory = new QSharedMemory(this);
m_pSharedMemory->setKey("sharememory");
if(!m_pSharedMemory->attach())//尝试访问该共享内存块
{
qDebug()<<" attach failed";
return;
}
m_pSharedMemory->lock();
char* sm = static_cast<char*>(m_pSharedMemory->data());
char *out = new char[static_cast<unsigned int>(m_pSharedMemory->size())];
memcpy(out,sm,static_cast<unsigned int>(m_pSharedMemory->size()));
m_pSharedMemory->unlock();
m_pSharedMemory->detach();
QByteArray buff(out);
qDebug()<<buff;
跟服务端相同的道理
创建QSharedMemory 对象,通过相同的key链接内存部分;
然后写入,读取共享内存部分的内容‘;’
向内存写入一个结构体
if(!m_pSharedMemory->attach())//尝试访问该共享内存块
{
qDebug()<<" attach failed";
return;
}
ackPoint p;
p.head = 99;
p.value =1;
// const char* path = "D://a.png";
QString str = "hello korol";
QByteArray buff = str.toLatin1();
memcpy(p.data,buff.data(),64);
qDebug()<<QString(p.data);
m_pSharedMemory->lock();
memset(m_pSharedMemory->data(),0,sizeof(p));
qDebug()<<"sizeof(ackPoint) = "<<sizeof (ackPoint)<<"p.size = "<<sizeof (p);
memcpy(m_pSharedMemory->data(),reinterpret_cast<char*>(&p),sizeof (p));
m_pSharedMemory->unlock();
m_pSharedMemory->detach();
此外,为了确保进程之间的同步,一般会搭配 事件来使用
创建命名的事件对象。
HANDLE CreateEventA(
[in, optional] LPSECURITY_ATTRIBUTES lpEventAttributes,
[in] BOOL bManualReset,
[in] BOOL bInitialState,
[in, optional] LPCSTR lpName
);
参数
[in, optional] lpEventAttributes
指向SECURITY_ATTRIBUTES结构的指针。如果此参数为NULL,则句柄不能被子进程继承。
结构的lpSecurityDescriptor成员指定 新事件的安全描述符。如果lpEventAttributes为NULL,则事件获取默认安全描述符。事件的默认安全描述符中的 ACL 来自创建者的主要或模拟令牌。
[in] bManualReset
如果此参数为TRUE,该函数将创建一个手动重置事件对象,该对象需要使用 ResetEvent函数将事件状态设置为非信号。如果此参数为FALSE,该函数会创建一个自动重置事件对象,并且系统会在单个等待线程被释放后自动将事件状态重置为非信号状态。
[in] bInitialState
如果此参数为TRUE,则表示事件对象的初始状态;否则,它是无信号的。
[in, optional] lpName
事件对象的名称。该名称仅限于 MAX_PATH 个字符。名称比较区分大小写。
如果lpName与现有命名事件对象的名称匹配,则此函数请求EVENT_ALL_ACCESS访问权限。在这种情况下,将 忽略bManualReset和bInitialState参数,因为它们已由创建过程设置。如果 lpEventAttributes参数不是NULL,它确定句柄是否可以被继承,但它的安全描述符成员被忽略。
如果lpName为NULL,则创建的事件对象没有名称。
然后在另一个进程打开
HANDLE OpenEventA(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] LPCSTR lpName
);
[in] dwDesiredAccess
对事件对象的访问。如果指定对象的安全描述符不允许调用进程请求访问,则函数失败。有关访问权限的列表,请参阅 同步对象安全性和访问权限。
[in] bInheritHandle
如果此值为TRUE,则由该进程创建的进程将继承句柄。否则,进程不会继承此句柄。
[in] lpName
要打开的事件的名称。名称比较区分大小写。
此函数可以打开私有命名空间中的对象。有关详细信息,请参阅对象命名空间。
终端服务: 名称可以具有“全局”或“本地”前缀,以显式打开全局或会话命名空间中的对象。名称的其余部分可以包含除反斜杠字符 (\) 之外的任何字符。有关详细信息,请参阅 内核对象命名空间。
注意 快速用户切换是使用终端服务会话实现的。第一个登录的用户使用会话 0,下一个登录的用户使用会话 1,依此类推。内核对象名称必须遵循为终端服务概述的准则,以便应用程序可以支持多个用户。
然后通过
SetEvent(m_event); ResetEvent(m_event);等函数来保障进程中的同步。