目录
一:共享内存
共享内存允许两个不相关的进程去访问同一部分逻辑内存
如果需要在两个运行中的进程之间传输数据,共享内存将是一种【效率极高】的解决方案
常见的对内存的操作有:
内存初始化memset 内存拷贝memcpy
C: 指针动态开辟内存空间malloc 释放free
C++: 开空间new 释放delete
共享内存概述:
共享内存是由IPC为一个进程创建的一个特殊的地址范围,它将出现在进程的地址空间中
其他进程可以把同一段共享内存段【“连接到”】它们自己的地址空间里去
所有进程都可以【访问】共享内存【地址】,就好像它们是有malloc分配的一样【连接到共享内存的首地址 指针】
如果一个进程向这段共享内存写了数据,所做的改动会立刻被有权访问同一段共享内存的其他进程看到
共享内存由操作系统管理
消息队列 共享内存 信号量 为内存三剑客,由操作系统管理
二:shmat函数
共享内存使用的函数与信号量的很相似,涉及到的函数如下
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函数
作用:用来创建共享内存
函数原型
int shmget(key_t key.size_t size,int shmflg);
key:这个共享内存段的名字
size:需要共享的内存量【共享内存 内存空间大小 自己设置】
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
如果共享内存创建成功,shmget将返回一个非负整数,即该段共享内存的标识码;
如果失败,则返回“-1”
三:共享内存编程应用
查看shmget函数使用说明
查看shmat函数使用说明
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;
}
ipcs查看内存三剑客
可以看出
共享内存段 权限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是一个阻塞函数】
这个阻塞函数会等到消息队列有消息这个事件发生了,才会继续执行
综合上述的分析
将消息队列和共享内存这两个技术结合在一起,可有如下框架设计
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;
}
在bin中找到可执行文件,./Tab 运行程序
ipcs查看内存
在读端进程(工程),同样bin中找到可执行文件,./Tab运行,读共享内存数据如下