linux 共享内存 shm_open ,mmap的正确使用

18 篇文章 1 订阅

  在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/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#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;
}

编译:
​​​​​​​gcc -o writer writer.c -lrt 

2. reader.c 打开内存共享文件读数据

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#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)
        {
                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;
}

编译:
​​​​​​​gcc -o reader reader.c -lrt

如果写入程序调用了 shm_unlink("shm-file0001");则读者程序将无法打开共享内存文件。

注意:笔者认为,共享内存 与 内存映射是两个概念,并不是用shm_open 打开的文件一定要用mmap来映射才能在进程间共享,用常规的Linux函数write写入数据以后,仍然能够共享。

  • 44
    点赞
  • 212
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值