linux共享内存shm_open,mmap的正确使⽤
  在linux系统开发当中,时常需要在多个进程之间交换数据,在多个进程之间交换数据,有很多⽅法,但最⾼效的⽅法莫过于共享内存。
  linux共享内存是通过tmpfs这个⽂件系统来实现的,tmpfs⽂件系的⽬录为/dev/shm,/dev/shm是驻留在内存 RAM 当中的,因此读写速度与读写内存速度⼀样,/dev/shm的容量默认尺⼨为系统内存⼤⼩的⼀半⼤⼩,使⽤df -h命令可以看到。但实际上它并不会真正的占⽤这块内存,如果/dev/shm/下没有任何⽂件,它占⽤的内存实际上就是0字节,仅在使⽤shm_open⽂件时,/dev/shm才会真正占⽤内存。
在Linux系统使⽤共享内存,⼀般⽤到以下⼏个函数:
int shm_open(const char *name, int oflag, mode_t mode);
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);
int shm_unlink(const char *name);
int ftruncate(int fd, off_t length);
int shm_open(const char *name, int oflag, mode_t mode);
功能说明:shm_open ⽤于创建或者打开共享内存⽂件。笔者认为shm_open 也许仅仅是系统函数open的⼀个包装,不同之处就是
shm_open操作的⽂件⼀定是位于tmpfs⽂件系统⾥的,常见的Linux发布版的tmpfs⽂件系统的存放⽬录就是/dev/shm。
返回值:成功返回fd>0,  失败返回fd<0
参数说明:
name:要打开或创建的共享内存⽂件名,由于shm_open 打开或操作的⽂件都是位于/dev/shm⽬录的,因此name不能带路径,例如:/var/myshare 这样的名称是错误的,⽽ myshare 是正确的,因为 myshare 不带任何路径。如果你⼀定要在name添加路径,那么,请在/dev/shm⽬录⾥创建⼀个⽬录,例如,如果你想创建⼀个  bill/myshare 的共享内存⽂件,那么请先在/dev/shm⽬录⾥创
建 bill这个⼦⽬录,由于不同⼚家发布的linux系统的tmpfs的位置也许不是/dev/shm,因此带路径的名称也许在别的环境下打开不成功。
oflag:打开的⽂件操作属性:O_CREAT、O_RDWR、O_EXCL的按位或运算组合
mode:⽂件共享模式,例如 0777
------------------------------------------------------------------------------------------------------------------
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能说明: 将打开的⽂件映射到内存,⼀般是将shm_open打开的⽂件映射到内存,当然也可以将硬盘上的⽤open函数打开的⽂件映射到内存。这个函数只是将⽂件映射到内存中,使得我们⽤操作内存指针的⽅式来操作⽂件数据。
参数说明:
addr:要将⽂件映射到的内存地址,⼀般应该传递NULL来由Linux内核指定。
length:要映射的⽂件数据长度。
prot:映射的内存区域的操作权限(保护属性),包括PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags:标志位参数,包括:MAP_SHARED、MAP_PRIVATE与MAP_ANONYMOUS。
MAP_SHARED:  建⽴共享,⽤于进程间通信,如果没有这个标志,则别的进程即使能打开⽂件,也看不到数据。
MAP_PRIVATE: 只有进程⾃⼰⽤的内存区域
MAP_ANONYMOUS:匿名映射区
fd:  ⽤来建⽴映射区的⽂件描述符,⽤ shm_open打开或者open打开的⽂件。
offset:映射⽂件相对于⽂件头的偏移位置,应该按4096字节对齐。
返回值:成功返回映射的内存地址指针,可以⽤这个地址指针对映射的⽂件内容进⾏读写操作,读写⽂件数据如同操作内存⼀样;如果 失败则返回NULL。
------------------------------------------------------------------------------------------------------------------
int munmap(void *addr, size_t length);
功能说明: 取消内存映射,addr是由mmap成功返回的地址,length是要取消的内存长度,munmap 只是将映射的内存从进程的地址空间撤销,如果不调⽤这个函数,则在进程终⽌前,该⽚区域将得不到释放。
----------------------------------------------------------------------------------------------------------------------
int shm_unlink(const char *name);
功能说明:删除/dev/shm⽬录的⽂件,shm_unlink 删除的⽂件是由shm_open函数创建于/dev/shm⽬录的。可以⽤系统函数unlink来达到同样的效果,⽤/dev/shm + name 组成完整的路径即可,但⼀般不要这么做,因为系统的tmpfs的位置也许不是/dev/shm。⽤shm_open 创建的⽂件,如果不调⽤此函数删除,会⼀直存在于/dev/shm⽬录⾥,直到操作系统重启或者调⽤linux命令rm来删除为⽌。
------------------------------------------------------------------------------------------------------------
int ftruncate(int fd, off_t length);
功能说明:重置⽂件⼤⼩。任何open打开的⽂件都可以⽤这个函数,不限于shm_open打开的⽂件。
好了,以下是共享内存操作的实践代码:
1.writer.c ,创建内存共享⽂件并写⼊数据。
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#define MMAP_DATA_SIZE 1024
#define USE_MMAP 1
int main(int argc,char * argv[])
{
char * data;
int fd = shm_open("shm-file0001", O_CREAT|O_RDWR, 0777);
if (fd < 0) {
printf("shm_open failed!\n");
return -1;
}
ftruncate(fd, MMAP_DATA_SIZE);
if (USE_MMAP) {
data = (char*)mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);                if (!data) {
printf("mmap failed\n");
close(fd);
}
sprintf(data, "This is a share memory! %d\n", fd);
munmap(data, MMAP_DATA_SIZE);
}
else {
char buf[1024];
int len = sprintf(buf,"This is a share memory by write! ! %d\n",fd);
if (write(fd, buf, len) <= 0) {
printf("write file %d failed!%d\n",len,errno);
}
}
close(fd);
getchar();
shm_unlink("shm-file0001");
return 0;
}
编译:
g cc -o writer writer.c -lrt
2. reader.c 打开内存共享⽂件读数据
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define MMAP_DATA_SIZE 1024
int main(int argc,char * argv[])
{
char * data;
int fd = shm_open("shm-file0001", O_RDWR, 0777);
if(fd < 0)
{
mmap格式怎么打开printf("error open shm object\n");
return -1;
}
data = (char*)mmap(NULL, MMAP_DATA_SIZE, PROT_READ, MAP_SHARED, fd, 0);
if (!data) {
printf("mmap failed!\n");
close(fd);
return -1;
}
printf(data);
munmap(data,MMAP_DATA_SIZE);
close(fd);
getchar();
return 0;
}
编译:
g cc -o reader reader.c -lrt
如果写⼊程序调⽤了 shm_unlink("shm-file0001");则读者程序将⽆法打开共享内存⽂件。
注意:笔者认为,共享内存 与 内存映射是两个概念,并不是⽤shm_open 打开的⽂件⼀定要⽤mmap来映射才能在进程间共享,⽤常规的Linux函数write写⼊数据以后,仍然能够共享。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。