Linux系统下进程通信的方式有很多:
共享内存允许两个或更多进程访问同一块内存。内存共享是使用的比较简单方便的一种了,简单的一些数据通信和文件共享,用内存共享非常方便。说起内存共享就得提一提多线程、多进程间的内存共享。线程之间的内存都是共享的。严格的说,同一进程的线程使用的是同一个地址空间,而不是在不同地址空间之间进行内存共享;父子进程间的内存共享。父进程以MAP_SHARED|MAP_ANONYMOUS选项mmap一块匿名内存,fork之后,其子孙进程之间就能共享这块内存。
Linux内存共享有几种不同的实现方式
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);
#include
#include
#include
#include
#include
#include
#include
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;
}
#include
#include
#include
#include
#include
#include
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.键值key可以ftok生成,也可以指定固定值
2.如果共享区域被删除了,再去访问读数据会造成程序崩溃
3.内存共享一般要配合信号量进行数据同步
Qt的内存共享操作封装在SharedMemory类里,使用相比Linux原生内存共享也更简单一点。
//默认构造函数
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;
这里使用内置的lock进行数据同步,当然也可以使用信号量进行同步。
并且Qt会在最后一个进程或线程detach()之后直接销毁QSharedMemory。
下面展示一个通过内存共享一个进行把图片写道内存区域,另外一个进程读图片并显示出来。
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();
}
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的共享内存key可以在实例化的时候把key带进去,也可以使用setKey函数进行key的设置。读写端key必须保持一致。
代码示例链接:https://download.csdn.net/download/haohaohaihuai/12566419
作者:费码程序猿
欢迎技术交流:QQ:255895056
转载请注明出处,如有不当欢迎指正