共有メモリの定義
ソフトウェアでは、共有メモリという用語は、複数のプロセスがアクセスできるメモリを指します。プロセスは、プログラムの単一の実行中のインスタンスです。この場合、共有メモリはプロセス間通信に使用されます
Qtの共有メモリ
2つのプロセス間で共有メモリを使用する
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に読み取ります。
注意
- 共有メモリを作成するときは、キー値を指定する必要があり、キー値は一意です。上記の例については、コンストラクターを参照してください
- 通信する前に共有メモリに接続する必要があります
- 共有メモリへのデータの書き込みと読み取りを行う場合は、データをロックし、操作の完了後にロックを解除する必要があります。これが上記の例のロックとロック解除です。
親プロセスと子プロセス間で共有メモリを使用する
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 reateFile関数を呼び出すことで返すことができます。ここでは、実際のファイルは必要ないため、ファイルを作成するためにCreateFileを呼び出す必要はありません
。hFileパラメーターにはINVALID_HANDLE_VALUEを入力できます。lpFileMappingAttributes:パラメーターはファイルマッピングカーネルのSECURITY_ATTRIBUTES構造へのポインターです。オブジェクトであり、通常渡される値はNULLです
。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);
インスタンス
注意
書き込みと書き込みの競合は十分に解決されていません。メモリマップファイルは共有リソースです。複数のプロセスの読み取りと書き込みで同期の問題が発生する必要があります。この例では問題がない可能性がありますが、より高い問題があります。実際のプロジェクトでは、周波数の読み取りと書き込みを同時に行う場合は、読み取りと書き込みの同期を実行する必要があります。