Linux下进程通信---共享内存之:mmap

进程通信:进程与进程间的数据交换,称为进程通信。进程通讯的方式有:共享内存、信号量、管道、消息队列、socket等等。

共享内存:内核管理一片物理内存,允许不同的进程同时映射,多个进程可以映射同一块内存,被多个进程同时映射的物理内存,即共享内存。映射物理内存叫挂接,用完以后解除映射叫脱接。

优点:共享内存是进程通讯最快最有效的。

缺点:不存在同步机制,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,需与其他机制合作来实现同步。

共享内存有两种方式:mmap、shm。

mmap:mmap方式是将文件与进程地址空间进行映射,对实际物理内存影响小。mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read,write等操作。即在磁盘上建立一个文件,每个进程地址空间中开辟出一块空间进行映射。

shm:shm方式是将每个进程的共享内存与实际物理存储器进行映射,对实际物理内存影响大。即每个进程最终会映射到同一块物理内存。shm保存在物理内存,这样读写的速度要比磁盘要快,但是存储量不是特别大。

函数:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize); 

    参数start:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。

    参数length:代表将文件中多大的部分映射到内存。

    参数prot:映射区域的保护方式。可以为以下几种方式的组合:
    PROT_EXEC 映射区域可被执行
    PROT_READ 映射区域可被读取
    PROT_WRITE 映射区域可被写入
    PROT_NONE 映射区域不能存取

    参数flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。
    MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
    MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享, 原来的文件会改变。
    MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任      何修改都不会写回原来的文件内容。当共享的对象的虚拟存储区域为私有对象时, 修改只会被本进程中改变。
    MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
    MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
    MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。

   参数fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系      统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。

参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。

返回值:若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。

mmap父子进程的实现例子如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/mman.h>

struct studen{
	int age;
	char name[32];
};

const char *names[]={"zhangsan","lisi","wangwu"};

int main(int argc, char *argv[])
{
	int i = 0;
	struct studen *studen_t;
	studen_t = (struct studen*)mmap(NULL,sizeof(struct studen)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);  

	if(fork() == 0) //创建子进程,等于0是子进程,子进程读
	{
		sleep(1);
		for(i=0;i<3;i++)
		{
			printf("number:%d name: %s age: %d\n",i+1,studen_t[i].name,(*(studen_t+i)).age);
		}
		(*studen_t).age = 2019; //改变zhangsan的年纪为2019
		munmap(studen_t,sizeof(struct studen)*10); //解除映射
		return 0;
	} else { 
		//父进程写
		for(i=0;i<3;i++)
		{
			memcpy((*(studen_t+i)).name,names[i],strlen(names[i]));
			(*(studen_t+i)).age = 18+i;
		}
		sleep(3);
		printf("name: %s age: %d\n",(*studen_t).name,(*studen_t).age);
		munmap(studen_t,sizeof(struct studen)*10); //解除映射
		printf("umap success\n");
		return 0;
	}	
}

实现结果如下:

mmap以文件进行通信的例子如下:

//----------------------------------write---------------------------------
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>

struct studen{
	int age;
	char name[32];
};

const char *names[]={"zhangsan","lisi","wangwu"};

int main(int argc, char *argv[])
{
	int i = 0;
	int fd = 0;
	struct studen *studen_t;
	fd=open("./mmap.txt",O_CREAT|O_RDWR|O_TRUNC,00777);
	lseek(fd,sizeof(struct studen)*3-1,SEEK_SET);
	write(fd,"",1);

	studen_t = (struct studen*)mmap(NULL,sizeof(struct studen)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);  

	close(fd);
	for(i=0;i<3;i++)
	{
		memcpy((*(studen_t+i)).name,names[i],strlen(names[i]));
		(*(studen_t+i)).age = 18+i;
	}
	sleep(3);
	//printf("name: %s age: %d\n",(*studen_t).name,(*studen_t).age);
	munmap(studen_t,sizeof(struct studen)*10); //解除映射
	printf("umap success\n");
	return 0;	
}


//----------------------------------read---------------------------------
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>

struct studen{
	int age;
	char name[32];
};


int main(int argc, char *argv[])
{
	int i = 0;
	int fd;
	struct studen *studen_t;

	fd=open( "./mmap.txt",O_CREAT|O_RDWR,00777 );
	studen_t = (struct studen*)mmap(NULL,sizeof(struct studen)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);  
	close(fd);
	sleep(1);
	for(i=0;i<3;i++)
	{
		printf("number:%d name: %s age: %d\n",i+1,studen_t[i].name,(*(studen_t+i)).age);
	}
	munmap(studen_t,sizeof(struct studen)*10); //解除映射
	return 0;
}

运行的结果如下:

mmap_test1.c写入mmap.txt,mmap_test2.c再从mmap.txt读出。

总结:一般来说mmap方式是将文件与进程地址空间进行映射,对实际物理内存影响小,操作简单,重启会保存操作系统同步的映像和本身的文件,shm方式是将每个进程的共享内存与实际物理存储器进行映射,对实际物理内存影响大,所以一般用mmap会比较好。

猜你喜欢

转载自blog.csdn.net/weixin_42432281/article/details/87347874