简体   繁体   English

快速调整mmap文件的大小

[英]Fast resize of a mmap file

I need a copy-free re-size of a very large mmap file while still allowing concurrent access to reader threads. 我需要一个非常大的mmap文件的无副本重新大小,同时仍然允许并发访问读取器线程。

The simple way is to use two MAP_SHARED mappings (grow the file, then create a second mapping that includes the grown region) in the same process over the same file and then unmap the old mapping once all readers that could access it are finished. 简单的方法是在同一个文件中使用两个MAP_SHARED映射(增长文件,然后创建包含增长区域的第二个映射),然后在所有可以访问它的读者完成后取消映射旧映射。 However, I am curious if the scheme below could work, and if so, is there any advantage to it. 但是,我很好奇下面的方案是否有效,如果有的话,它是否有任何优势。

  1. mmap a file with MAP_PRIVATE 使用MAP_PRIVATE mmap一个文件
  2. do read-only access to this memory in multiple threads 在多个线程中对此内存进行只读访问
  3. either acquire a mutex for the file, write to the memory (assume this is done in a way that the readers, which may be reading that memory, are not messed up by it) 获取文件的互斥锁,写入内存(假设这是以读者可能正在读取内存的方式完成的,不会被它搞砸)
  4. or acquire the mutex, but increase the size of the file and use mremap to move it to a new address (resize the mapping without copying or unnecessary file IO.) 或获取互斥锁,但增加文件的大小并使用mremap将其移动到新地址(调整映射大小而不复制或不必要的文件IO。)

The crazy part comes in at (4). 疯狂的部分出现在(4)。 If you move the memory the old addresses become invalid, and the readers, which are still reading it, may suddenly have an access violation. 如果移动内存,旧地址将变为无效,仍在阅读内容的读者可能会突然发生访问冲突。 What if we modify the readers to trap this access violation and then restart the operation (ie don't re-read the bad address, re-calculate the address given the offset and the new base address from mremap.) Yes I know that's evil, but to my mind the readers can only successfully read the data at the old address or fail with an access violation and retry. 如果我们修改读取器以捕获此访问冲突然后重新启动操作(即不重新读取错误地址,重新计算给定偏移量的地址和mremap中的新基址),该怎么办。是的我知道这是邪恶的但是在我看来,读者只能成功读取旧地址的数据,或者因访问冲突而失败并重试。 If sufficient care is taken, that should be safe. 如果采取足够的谨慎措施,那应该是安全的。 Since re-sizing would not happen often, the readers would eventually succeed and not get stuck in a retry loop. 由于重新调整大小不会经常发生,读者最终会成功并且不会陷入重试循环。

A problem could occur if that old address space is re-used while a reader still has a pointer to it. 如果在读取器仍具有指向它的指针时重新使用旧地址空间,则可能会出现问题。 Then there will be no access violation, but the data will be incorrect and the program enters the unicorn and candy filled land of undefined behavior (wherein there is usually neither unicorns nor candy.) 然后将没有访问冲突,但数据将是不正确的,并且程序进入不确定行为的独角兽和糖果填充的土地(其中通常既没有独角兽也没有糖果。)

But if you controlled allocations completely and could make certain that any allocations that happen during this period do not ever re-use that old address space, then this shouldn't be a problem and the behavior shouldn't be undefined. 但是如果你完全控制了分配并且可以确定在此期间发生的任何分配都不会重复使用那个旧的地址空间,那么这不应该是一个问题,并且行为不应该是未定义的。

Am I right? 我对吗? Could this work? 这可行吗? Is there any advantage to this over using two MAP_SHARED mappings? 使用两个MAP_SHARED映射是否有任何优势?

It is hard for me to imagine a case where you don't know the upper bound on how large the file can be. 我很难想象一个你不知道文件大小的上限的情况。 Assuming that's true, you could "reserve" the address space for the maximum size of the file by providing that size when the file is first mapped in with mmap(). 假设这是真的,您可以通过在首次使用mmap()映射文件时提供该大小来“保留”地址空间以获得文件的最大大小。 Of course, any accesses beyond the actual size of the file will cause an access violation, but that's how you want it to work anyway -- you could argue that reserving the extra address space ensures the access violation rather than leaving that address range open to being used by other calls to things like mmap() or malloc(). 当然,超出文件实际大小的任何访问都会导致访问冲突,但这就是你希望它的工作方式 - 你可以争辩说保留额外的地址空间可以确保访问冲突而不是让地址范围保持开放被其他调用用于mmap()或malloc()之类的东西。

Anyway, the point is with my solution, you never move the address range, you only change its size and now your locking is around the data structure that provides the current valid size to each thread. 无论如何,关键在于我的解决方案,你永远不会移动地址范围,你只改变它的大小,现在你的锁定是围绕为每个线程提供当前有效大小的数据结构。

My solution doesn't work if you have so many files that the maximum mapping for each file runs you out of address space, but this is the age of the 64-bit address space so hopefully your maximum mapping size is no problem. 如果您有这么多文件,每个文件的最大映射会使您超出地址空间,我的解决方案不起作用,但这是64位地址空间的年龄,所以希望您的最大映射大小没有问题。

(Just to make sure I wasn't forgetting something stupid, I did write a small program to convince myself creating the larger-than-file-size mapping gives an access violation when you try to access beyond the file size, and then works fine once you ftruncate() the file to be larger, all with the same address returned from the first mmap() call.) (只是为了确保我没有忘记一些愚蠢的东西,我确实写了一个小程序来说服自己创建大于文件大小的映射,当你试图超出文件大小访问时会产生访问冲突,然后工作正常一旦你ftruncate()文件变大,所有从第一个mmap()调用返回的地址都相同。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM