进程间通信之共享内存-系统调用mmap详解(IPC范例篇)

IPC-进程间通信之共享内存-系统调用mmap详解(IPC范例篇)
(基础知识和范例1看我前一篇博客)
范例2:两个进程通过映射普通文件实现共享内存通信
适用于任何进程之间; 此时,需要打开或创建一个文件,然后再调用mmap();
mmap_write.c首先打开或创建一个文件,并把文件的长度设置为3个people结构大小。然后从mmap()的返回地址开始,设置了 10个people结构的值,然后进程睡眠5秒钟,等待其他进程映射同一个文件,最后解除映射;
mmap_read.c只是简单的映射一个文件,并以people数据结构的格式从mmap()返回的地址处读取10个people结构,并输出读取的值,然后解除映射。
源代码:

/* mmap_write.c */ #include #include #include #include #include #include #include typedef struct{ char name[4]; intage; }people; int main(int argc, char** argv) { int fd,i; people *p_map; char s[4]={0}; // map a normal file as shared mem: const char*file ="mmap_test"; //fd=open(file,O_CREAT|O_RDWR |O_TRUNC,0777); /* O_TRUNC */ fd=open(file,O_RDWR|O_TRUNC ); if(fd<0) { perror("open"); exit(-1); } /* fromSEEK_SETsearchsizeof(people)*10-1offset */ lseek(fd,sizeof(people)*3-1,SEEK_SET); write(fd,"",1); p_map = (people*) mmap( 0,sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ); printf("mmap address:%#x\n",(unsigned int)&p_map); close( fd ); s[0] = 'B'; s[1] = '\0'; for(i=0; i<10; i++) { s[0] ++; memcpy( ( *(p_map+i) ).name, &s,sizeof(s) ); ( *(p_map+i) ).age = 20+i; } printf(" initialize over \n "); sleep(5); munmap( p_map, sizeof(people)*10 ); printf( "umap ok \n" ); return 0; }

/* mmap_read.c */ #include #include #include #include #include #include typedef struct{ char name[4]; intage; }people; int main(int argc, char** argv) { int fd,i; // map a normal file as shared mem: const char*file ="mmap_test"; people *p_map; //fd=open( file,O_CREAT|O_RDWR,0777 ); fd=open( file,O_RDWR ); if(fd<0) { perror("open"); exit(-1); } p_map = (people*)mmap(0, sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED, fd,0); printf("mmap address:%#x\n",(unsigned int)&p_map); close(fd); for(i = 0; i<10; i++) { printf( "name: %s age %d\n",p_map[i].name,p_map[i].age/*(*(p_map+i)).name, (*(p_map+i)).age*/ ); }munmap( p_map,sizeof(people)*10 ); return 0; }

测试:
进程间通信之共享内存-系统调用mmap详解(IPC范例篇)
文章图片

注:mmap_write输出initialize over 之后,输出umap ok之前的输出和mmap_write输出initialize over 和umap ok之后的输出;
总结:
1. 最终被映射文件的内容的长度不会超过文件本身的初始大小,即映射不能改变文件的大小;
2. 可以用于进程通信的有效地址空间大小大体上受限于被映射文件的大小,但不完全受限于文件大小。打开文件被截短为3个people结构大小,而在mmap_write中初始化了10个people数据结构,在恰当时候(mmap_write输出initialize over 之后,输出umap ok之前)调用mmap_read会发现mmap_read将输出全部10个people结构的值.
注:在linux中,内存的保护是以页为基本单位的,即使被映射文件只有一个字节大小,内核也会为映射分配一个页面大小(X86 4K)的内存。当被映射文件小于一个页面大小时,进程可以对从mmap()返回地址开始的一个页面大小进行访问,而不会出错;但是,如果对一个页面以外的地址空间进行访问,则导致错误发生。因此,可用于进程间通信的有效地址空间大小不会超过文件大小及一个页面大小的和。
3、 文件一旦被映射后,调用mmap()的进程对返回地址的访问是对某一内存区域的访问,暂时脱离了磁盘上文件的影响。所有对mmap()返回地址空间的操作 只在内存中有意义,只有在调用了munmap()后或者msync()时,才把内存中的相应内容写回磁盘文件,所写内容仍然不能超过文件的大小
4. 普通文件内存映射的创建如下所示:
fd=open( file,O_RDWR );
if(fd<0)
{
perror(“open”);
exit(-1);
}
p_map = (people*)mmap(0, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd,0);
【进程间通信之共享内存-系统调用mmap详解(IPC范例篇)】范例3:父子进程通过匿名映射实现共享内存
适用于具有亲缘关系的进程之间; 由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。
对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可,
源代码:
#include #include #include #include #include #include typedef struct{ char name[4]; intage; }people; int main(int argc, char** argv) { int i; people *p_map; char s[4]={0}; p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0); if(fork() == 0) { sleep(2); for(i = 0; i<10; i++) printf("child read: the %d people's name: %s,age:%d\n",i+1,(*(p_map+i)).name,(*(p_map+i)).age); (*p_map).age = 18; (*p_map).name[0]='m'; (*p_map).name[1]='a'; (*p_map).name[2]='\0'; munmap(p_map,sizeof(people)*10); return 0; }s[0] = 'B'; s[1] = '\0'; for(i=0; i<10; i++) { s[0] ++; memcpy( ( *(p_map+i) ).name, &s,sizeof(s) ); ( *(p_map+i) ).age = 20+i; }sleep(5); printf( "parent read: the first people's name:%s,age:%d\n\n",(*p_map).name,(*p_map).age ); munmap( p_map,sizeof(people)*10 ); printf( "umap ok/n" ); return 0; }

测试:
进程间通信之共享内存-系统调用mmap详解(IPC范例篇)
文章图片

总结:
1. 匿名内存映射与使用 /dev/zero类型,都不需要真实的文件。要使用匿名映射之需要向 mmap 传入MAP_ANONYMOUS标志,并且 fd 参数置为-1 。
2. 所谓匿名,指的是映射区并没有通过 fd与磁盘文件相关联。匿名内存映射用在有血缘关系的进程间。
3. 匿名内存映射的创建如下所示:
p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
if(fork() == 0)
{/子进程处理/}
/父进程处理/

    推荐阅读