系统相关
首页 > 系统相关> > linux下共享内存sharedMemory(shm)和内存映射memorymap的关系

linux下共享内存sharedMemory(shm)和内存映射memorymap的关系

作者:互联网

        这一段因为持续使用共享内存和内存映射,最早的例子也是从网上找的,然后改了一下满足我们的需求,但底层的区别和联系没有细想。今天因为要测试给认知框架开发的数据共享库涉及到了共享内存,同事问这些事情,而前面看过system V、Posix、XSI的共享内存的关系,但是忘记了。于是又大概看了一下,并进行了仔细思考。

        sharedMemory

        首先,我们要明确共享内存(shm)的应用场景是什么?也就是在什么场合使用共享内存。一般来说,多进程共享同一内存区域或者跨进程访问某个区域时才需要共享内存。也就是同一片内存,被多个进程共享。

        共享内存,顾名思义,就是预留出的内存区域,它允许一组进程对其访问。 共享内存是system v IPC中三种通信机制最快的一种,也是最简单的一种。对于进程来说, 获得共享内存后,他对内存的使用和其他的内存是一样的。由一个进程对共享内存所进行的 操作对其他进程来说都是立即可见的,因为每个进程只需要通过一个指向共享内存空间的指针就可以来读取 共享内存中的内容(说白了就好比申请了一块内存,每个需要的进程都有一个指针指向这个内存) 就可以轻松获得结果。

        memoryMap

        内存映射,一般指的是为了加快文件操作,将文件系统上某个文件的一段内容映射到内存中,用户可以在核外地址空间直接读写内核地址空间的地址对应的内容,这样进程在读写文件时,少了一次拷贝开销。这个一般是用在一个进程内的。

        由此看来,sharedMemory用于多个进程共享同一块地址空间,实现多个人访问同一个区域,目的是共享。mmap内存映射的目的是加快访问速度。这两个并不冲突,所以可以组合到一起使用。

  mmap的用途

  1. 内存映射->文件内存映射,内存映射分为2种,文件内存映射和匿名映射。使用mmap系统调用把一个文件映射到内存中。使用方法一般是open()一个文件之后把文件描述符传入mmap()进行映射。
  2.  共享内存:主要告诉你,共享内存是最快的进程间通信方式,然后:

        就POSIX接口的共享内存来说,他们的底层都是调用mmap,不同的只是共享内存打开文件调用shm_open(),而内存映射调用open()。那shm_open有什么神奇的呢?看了下glibc2.29中的源码:

/* Open shared memory object. */ 
int 
shm_open (const char *name, int oflag, mode_t mode) 
{ 
SHM_GET_NAME (EINVAL, -1, ""); 
oflag |= O_NOFOLLOW | O_CLOEXEC; 
int state; 
pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state); 
int fd = open (shm_name, oflag, mode); 
...... 
pthread_setcancelstate (state, NULL); 
return fd; 
} 

        代码里赫然写着open,看来shm_open只不过是封装了一个更安全的open而已。

为什么共享内存一定要放在/dev/shm/下?

        上面代码中,shm_open传入的参数叫name,然而到open的时候却变成了shm_name,但是前后却找不到shm_name定义的地方。大佬们写的代码都这么神奇的吗?仔细一看,原来在上边的宏里:

SHM_GET_NAME (EINVAL, -1, ""); 

        宏中声明了shm_name,构造出shm_dir,shm_dir的内容就是/dev/shm/,之后通过和name拼接:

__mempcpy (__mempcpy (__mempcpy (shm_name, shm_dir, shm_dirlen), \
prefix, sizeof prefix - 1),name,namelen) 

        生成了shm_name。

        但是这只是解释了是怎么干的,却没有解释,为什么要这么干,为什么GlibC要在代码里写死把共享内存放在这个分区下呢?

        /dev/shm/使用了一种特殊的文件系统tmpfs,它是虚拟的,并不是磁盘上的真实的分区,它如何快,如何好,如何厉害等等……通过df命令查看/dev/shm/分区,类型确实写着tmpfs,而使用文件内存映射生成的文件,却平平无奇,看不出有什么特殊的。莫非,共享内存和文件内存映射的区别在这里?共享内存使用了一种特殊的文件系统,而文件内存映射没有吗?但是他们明明传入mmap的参数也是一样的啊…也即是说内存映射和共享内存用的都是mmap,但叫法和用途却不同,使用的文件和涉及的文件系统也不同。我猜测tmpfs整个就构建在内核空间,而不是用户空间,所以多个进程才可以共享。

/dev/shm或者tmpfs

        内存文件系统tmpfs,在操作系统上有一个路径是/dev/shm,该目录下的文件都在内存中存储,断电后消失。如何打开/dev/shm下的文件呢?shm_open可以打开。就像open可以打开普通文件一样。因为文件系统在内存中,其操作读写也都在内存中,不存在写盘的操作,所以很快。

Tmpfs is a file system which keeps all files in virtual memory. 
Everything in tmpfs is temporary in the sense that no files will 
be created on your hard drive. If you unmount a tmpfs instance, 
everything stored therein is lost. 

        这段说tmpfs是一个文件系统,所有的文件都存在于虚拟内存中,物理磁盘上没有任何文件。如果把一个tmpfs实例解挂了,其中存储的内容就丢失了。这段和网上说的如出一辙,对一个新手来说一样的云里雾里。什么叫在虚拟内存里?虚拟内存不就是个抽象吗?顶多就是内核的一个数据结构啊。没有文件在磁盘上?那文件去哪了?在物理内存上吗?不大对啊,物理内存直接当硬盘使吗?

Since tmpfs lives completely in the page cache and on swap, all tmpfs
pages currently in memory will show up as cached. It will not show up
as shared or something like that. Further on you can check the actual 
RAM+swap use of a tmpfs instance with df(1) and du(1). 

        这段说tmpfs文件系统完全存活在page cache和swap中,当前在内存中的tmpfs页会显示为cacheed,不会被显示为shared或其他。可以通过df和du命令查看tmpfs实例真实使用的物理内存+swap大小。

        随便找一台测试机器,用free -g命令看下:

[xxxxx ~]# free -g 
      total used free shared buff/cache available 
Mem:  124   0     33   0         90     82 
Swap: 0 17575006175232 17179869183

shared为0,buff/cache为90G

然后删除/dev/shm/下的几个文件看看:

[xxxxx ~]# rm /dev/shm/News_Share_Memory_V10 
[xxxxx ~]# free -g 
    total used free shared buff/cache available 
Mem: 124    0   36    0       87       84 
Swap: 0 17575006175232 17179869183 
[xxxxx ~]# rm /dev/shm/Video_Share_Memory_V10 
[xxxxx ~]# free -g 
    total used free shared buff/cache available 
Mem: 124    0   39    0        84        88 
Swap: 0 17575006175232 17179869183 

        嗯,大小果真对的上,证明文中所言非虚,tmpfs文件系统中的实例,会显示在cache中,而不是shared,尽管我们会管这个叫”共享内存“。

There is always a kernel internal mount which you will not see at 
all. This is used for shared anonymous mappings and SYSV shared 
memory. 
This mount does not depend on CONFIG_TMPFS. If CONFIG_TMPFS is not 
set, the user visible part of tmpfs is not build. But the internal 
mechanisms are always present. 

        这段是说着,内核内部会有一个挂载,你是看不到的,当你使用匿名共享内存映射或者System V共享内存的时候,会用到这个隐藏的挂载。而且,即使内核中的编译选项CONFIG_TMPFS没有设置,tmpfs那没有编译,这个内部隐藏的机制依然有效。

        这点对我们来说意义重大,这意味着,共享内存和tmpfs文件系统,都是使用了内核提供的机制来实现的,共享内存的实现,和tmpfs系统是没有依赖关系的。那么这个机制到底是啥呢?到底是怎么实现的呢?在此挖个坑。

glibc 2.2 and above expects tmpfs to be mounted at /dev/shm for 
POSIX shared memory (shm_open, shm_unlink). Adding the following 
line to /etc/fstab should take care of this: 
tmpfs /dev/shm tmpfs defaults 0 0 
Remember to create the directory that you intend to mount tmpfs on 
if necessary. 

        这段就更直白了,glibc在实现POSIX共享内存时,会预设tmpfs文件系统挂载在/dev/shm/,所以用POSIX共享内存的时候,记得在/etc/fstab里编辑一下,把这个分区给挂载上。

共享内存和文件内存映射有什么区别?

        首先是共享内存和文件内存映射的接口、用法不一样,GLIBC的POSIX的共享内存实现会默认把共享内存文件放在/dev/shm/分区下,如果没有这个分区,需要手动挂载一下。

        然后就是共享内存和文件内存映射,在内核中的实现原理,都使用了内核的cache和swap机制,完全没有区别。

        虽然System V与POSIX共享内存都是通过tmpfs实现,但是受的限制却不相同。也就是说/proc/sys/kernel/shmmax只会影响SYS V共享内存,/dev/shm只会影响Posix共享内存。实际上,System V与Posix共享内存本来就是使用的两个不同的tmpfs实例(instance)

标签:共享内存,映射,memorymap,dev,内存,linux,tmpfs,shm
来源: https://blog.csdn.net/jinking01/article/details/120274820