Linux 共有メモリと Qt 共有メモリ QSharedMemory
1. Linuxプロセス通信
Linux システムではプロセス間で通信する方法が数多くあります。
- パイプ
- 名前付きパイプ (FIFO)
- メモリマッピング(mappedmemeory)
- メッセージキュー
- 共有メモリ
- セマフォ
- 信号
- ソケット
共有メモリを使用すると、2 つ以上のプロセスが同じメモリにアクセスできます。メモリ共有は比較的簡単で便利な方法であり、簡単なデータ通信やファイル共有などには非常に便利です。メモリ共有といえば、マルチスレッドとマルチプロセス間のメモリ共有について言及する必要があります。メモリはスレッド間で共有されます。厳密に言えば、同じプロセス内のスレッドは、異なるアドレス空間間でメモリを共有するのではなく、親プロセスと子プロセスの間でメモリを共有するのではなく、同じアドレス空間を使用します。親プロセスは、MAP_SHARED|MAP_ANONYMOUS オプションを使用して匿名メモリを mmap し、フォーク後、このメモリは子孫プロセス間で共有できます。
2. Linux 共有メモリの使用量
Linux のメモリ共有を実装するには、いくつかの異なる方法があります。
- 従来の SYS V に基づく共有メモリ。
- POSIX mmap ファイル マッピングに基づいて共有メモリを実装します。
- 共有メモリは memfd_create() および fd クロスプロセス共有を通じて実装されます。
ここでは SYS V の共有メモリに焦点を当てます。
まず、共有メモリの原理を理解しましょう.
2 つ以上のプロセスがページ テーブルを介して仮想メモリ アドレスを物理アドレスにマッピングすると、その物理アドレスに共同でアクセスできるメモリ (メモリ共有領域) が存在します。プロセスAとプロセスBはこの領域を操作してデータを交換できます。
1. 基本的な使用手順
- キー値を生成する
key_t ftok(const char *path ,int id);
- 共有メモリ領域を作成する
int shmget(key_t key, size_t size, int shmflg);
- マップされたメモリ共有領域
void *shmat(int shmid, const void *shmaddr, int shmflg);
- マップを解除する
int shmdt(const void *shmaddr);
- 共有メモリを削除する
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
2. 共有データの書き込み
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
int main()
{
key_t key = ftok(".",0x7777);
if(key < 0)
{
perror("ftok failed");
return -1;
}
//创建共享内存
int shmid = shmget(key,4096,IPC_CREAT|IPC_EXCL|0666);
if(shmid<0)
{
perror("shmget failed");
}
//映射,将共享内存挂接到进程地址空间
char* buf = shmat(shmid,NULL,0);
int i=0;
memset(buf,'\0',sizeof(buf));
strcat(buf,"第六天魔王,");
//分离去关联
shmdt(buf);
//等待输入退出
while(getchar() == '\0');
//删除共享内存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
3. 共有データの読み取り
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
int main()
{
key_t key = ftok(".",0x7777);
if(key < 0)
{
perror("ftok failed");
return 1;
}
//创建共享内存
int shmid = shmget(key,4096,IPC_CREAT);
if(shmid<0)
{
perror("shmget failed");
return 2;
}
//映射共享内存区
char* buf = shmat(shmid,NULL,0);
//读取共享数据
printf("read shm data: %s\n",buf);
//分离去关联
shmdt(buf);
return 0;
}
ファイルをコンパイルします: gcc -o xxx xxx.c.
最初に書き込みを実行し、データを共有領域に書き込みます.
コマンドipcs -mを使用してシステム共有メモリ関連情報を読み取ります。共有メモリからの読み取りを実行します。
主キー 0x7701a32 に 4096 バイトの共有領域が作成され、権限が 666 であることがわかります。プロセスがアクセスすると接続数が変化します。共有メモリ領域が既に存在する場合、再度作成するとエラーが報告されますので、共有領域を削除する場合は関数
int shmctl (int shmid, int cmd, struct shmid_ds *buf) を実行する必要があります。
コマンドipcrm - M 0x7701a32を使用して手動で削除することもできます。これにより、上で作成した共有メモリ領域が削除されます。
1. キー値キーは ftok で生成することも、固定値を指定することもできる
2. 共有領域を削除した場合、読み込んだデータに再度アクセスするとプログラムがクラッシュする 3.
メモリ共有は通常、メモリ共有と連携する必要があるデータ同期のためのセマフォ。
3. Qt共有メモリSharedMemoryの利用
Qt のメモリ共有操作は SharedMemory クラスにカプセル化されており、Linux のネイティブ メモリ共有よりも簡単に使用できます。
1.SharedMemoryクラスの紹介
//默认构造函数
QSharedMemory(QObject *parent = Q_NULLPTR);
//构造函数,携带key参数
QSharedMemory(const QString &key, QObject *parent = Q_NULLPTR);
//析构
~QSharedMemory();
//设置key
void setKey(const QString &key);
//返回key
QString key() const;
//设置平台特点key
void setNativeKey(const QString &key);
//返回key
QString nativeKey() const;
//创建共享内存,指定大小和读写模式
bool create(int size, AccessMode mode = ReadWrite);
//返回共享内存大小
int size() const;
//关联共享内存
bool attach(AccessMode mode = ReadWrite);
//判断是否已关联共享内存
bool isAttached() const;
//分离共享内存
bool detach();
//返回共享内存数据
void *data();
//返回const指针共享内存const数据
const void* constData() const;
//返回const指针共享内存数据
const void *data() const;
#ifndef QT_NO_SYSTEMSEMAPHORE
//锁住共享内存
bool lock();
//解锁共享内存
bool unlock();
#endif
//返回共享内存错误
SharedMemoryError error() const;
//返回共享内存错误QString类型
QString errorString() const;
ここではデータの同期に組み込みロックを使用していますが、もちろんセマフォも同期に使用できます。
そして、Qt は最後のプロセスまたはスレッドの detach() の直後に QSharedMemory を破棄します。
以下では、メモリ共有により画像をメモリ領域に書き込み、別の処理が画像を読み出して表示する処理を示します。
2. 共有データの書き込み
void ShmWriter::WriteToShareMemory()
{
//准备图片数据
QImage image;
if(!image.load(":/img/puzzle.jpg"))
{
qDebug()<<"load image failed!";
return;
}
QBuffer buff;
buff.open(QBuffer::WriteOnly);
QDataStream out(&buff);
out<<image;
//创建共享内存对象并设置key为"ImageShm"
mShmWriter = new QSharedMemory("ImageShm",this);
if(mShmWriter->isAttached())
{
//还在连接状态则进行分离
mShmWriter->detach();
}
//创建共享内存
if(!mShmWriter->create(buff.size()))
{
qDebug()<<"create share memory failed!"<<mShmWriter->errorString();
return;
}
//锁住共享区域
mShmWriter->lock();
//拷贝数据到共享内存
char *shmAddr = (char*)mShmWriter->data();
const QByteArray arry(buff.data());
const char *i_data = arry.data();
memcpy(shmAddr,i_data,buff.size());
//解锁
mShmWriter->unlock();
}
3. 共有データの読み取り
void ShmReader::readFromShareMemory()
{
//锁定
mShmReader->lock();
//关联共享内存
if(!mShmReader->attach())
{
qDebug()<<"attach failed!"<<mShmReader->errorString();
return;
}
// 从共享内存中读取数据
QBuffer buffer;
QDataStream in(&buffer);
QImage image;
buffer.setData( (const char*)mShmReader->constData(),mShmReader->size());
buffer.open(QBuffer::ReadOnly);
in>>image;
//解锁
mShmReader.unlock();
//分离共享内存
mShmReader.detach();
//显示
ui->label->setPixmap(QPixmap::fromImage(image));
}
最後に以下のように画像データを読み出します。
Qt の共有メモリ キーはインスタンス化中に取り込むことも、setKey 関数を使用してキーを設定することもできます。読み取りキーと書き込みキーは一貫している必要があります。
コードサンプルのリンク: https://download.csdn.net/download/haohaohaihuai/12566419
著者: Feima Programmer
技術交流へようこそ: QQ: 255895056
転載の際は出典を明記し、不適切な点があれば修正してください