我有一个嵌入式ARM Linux盒子,内存很小(512MB)且没有交换空间,我需要在上面创建并操作一个相当大的文件(~200MB)。把整个文件加载到内存中,在内存中修改内容,然后再写回去,有时会触发OOM杀手,这是我想要避免的。
我的解决方法是使用mmap()
将该文件映射到进程的虚拟地址空间中;这样,对映射内存区域的读取和写入将转到本地闪存文件系统,并且如果内存不足,Linux可以将一些的内存页刷新回磁盘以释放一些RAM,从而避免OOM-killer。 (这可能会使我的程序变慢,但对于这种情况慢是可以接受的)
然而,即使使用mmap()
调用,我仍然偶尔看到进程被OOM-killer杀死,而执行以上操作。
我的问题是,我是否过于乐观地认为Linux在存在大量和受限RAM的情况下会表现得如何? (即是否一个200MB的文件,然后读/写到是不是足够聪明,以在内存不足时换出页面,但我在使用它时做错了什么?
对于我来说,做映射的代码在这里:
void FixedSizeDataBuffer :: TryMapToFile(const std::string & filePath, bool createIfNotPresent, bool autoDelete)
{
const int fd = open(filePath.c_str(), (createIfNotPresent?(O_CREAT|O_EXCL|O_RDWR):O_RDONLY)|O_CLOEXEC, S_IRUSR|(createIfNotPresent?S_IWUSR:0));
if (fd >= 0)
{
if ((autoDelete == false)||(unlink(filePath.c_str()) == 0)) // so the file will automatically go away when we're done with it, even if we crash
{
const int fallocRet = createIfNotPresent ? posix_fallocate(fd, 0, _numBytes) : 0;
if (fallocRet == 0)
{
void * mappedArea = mmap(NULL, _numBytes, PROT_READ|(createIfNotPresent?PROT_WRITE:0), MAP_SHARED, fd, 0);
if (mappedArea)
{
printf("FixedSizeDataBuffer %p: Using backing-store file [%s] for %zu bytes of data\n", this, filePath.c_str(), _numBytes);
_buffer = (uint8_t *) mappedArea;
_isMappedToFile = true;
}
else printf("FixedSizeDataBuffer %p: Unable to mmap backing-store file [%s] to %zu bytes (%s)\n", this, filePath.c_str(), _numBytes, strerror(errno));
}
else printf("FixedSizeDataBuffer %p: Unable to pad backing-store file [%s] out to %zu bytes (%s)\n", this, filePath.c_str(), _numBytes, strerror(fallocRet));
}
else printf("FixedSizeDataBuffer %p: Unable to unlink backing-store file [%s] (%s)\n", this, filePath.c_str(), strerror(errno));
close(fd); // no need to hold this anymore AFAIK, the memory-mapping itself will keep the backing store around
}
else printf("FixedSizeDataBuffer %p: Unable to create backing-store file [%s] (%s)\n", this, filePath.c_str(), strerror(errno));
}
如果必须的话,我可以将这段代码重写为普通的文件I/O,但如果mmap()
能胜任这个任务(或者如果不能胜任,我至少想知道为什么不能),那就太好了。
madvise(MADV_DONTNEED)
来释放不再需要的映射文件范围,并且对文件进行“窗口化”处理。否则,mmap()将会在内存中保留数据。 - JATothrim