【Linux学习】- 共享内存

目录

一:共享内存

二:共享内存函数

三:共享内存-代码学习

四:学习了解一个框架结构【服务器基础框架】


一:共享内存

•共享内存允许两个不相关的进程去访问同一部分逻辑内存
•如果需要在两个运行中的进程之间传输数据,共享内存将是一种【效率极高】的解决方案

常见的对内存的操作:

内存初始化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;
}

结果:

 

猜你喜欢

转载自blog.csdn.net/m0_56051805/article/details/125795525