Linux内核编程 共享内存使用案例

目录

一:共享内存

二:shmat函数

三:共享内存编程应用

四:服务器开发基础架构

五:共享内存实例


一:共享内存

共享内存允许两个不相关的进程去访问同一部分逻辑内存

如果需要在两个运行中的进程之间传输数据,共享内存将是一种【效率极高】的解决方案

常见的对内存的操作有:

内存初始化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运行,读共享内存数据如下 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenruhan_QAQ_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值