共享内存介绍

共享内存是IPC中效率最高的一个,它是原理是linux内核在内存中开辟一个空间,给进程进行读写。
每个进程都会通过API函数,把这块linux内核中的内存映射到自己的进程空间里面来,是映射的,虚拟的,不是实际在进程内存中。
通过这种方法来达到进程间共享数据目的

• 共享内存使用的函数与信号量的很相似,涉及到的函数如下
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmget(key_t key, int size, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmget:创建共享内存

int shmget(key_t key,size_t size, int shmflg);
• key: 这个共享内存段的名字,我们通常自定义数字并用key_t类型强转
• size: 需要共享的内存量
• shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
• 如果共享内存创建成功,shmget将返回一个非负整数,即该段共享内存的标识码;如果失败,则返回“-1”
例子:

int shm_id;
	shm_id = shmget((key_t)6677, SHM_ZIZE_MAX, IPC_CREAT | 0666);//666可读可写

shmat:将创建好的共享内存连接到某个进程,并指定内存空间

void *shmat(int shmid, const void *shmaddr, int shmflg);

• shm_id: shmget返回的共享内存标识
• shm_addr:把共享内存连接到当前进程去的时候准备放置它的那个地址
• shmflg是一组按位OR(或)在一起的标志。它的两个可能取值是SHM_RND和SHM_RDONLY
• 调用成功,返回一个指针,指针指向共享内存的第一个字节,如果失败,则返回“-1”
void *shm_addr = NULL;
//shmat的第二个参数shm_addr:把共享内存连接到当前进程去的时候准备放置它的那个地址,为NULL为让系统自动选择
//第三个参数是一组按位OR(或)在一起的标志,SHM_RDONLY表示只读
shm_addr = shmat(shm_id, NULL, SHM_RDONLY);
• shmaddr为0(NULL),核心自动选择一个地址
• shmaddr不为0且shmflg无SHM_RND标记,则以shmaddr为连接地址。
• shmaddr不为0且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
• shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

注意点:
• 在fork() 后,子进程继承已连接的共享内存
• 在exec后,已连接的共享内存会自动脱离(detach)
• 在结束进程后,已连接的共享内存会自动脱离(detach)

shmdt:脱钩函数,把共享内存与当前进程脱离开

int shmdt(const void *shmaddr);
• shm_addr: 由shmat返回的地址指针
• 操作成功,返回“0”,失败则返回“-1”
• 脱离共享内存并不等于删除它,只是当前进程不能再继续访问它而已
注意:共享内存实际是独立于内存存在的,意味着进程结束后,共享内存以及里面保存的数据实际还存在。

代码实例

共享内存写入端:


#include <iostream>
#include<unistd.h>//unix stand lib
#include<sys/types.h>
#include<sys/fcntl.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<dirent.h>//file dir
#include <sys/wait.h>//wait func
#include <stdlib.h>//ststem
#include <signal.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/shm.h>
using namespace std;
typedef struct
{
	int age;
	char name[10];
}STU;

#define SHM_ZIZE_MAX 4096


int main(int argc, char *argv[])
{
	
	int shm_id;
	shm_id = shmget((key_t)6677, SHM_ZIZE_MAX, IPC_CREAT | 0666);//666可读可写
	if (shm_id == -1)
	{
		perror("shm create error");
		return -1;
	}

	STU stu_info = { 19, "rabbit" };

	void *shm_addr = NULL;
	//shmat的第二个参数shm_addr:把共享内存连接到当前进程去的时候准备放置它的那个地址,为NULL为让系统自动选择
	//第三个参数是一组按位OR(或)在一起的标志,0表示可读可写
	shm_addr = shmat(shm_id, NULL, 0);
	//要对内存清空一下
	memset(shm_addr, 0, SHM_ZIZE_MAX);
	//写进内存:memcpy因为映射就看做是自己的内存

	memcpy(shm_addr, &stu_info, sizeof(STU));//地址,内容,大小
	cout << "memcpy(shm_addr, &stu_info, sizeof(STU))" << endl;
	while(1){sleep(1);}
	shmdt(shm_addr);//脱钩函数
	
	return 0;

共享内存读出端:

#include <iostream>
#include<unistd.h>//unix stand lib
#include<sys/types.h>
#include<sys/fcntl.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<dirent.h>//file dir
#include <sys/wait.h>//wait func
#include <stdlib.h>//ststem
#include <signal.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/shm.h>
typedef struct
{
	int age;
	char name[10];
}STU;

#define SHM_ZIZE_MAX 4096
int main(int argc, char *argv[])
{
	int shm_id;
	shm_id = shmget((key_t)6677, SHM_ZIZE_MAX, IPC_CREAT | 0666);//666可读可写
	if (shm_id == -1)
	{
		perror("shm create error");
		return -1;
	}
	
	STU stu_info;
	STU *recv_stu_info;
	bzero(&stu_info, sizeof(STU));
	void *shm_addr = NULL;
	//shmat的第二个参数shm_addr:把共享内存连接到当前进程去的时候准备放置它的那个地址,为NULL为让系统自动选择
	//第三个参数是一组按位OR(或)在一起的标志,SHM_RDONLY表示只读
	shm_addr = shmat(shm_id, NULL, SHM_RDONLY);

	//法一:从内存中拿出来:memcpy因为映射就看做是自己的内存
	//memcpy(&stu_info, shm_addr,sizeof(STU));//地址,内容,大小
	//cout << "stu_info.name" << stu_info.name << endl;
	
	//法二:也可以直接把共享内存指针强制转换成STU*,这时要shmat的参数3改为0读写
	//这里也说明如果有进程把数据写入共享内存,如果没有其他进程去修改或清除
	//那么数据永远都在里面,消息队列和共享内存里面的数据生命周期和进程不同(进程消失了数据可以还在)
	recv_stu_info = (STU*)shm_addr;
	cout << "stu_info.name" << recv_stu_info->name << endl;

	
	
	
	shmdt(shm_addr);//脱钩函数
	
	return 0;
}
Logo

更多推荐