基于VS2019 C++的跨平台(Linux)开发(1.5)——共享内存

一、引言

        之前的管道学习中,我们利用命名管道生成了两个.fifo的文件,如果用户不懂技术,以为这个文件没用就可能把它们删除了。此外,因为管道(不管是命名管道还是匿名管道)能够容纳的最大数据是有限的(65535字节),你不断的往管道里面写数据, 如果超过这个容量就可能导致管道损坏。(就像水管承载的水量是有限的,超过一定的水压就可能爆裂),所以总结起来就是管道容易被误删,管道容量有上限。那么接下来就涉及到了共享内存。

二、共享内存概述

        共享内存是由IPC为一个进程创建的一个特殊的地址范围,它将出现在进程的地址空间中。 其他进程可以把同一段共享内存段“连接到”它们自己的地址空间里去。如下图所示

①上图中的共享内存它有可能是A或者B其中一个进程创建的。但是它不属于任何进程(不属于A也不属于B),属于操作系统本身。

②A、B进程都可以访问共享内存地址,就好像它们是有malloc分配的一样。

③如果A进程向这段共享内存写了数据,所做的改动会立刻被有权访问同一段共享内存的其他进程看到,如这里的B进程。

        可以想象成现实当中的共享单车、共享充电宝。首先,这两者都不属于用户;其次,大家都有权利使用;最后,如果前一个用户把它用坏了,后一个用户如果碰巧也用上这个,那么它的状态就是上次弄坏的状态。 再具体点,就是如果前一个用户用的共享充电宝用了百分之三十的电量,那么后一个用户如果立马也使用的话,就只有百分之七十的电量可供使用,当然是假设这个充电宝还没有充上电的时候

        所以简单来说,共享有以下三个特点:这个东西不属于你;谁都能用;后一个人使用一定能看到前一个人的操作结果

        再回到共享内存这个概念,即这段内存不属于任何进程;所有进程都能使用这段内存;有一个进程对内存进行了改变,那么其他进程都能看见操作结果。

通过ipcs命令可以查看共享内存、消息队列、信号量。信号和管道都属于某一进程空间,但这三种都属于操作系统本身,进程存不存在与这三种没关系。即这三种东西是对于进程来说的独立的。进程没开,共享内存仍可以存在(如下图)。

ipcrm -m   shmid   删除指定id的共享内存

ipcm -a 把所有创建出来的共享内存,信号量等全部删除(除root外)

 三、共享内存函数

1、shmget函数——创建

作用:用来创建共享内存

原型:int  shmget(key_t key,size_t size, int shmflg);

参数说明:

  • key: 共享内存段的名字
  • size: 共享内存大小
  • shmflg:由九个权限标志构成(读、写、执行——0777),用法和创建文件时使用的mode模式标志是一样的,但是不需要用umask(0)

返回值:如果共享内存创建成功,返回一个非负整数,即该段共享内存的标识码(shm_id);如果失败,则返回“-1” 

注意:该函数有两层含义:如果不存在则创建;如果存在(根据key判断)则不做操作

2、shmat函数——连接

作用:共享内存段刚被创建的时候,任何进程还都不能访问它,为了建立对这个共享内存段的访问渠道,必须由我们来把它连接到自己的进程的地址空间(就像共享单车你要扫码支付完才能使用)

原型:void* shmat(int shm_id,     const void *shm_addr,     int shmflg);

参数说明:

  • shm_id: shmget返回的共享内存标识
  • shm_addr:把共享内存连接到当前进程去的时候准备放置它的那个地址
  • shmflg是一组按位OR(或)在一起的标志。它的两个可能取值是SHM_RND和SHM_RDONLY

返回值:调用成功,返回一个指针,指针指向共享内存的第一个字节;如果失败,则返回“-1”(void* 则表示可以存任何类型,因为刚连接自己也不知道是什么类型)

3、shmdt函数——断开

作用:把共享内存与当前进程脱离开(就像共享单车的归还动作)

原型:int  shmdt(const void *shm_addr);

参数说明:shm_addr: 由shmat返回的地址指针

返回值:操作成功,返回“0”,失败则返回“-1”

脱离共享内存并不等于删除它,只是当前进程不能再继续访问它而已

4、shmctl函数——删除

作用:共享内存的控制函数(一般用不到,因为数据可以共享就可以覆盖,没必要删除)

原型:int  shmctl(int shm_id,int command,struct shmid_ds *buf);

参数说明:

  • shm_id: 由shmget返回的共享内存标识码
  • command:将要采取的动作(三个取值:IPC_STAT、IPC_SET、IPC_RMID)
  • buf:指向一个保存着共享内存的模式状态和访问权限的数据结构

返回值:操作成功,返回“0”,失败则返回“-1”

 内存操作:

  • memset  通常为新申请的内存进行初始化工作(清空——bzero函数)
  • memcpy 内存拷贝,只是将数据拷贝出来,原本数据不会清空

四、示例

        创建两个应用程序,一个读一个写

1、代码

#include <iostream>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <string.h>
using namespace std;

//写端

typedef struct student
{
	char stuid[10];
	char name[20];

}STU;
int main()
{
	void* shmaddr = NULL;
	int shmid = 0;

	STU stu1 = { "1001","zqw"};
	//创建
	shmid = shmget((key_t)1001,2048,IPC_CREAT|0777);

	if (shmid == -1)
	{
		perror("shmget  error");
	}
	else
	{
		//连接共享内存 获取到共享内存的首地址shmaddr
		shmaddr = shmat(shmid,NULL,0);

		//写入数据  = 内存拷贝
		memcpy(shmaddr, &stu1,sizeof(STU));

		/*cout <<"res_stu  id" << res_stu.stuid<< endl;
		cout << "res_stu  name" << res_stu.name << endl;*/

		//断开共享内存的连接
		shmdt(shmaddr);
	}

	return 0;
}

//读端

typedef struct student
{
	char stuid[10];
	char name[20];

}STU;
int main()
{
	void* shmaddr = NULL;
	int shmid = 0;

	STU res_stu = { 0 };
	//创建
	shmid = shmget((key_t)1001, 2048, IPC_CREAT | 0777);

	if (shmid == -1)
	{
		perror("shmget  error");
	}
	else
	{
		//连接共享内存 获取到共享内存的首地址
		shmaddr = shmat(shmid, NULL, 0);

		//读取数据  = 内存拷贝
		memcpy(&res_stu, shmaddr, sizeof(STU));

		cout << "读端  id = " << res_stu.stuid << endl;
		cout << "读端  name = " << res_stu.name << endl;

		//断开共享内存的连接

		shmdt(shmaddr);
	}

	return 0;
}

2、运行结果 

  • 因为在读端没有对数据进行更改、清空等操作,只是单纯的拷贝,所以共享内存的数据依然还在,读多次的结果还是一样
  • 进程结束,共享内存依然还在,说明了共享内存属于操作系统直接管理而不属于进程
  • 共享内存足够用就好,创建完会一直占用内存,直到你手动删除

 转载请注明出处

基于VS2019 C++的跨平台(Linux)开发(1.5)——共享内存

猜你喜欢

转载自blog.csdn.net/hml111666/article/details/123697406
今日推荐