目录
一:共享内存
•共享内存允许两个不相关的进程去访问同一部分逻辑内存•如果需要在两个运行中的进程之间传输数据,共享内存将是一种【效率极高】的解决方案
常见的对内存的操作:
内存初始化memset 内存拷贝memcpy
指针动态开辟内存空间malloc 释放free
C++ 开空间new 释放delete
共享内存概述:
•共享内存是由IPC为一个进程创建的一个特殊的地址范围,它将出现在进程的地址空间中。•其他进程可以把同一段共享内存段【“连接到”】它们自己的地址空间里去。•所有进程都可以【访问】共享内存【地址】,就好像它们是有malloc分配的一样【连接到共享内存的首地址 指针】。•如果一个进程向这段共享内存写了数据,所做的改动会立刻被有权访问同一段共享内存的其他进程看到。
共享内存由操作系统管理 。
二:共享内存函数
•共享内存使用的函数与信号量的很相似,涉及到的函数如下void *shmat(int shmid, const void *shmaddr, int shmflg);//连接[void * 共享内存所有类型的数据都可以 *首地址 指针]
int shmdt(const void *shmaddr);//断开连接
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
int shmget(key_t key, int size, int shmflg);//如果不存在 就创建 如果存在 就访问
shmget函数
作用:用来创建共享内存
•key: 这个共享内存段的名字•size: 需要共享的内存量【共享内存 内存空间大小 自己设置】•shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的•如果共享内存创建成功,shmget将返回一个非负整数,即该段共享内存的标识码;如果失败,则返回“-1”
三:共享内存-代码学习
writeMain.cpp【设为启动项】
#include<iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
using namespace std;
typedef struct student
{
char stuid[20];
char stuname[20];
}STU;
int main()
{
STU stu = {"OMO211224","陈茹涵"};
void* shmaddr = NULL;
int shmid = shmget((key_t)1002, sizeof(STU), IPC_CREAT | 0777);
if (shmid == -1)
{
perror("shmget error");
}
else
{
//进程连接共享内存 首地址
shmaddr = shmat(shmid,NULL,0);
//写入共享内存
memcpy(shmaddr, &stu, sizeof(STU));
//进程和共享内存断开连接
//shmdt(shmaddr);
}
return 0;
}
共享内存段 权限777 就是新创建的
字节数40:定义的结构体 20 + 20 =40【结构体大小 正好是共享内存的大小】【共享内存里面有没有数据是看不到的,能看到的只是一个内存空间,有没有数据并不知道,只能知道空间的大小是40字节】
连接数1【代码写的没有断开连接 程序还在运行】
程序运行退出(结束进程),进程不在,共享内存依然还在,因为是由操作系统管理的。
共享内存里面是否有数据,需要读一下共享内存
readMain.cpp【设为启动项】
#include<iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
using namespace std;
typedef struct student
{
char stuid[20];
char stuname[20];
}STU;
int main()
{
STU resstu = { 0 };
void* shmaddr = NULL;
int shmid = shmget((key_t)1002, sizeof(STU), IPC_CREAT | 0777);
if (shmid == -1)
{
perror("shmget error");
}
else
{
//进程连接共享内存 首地址
shmaddr = shmat(shmid, NULL, 0);
//读取共享内存 第一个参数是目标 第二个参数是源头
memcpy(&resstu , shmaddr, sizeof(STU));
cout << "resstu.stuid = " << resstu.stuid << endl;
cout << "resstu.stuname = " << resstu.stuname << endl;
//进程和共享内存断开连接
shmdt(shmaddr);
}
return 0;
}
连接数0【代码写的断开连接 程序还在运行】
共享内存的操作其实就类似memcpy memset,若是清空共享内存 ,测试一下,是否还可以读到。
memset(shmaddr, 0, sizeof(STU));
结果:第一次是可读到的。
但是执行了memset之后,就不可以读到了。
四:学习了解一个框架结构【服务器基础框架】
首先先引入问题:
memcpy是内存拷贝的操作 【不是阻塞函数】【从上面案例中也可以看出无论共享内存中是否有数据,memcpy都正常执行 即便空数据 也会打印出空的】
那么现在对于上图 , 如果想要实现以上操作 需要先A memcpy写入数据,然后B memcpy读取,需要A进程先执行后B进程。无论共享内存有没有数据,memcpy都能执行【无非拷贝一个空数据】。如果A B两个进程同时执行,怎么能确定A进程能够先执行【需要保证A进程先执行完后B进程再开始】。
消息队列可以做通知,消息队列发的时候有顺序 读的时候有顺序 发的数据过去再读完之后还会自动清空。
消息队列 先启动读取 发现读取不到【msgrcv是一个阻塞函数】 再启动 发送消息数据。 发现原本读不到的再发送消息数据后立马读到,可以证实【msgrcv是一个阻塞函数】。这个阻塞函数会等到消息队列有消息这个事件发生了,才会继续执行。
综合上述的分析。IPC将消息队列和共享内存这两个技术结合在一起,有如下框架设计
A进程先memcpy(写)共享内存 ,对方B不能直接memcpy(读),需要等A进程先执行完才可以执行B进程。【先有数据 才能 操作数据】
利用msgcrv【阻塞函数】 :共享内存中有数据 才能执行到B进程【B进程一定晚于A进程 才是正常的业务流程】
五:练习
1.创建两个工程(工程A和工程B)
2.工程A使用共享内存发送多个学生结构体数据(比如三个学生结构体)
3.工程B使用共享内存接收多个学生结构体数据。
【一个学生结构体数据传递 思考如何连续发多条结构体数据】
#include<iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
using namespace std;
typedef struct student
{
char stuid[20];
char stuname[20];
}STU;
int main()
{
STU stu1 = {"OMO211224","陈茹涵"};
STU stu2 = { "OMO211225","林夕" };
STU stu3 = { "OMO211226","陈晓" };
void* shmaddr = NULL;
int shmid = shmget((key_t)1002, sizeof(STU) * 3, IPC_CREAT | 0777);
if (shmid == -1)
{
perror("shmget error");
}
else
{
//进程连接共享内存 首地址
shmaddr = shmat(shmid,NULL,0);
//写入共享内存
memcpy(shmaddr, &stu1, sizeof(STU));
memcpy(shmaddr + sizeof(STU), &stu2, sizeof(STU));
memcpy(shmaddr + sizeof(STU) * 2, &stu3, sizeof(STU));
//进程和共享内存断开连接
shmdt(shmaddr);
}
return 0;
}
#include<iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
using namespace std;
typedef struct student
{
char stuid[20];
char stuname[20];
}STU;
int main()
{
STU resstu1 = { 0 };
STU resstu2 = { 0 };
STU resstu3 = { 0 };
void* shmaddr = NULL;
int shmid = shmget((key_t)1002, sizeof(STU)*3, IPC_CREAT | 0777);
if (shmid == -1)
{
perror("shmget error");
}
else
{
//进程连接共享内存 首地址
shmaddr = shmat(shmid, NULL, 0);
//读取共享内存 第一个参数是目标 第二个参数是源头
memcpy(&resstu1 , shmaddr, sizeof(STU));
cout << "resstu1.stuid = " << resstu1.stuid << endl;
cout << "resstu1.stuname = " << resstu1.stuname << endl;
memcpy(&resstu2, shmaddr+ sizeof(STU), sizeof(STU));
cout << "resstu2.stuid = " << resstu2.stuid << endl;
cout << "resstu2.stuname = " << resstu2.stuname << endl;
memcpy(&resstu3, shmaddr + sizeof(STU)*2, sizeof(STU));
cout << "resstu3.stuid = " << resstu3.stuid << endl;
cout << "resstu3.stuname = " << resstu3.stuname << endl;
memset(shmaddr, 0, sizeof(STU));
//进程和共享内存断开连接
shmdt(shmaddr);
}
return 0;
}
结果: