进程通信之共享内存

共享内存定义

在软件中,术语共享内存指可被多个进程存取的内存,一个进程是一段程序的单个运行实例。在这种情况下,共享内存被用作进程间的通讯

Qt中的共享内存

两个进程之间使用共享内存

QtTest.h

#pragma once

#include <QtWidgets/QWidget>
#include "ui_QtTest.h"
#include <QSharedMemory>//共享内存头文件
class QtTest : public QWidget
{
    Q_OBJECT

public:
    QtTest(QWidget *parent = Q_NULLPTR);
private:
	void setUpConnecitons();
private slots :
		void onButtonWrite(bool value);
		void onButtonRead(bool value);
private:
    Ui::QtTestClass ui;
	QSharedMemory _sharedMemory;
};

QtTest.cpp

#include "QtTest.h"
#include <QBuffer>
#include <QDebug>
QtTest::QtTest(QWidget *parent)
	: QWidget(parent)
	, _sharedMemory("QSharedMemoryTest")//指定共享内存的key值为 QSharedMemoryTest
	//必须给定一个key值 且唯一
{
	ui.setupUi(this);
	setUpConnecitons();
	qDebug() << "QtTest QtTest";
}

void QtTest::setUpConnecitons()
{
	connect(ui.pushButtonWrite, &QPushButton::clicked, this,&QtTest::onButtonWrite);
	connect(ui.pushButtonRead, &QPushButton::clicked, this, &QtTest::onButtonRead);
}

void QtTest::onButtonWrite(bool value)
{
	if (_sharedMemory.isAttached())//判断是否已经附加共享内存
	{
		_sharedMemory.detach();//解除对共享内存的附加
	}
	QBuffer buffer;
	buffer.open(QBuffer::ReadWrite);
	auto text = ui.lineEditWrite->text();
	QDataStream out(&buffer);
	out << text;//
	int size = buffer.size();
	qDebug() << "Write:---" << text<<"---Size--:"<<size;
	if (!_sharedMemory.create(size)) {//创建一块共享内存
		qDebug() << "Error:---"<<_sharedMemory.errorString();
		return;
	}
	_sharedMemory.lock();
	char *to = (char*)_sharedMemory.data();
	const char *from = buffer.data().data();
	memcpy(to, from, qMin(_sharedMemory.size(), size));

	_sharedMemory.unlock();
}

void QtTest::onButtonRead(bool value)
{
	if (!_sharedMemory.attach()) //附加到共享内存
	{
		qDebug() << "Attach Falied";
		return;
	}
	QBuffer buffer;
	QDataStream in(&buffer);
	_sharedMemory.lock();
	buffer.setData((char*)_sharedMemory.constData(), _sharedMemory.size());//读取共享内存中的数据  读取完之后并不会清空共享内存中的数据
	buffer.open(QBuffer::ReadOnly);
	QString text;
	in >> text;
	qDebug() << "Read:---" << text;
	_sharedMemory.unlock();
	ui.lineEditRead->setText(text);
	_sharedMemory.detach();
}

界面:
在这里插入图片描述
此程序的主要功能是:点击写入按钮则将上面的QLineEdit中的内容写入到共享内存。点击读取按钮则将共享内容中的数据读取到下面的QLineEdit中。
注意

  • 在创建共享内存的时候必须指定一个key值,且key值唯一。上例见构造函数
  • 必须附加到共享内存之后才能通信
  • 在向共享内存写入以及读取数据的时候需要加锁,操作完毕之后进行解锁。即上例中的lock以及unlock。
父子进程之间使用共享内存

windows下的共享内存

此小节来源:https://www.cnblogs.com/xiekeli/p/4018579.html
windows下的共享内存是通过FileMapping(内存映射文件)来实现的。是由一个文件到一块内存的映射。(对于大文件的操作一般是以内存映射文件的方式来加以处理的)。先将文件映射到内存,(如同将整个文件从磁盘加载到内存)。如此之后对此文件进行操作的时候就不必要再执行I/O操作。提高了执行速度。

共享内存实现步骤
  • 调用 CreateFileMapping 创建一个内存文件映射对象
HANDLE CreateFileMapping(
  HANDLE hFile,              // handle to file to map
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
                             // optional security attributes
  DWORD flProtect,           // protection for mapping object
  DWORD dwMaximumSizeHigh,   // high-order 32 bits of object size
  DWORD dwMaximumSizeLow,    // low-order 32 bits of object size
  LPCTSTR lpName             // name of file-mapping object
);

通过这个API函数 将创建一个内存映射文件的内核对象,用于映射文件到内存。与虚拟内存一样,内存映射文件可以用来保留一个地址空间的区域,并将物理存储器提交
给该区域。它们之间的差别是,物理存储器来自一个已经位于磁盘上的文件,而不是系统的页文件
hFile:用于标识你想要映射到进程地址空间中的文件句柄。该句柄可以通过调用C r e a t e F i l e函数返回。这里,我们并不需要一个实际的文件,所以,就不需要调用 CreateFile 创建一个文件, hFile 这个参数可以填写 INVALID_HANDLE_VALUE;
lpFileMappingAttributes:参数是指向文件映射内核对象的 SECURITY_ATTRIBUTES结构的指针,通常传递的值是 N U L L;
flProtect:对内存映射文件的安全设置(PAGE_READONLY 以只读方式打开映射;PAGE_READWRITE 以可读、可写方式打开映射;PAGE_WRITECOPY 为写操作留下备份)
dwMaximumSizeHigh:文件映射的最大长度的高32位。
dwMaximumSizeLow:文件映射的最大长度的低32位。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度。
lpName:指定文件映射对象的名字,别的进程就可以用这个名字去调用 OpenFileMapping 来打开这个 FileMapping 对象。
如果创建成功,返回创建的内存映射文件的句柄,如果已经存在,则也返回其句柄,但是调用 GetLastError()返回的错误码是:183(ERROR_ALREADY_EXISTS),如果创建失败,则返回NULL;

  • 调用 MapViewOfFile 映射到当前进程的虚拟地址上
    如果调用CreateFileMapping成功,则调用MapViewOfFile函数,将内存映射文件映射到进程的虚拟地址中
LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,  // file-mapping object to map into 
                              // address space
  DWORD dwDesiredAccess,      // access mode
  DWORD dwFileOffsetHigh,     // high-order 32 bits of file offset
  DWORD dwFileOffsetLow,      // low-order 32 bits of file offset
  DWORD dwNumberOfBytesToMap  // number of bytes to map
);
hFileMappingObject:CreateFileMapping()返回的文件映像对象句柄。
dwDesiredAccess: 映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。
dwFileOffsetHigh: 表示文件映射起始偏移的高32.
dwFileOffsetLow: 表示文件映射起始偏移的低32.
dwNumberOfBytesToMap :文件中要映射的字节数。为0表示映射整个文件映射对象。
  • 在接收进程中打开对应的内存映射对象
    在数据接收进程中,首先调用OpenFileMapping()函数打开一个命名的文件映射内核对象,得到相应的文件映射内核对象句柄hFileMapping;如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,将文件映射内核对象hFileMapping映射到当前应用程序的进程地址,进行读取操作。(当然,这里如果用CreateFileMapping也是可以获取对应的句柄)
HANDLE OpenFileMapping(
  DWORD dwDesiredAccess,  // access mode
  BOOL bInheritHandle,    // inherit flag
  LPCTSTR lpName          // pointer to name of file-mapping object
);
dwDesiredAccess:同MapViewOfFile函数的dwDesiredAccess参数
bInheritHandle :如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE。
lpName :指定要打开的文件映射对象名称。
  • 进行内存映射文件的读写
    一旦MapViewOfFile调用成功,就可以像读写本进程地址空间的内存区一样,进行内存的读写操作了。
//读操作:
if ( m_pViewOfFile  )
{
        // read text from memory-mapped file
        TCHAR s[dwMemoryFileSize];
        
        lstrcpy(s, (LPCTSTR) m_pViewOfFile);
}
//写操作:
if ( m_pViewOfFile )
 {
        TCHAR s[dwMemoryFileSize];
        m_edit_box.GetWindowText(s, dwMemoryFileSize);
            
        lstrcpy( (LPTSTR) m_pViewOfFile, s);
            
        // Notify all running instances that text was changed
        ::PostMessage(HWND_BROADCAST, 
            wm_Message,     
            (WPARAM) m_hWnd,
            0);    
}
  • 清理内核对象
    在用完后,要取消本进程地址空间的映射,并释放内存映射对象。
    //取消本进程地址空间的映射;   
    UnmapViewOfFile(pLocalMem);  
    pLocalMem=NULL;   
    //关闭文件映射内核文件  
    CloseHandle(hFileMapping);
实例
注意

和写之间的冲突没有很好的解决,内存映射文件是一个共享的资源,多个进程读写必然存在同步的问题,也许在这个例子中不会出现什么问题,但是实际项目中存在较高频率的并发读写的情况下,必须进行读写的同步

读写同步
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一路初心向前

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值