Ody*_*Ody 4 c c++ linux mmap shared-memory
我将一些数据存储在多个进程使用的共享内存数组中。在某些时候,我想扩大阵列。
假设进程之间已经有同步机制
最初,进程 1 将创建段,进程 2 将打开它。
过程1
shm_open() O_CREAT
ftruncate()
mmap() MAP_SHARED
过程2
shm_open()
mmap()
在某一时刻,一个进程想要扩大数组并调整共享段的大小。
处理 1 次调用
ftruncate()
mremap() MREMAP_MAYMOVE
是否应该通知进程 2 调整大小并调用mremap()更新它自己的虚拟地址?
如果必须通知进程 2,我正在考虑使用一些元数据打开第二个共享内存段,例如表的容量和互斥锁。每个进程最初从共享内存中存储表的容量,并在每个操作中根据共享内存元数据值检查本地值。如果值已更改,它将调用mremap()
如果mremap()在调整大小后必须在每个进程上调用,这是一种正确的方法吗?
是否应该通知进程 2 调整大小并调用
mremap()更新它自己的虚拟地址?
进程 2 已经映射了共享内存段的特定区域。另一个增加段大小的过程不会使该映射无效。这也不会改变在进程 2 中映射的共享内存段的区域——即使进程 2 最初映射了整个段,超出段原始末尾的部分也不会自动映射到进程 2 中。
因此,只要进程 2 不需要访问该段的其他页面,它就根本不需要更新其映射。然而,如果它想访问额外的共享内存,那么它确实需要更新它的映射。它可以尝试做到这一点通过Linux特有mremap(),或由munmap()后面mmap()。后者要求它仍然具有该段的打开文件描述符。无论哪种方式,都可能无法映射从相同基地址开始的更大空间。就此而言,可能根本无法映射更大的空间,无论是否有任何其他进程能够这样做。
关于映射的基地址
默认情况下,mremap()将尝试修改映射区域而不更改其基地址,并且可以通过从请求原始基地址并传递标志来要求munmap()+mmap()执行相同操作。如果无法在该地址扩展映射,这些将失败。在后一种情况下,这也将使该段完全未映射。mmap()MAP_FIXED
您可以通过MREMAP_MAYMOVE在mremap()标志中指定或避免指定MAP_FIXEDto来允许选择新的基地址mmap()。当在同一地址扩展映射失败时,这种尝试可能会成功,但请注意,基地址的更改会使该进程指向原始映射的所有指针无效,无论存储在何处。
因此,如果您打算更改基地址,那么最好不要将任何指针存储到映射中,除了指向基地址的单个指针。在这种情况下,通过该指针或相对于它的其他方式访问内容。
如果必须通知进程 2,我正在考虑使用一些元数据打开第二个共享内存段,例如表的容量和互斥锁。每个进程最初从共享内存中存储表的容量,并在每个操作中根据共享内存元数据值检查本地值。如果值已更改,它将调用
mremap()如果
mremap()在调整大小后必须在每个进程上调用,这是一种正确的方法吗?
如果您正确同步对元数据的访问,您的方案听起来可行。事实上,虽然可能有其他原因这样做,但问题中没有任何内容表明甚至有必要将元数据放在单独的共享内存段中。扩展不应影响段的原始页面的内容或其在进程 2 中的映射,因此如果元数据存储在那里,那么进程 2 应该仍然能够在扩展后访问它们。
但是请注意,如果用于同步对段的访问的互斥锁、信号量或类似物位于该段内,则您需要考虑mremap()可能会移动它的事实,并且munmap()/mmap()将使您需要更复杂的恢复方案munmap()成功但随后mmap()失败的情况。
在理解所有这些时,记住以下几点可能是有用的
共享内存段的大小是段的一个属性,由内核维护。
段的每个映射的特性,包括映射的区域(特定范围的页)和它所映射到的基地址,是特定进程的属性,独立于相同或其他段中的所有其他映射过程。
段的每个映射的特征也很大程度上独立于段本身。特别是,映射区域的偏移量和大小不会随着片段大小的变化而变化。
\n\n\n如果必须通知进程 2,我会考虑使用一些元数据(例如表 xe2x80x99s 容量和互斥体)打开第二个共享内存段。每个进程最初从共享内存存储 table\xe2\x80\x99s 容量,并在每次操作时根据共享内存元数据值检查本地值。如果值改变了它会调用
\nmremap()
除了约翰·博林格(John Bollinger)的出色回答之外,我还想指出,有一种方法可以不使用元数据共享内存段。例如,如果您有为您提供的文件描述符shm_open(),则另一种解决方案是强制其他进程使用以下命令检查段的大小fstat:
...\n\nstruct stat statbuf = { 0 };\nint fd = shm_open(...);\n\n...\n\n// Check whether the shared segment has changed\nfstat(fd, &statbuf);\n\nif (statbuf.st_size != current_size) // current_size is stored somewhere\n{\n // Remap the shared memory segment using statbuf.st_size here\n}\n\n\xc2\xb7\xc2\xb7\xc2\xb7\nRun Code Online (Sandbox Code Playgroud)\n\n您必须将共享内存段视为系统中某个位置的文件。出于参考目的,shm_overviewfstat()页面建议为此目的使用。