共享内存

共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据,只需要通过一些内存访问函数来传递。

共享内存示意图

共享内存是整块内存当中的一块特殊区域,这块区域可以映射到不同的地址空间。A、B进程就可以通过共享内存区传递数据,并且不涉及系统调用。

管道、消息队列与共享内存传递数据对比

这个例子是服务器向客户端传送文件,涉及了4次系统调用

用共享内存传递数据

共享内存是内存中的一块特殊区域,这块区域可以映射到服务器的地址空间中,也可以映射到客户的地址空间中。只涉及了两次系统调用,一次将数据拷贝到共享内存区,另一次是将共享内存区中的数据写到输出文件。从共享内存区读取数据这个过程是通过内存访问函数来实现的,不需要涉及到内存从进程的内存空间拷贝到用户空间中。所以效率大大提高。

mmap函数

功能:

将文件或者设备空间映射到共享内存区。

原型:

void *mmap(void *addr,size_t len,int prot,int flags,int fd,off_t offset);

参数:

    addr:要映射的起始地址,通常指定为NULL,让内核自动选择

    len:映射到进程地址空间的字节数

    prot:映射区保护方式

flags:标志

MAP_SHARED表示是共享的,当我们将文件I/O映射到共享内存区后,对文件的操作就好像是对内存的操作,也就是对内存操作就可以访问文件了。这时候对内存的操作,这个操作它所做的变动能够被其它进程看到。

MAP_PRIVATE表示变动私有,对映射到的内存区写操作不会影响到其它进程,并且这些变动不会写会到文件当中。

MAP_FIXED将返回的地址自动对齐为页面的整数倍

fd:

要映射到内存中的文件描述符

offset:

从文件头开始的偏移量

返回值:

成功返回映射到的内存区的起始地址,失败返回-1。

内存映射文件示意图

实际上,mmap这个函数在创建内存映射区的时候是以页面为单位来分配的,如果len小于一个页面的大小,mmap创建的共享内存区会大于len,至少是一个页面。

munmap函数

功能:

取消mmap函数建立的映射

原型:

int munmap(void *addr,size_t len);

参数:

    addr:映射的内存起始地址

    len:映射到进程地址空间的字节数

返回值:

成功返回0,失败返回-1。

 

msync函数

功能:

对映射的共享内存执行同步操作

原型:

int msync(void *addr,size_t len,int flags);

参数:

    addr:内存起始地址

    len:长度

    flags:选项

如果指定了SS_ASYNC选项,仅仅只是告诉内核要将高速缓冲区中的数据写回到磁盘,然后立刻返回。这是可能内核还未执行就返回了,不会发生阻塞。

如果指定了MS_SYNC选项,告诉内核要将高速缓冲区中的数据写回到磁盘,而且等待内核执行完毕才返回。

如果指定MS_INVALIDATE会使高速缓冲区的数据失效,下次进行读操作时只能对文件进行操作。

返回值:

成功返回0,失败返回-1。

map注意点

1.映射不能改变文件的大小

2.可用于进程间通信的有效地址空间不完全受限于被映射文件的大小

3.文件一旦被映射后,所有对映射区域的访问实际上是对内存区域的访问。映射区域内容写回文件时,所写内容不能超过文件的的大小。

应用代码

mmap_write

#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        }while(0)

typedef struct stu
{
	char name[4];
	int age;
}STU;

//接收一个参数,传递文件名称进来
int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		fprintf(stderr,"Usage: %s <file>\n",argv[0]);
		exit(EXIT_FAILURE);
	}
	
	int fd;
	//以创建的、读写的、清空的方式打开一个文件
	fd=open(argv[1],O_CREAT | O_RDWR | O_TRUNC,0666);
	if(fd==-1)
		ERR_EXIT("open");
	lseek(fd,sizeof(STU)*5-1,SEEK_SET);
	//产生一个40个字节的文件
	write(fd,"",1);
	STU  *p;
	//对文件进行映射
	//让内核选择一个合适的地址进行映射,第一个参数为NULL
	p=(STU*)mmap(NULL,sizeof(STU)*5,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	if(p==NULL)
		ERR_EXIT("mmap");
	char ch='a';
	int i;
	for(i=0;i<5;i++)
	{
		//对内存的操作就是对文件的操作
		memcpy((p+i)->name,&ch,1);
		(p+i)->age=20+i;
		ch++;
	}
	printf("initialize over\n");
	//取消mmap函数建立的映射
	
	munmap(p,sizeof(STU)*5);
	printf("exit...\n");
	return 0;
}

以前读写的话是需要read、write这些函数调用,通过共享内存就不再需要了。

mmap_read

#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        }while(0)

typedef struct stu
{
	char name[4];
	int age;
}STU;

//接收一个参数,传递文件名称进来
int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		fprintf(stderr,"Usage: %s <file>\n",argv[0]);
		exit(EXIT_FAILURE);
	}
	
	int fd;
	//打开一个文件
	fd=open(argv[1],O_RDWR);
	if(fd==-1)
		ERR_EXIT("open");
	STU  *p;
	//对文件进行映射
	//让内核选择一个合适的地址进行映射,第一个参数为NULL
	p=(STU*)mmap(NULL,sizeof(STU)*5,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	if(p==NULL)
		ERR_EXIT("mmap");
	int i;
	for(i=0;i<5;i++)
	{
		//对内存的读操作就是对文件的读操作
		printf("name=%s age=%d\n",(p+i)->name,(p+i)->age);		
	}
	printf("initialize over\n");
	//取消mmap函数建立的映射
	
	munmap(p,sizeof(STU)*5);
	printf("exit...\n");
	return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值