我们正在尝试改变 SQLite,一个嵌入式数据库系统,
使用 mmap() 而不是通常的 read() 和 write() 调用来访问
磁盘上的数据库文件。对整个数据使用单个大映射
文件。假设文件足够小,我们没有问题
在虚拟内存中为此寻找空间。
到目前为止,一切都很好。在许多情况下使用 mmap() 似乎更快一些
比 read() 和 write() 更重要。在某些情况下速度更快。
调整映射大小以提交写入事务
扩展数据库文件似乎是一个问题。为了延长
数据库文件,代码可以执行如下操作:
ftruncate(); // extend the database file on disk
munmap(); // unmap the current mapping (it's now too small)
mmap(); // create a new, larger, mapping
然后将新数据复制到新内存映射的末尾。
然而,munmap/mmap 是不可取的,因为它意味着下一次每次
访问数据库文件的页面时发生轻微页面错误并且
系统必须在操作系统页面缓存中搜索正确的帧
与虚拟内存地址相关联。换句话说,它会减慢
减少后续数据库读取。
在Linux上,我们可以使用非标准的mremap()系统调用来代替
munmap()/mmap() 来调整映射大小。这似乎避免了
轻微页面错误。
问题:在其他系统(例如 OSX)上应该如何处理这个问题?
没有 mremap() 吗?
目前我们有两个想法。还有一个关于每个问题的问题:
1) 创建大于数据库文件的映射。那么,在延伸的时候
数据库文件,只需调用 ftruncate() 即可扩展该文件
磁盘并继续使用相同的映射。
这将是理想的,并且在实践中似乎可行。然而,我们在
担心手册页中的此警告:
“更改底层文件大小的影响
映射到与添加或删除的区域相对应的页面上
该文件未指定。”
问:这是我们应该担心的事情吗?或者是不合时宜的
在此刻?
2)扩展数据库文件时,使用mmap()的第一个参数
请求与数据库的新页面相对应的映射
文件位于虚拟中当前映射之后
记忆。有效地扩展了初始映射。如果系统
无法满足在之后立即放置新映射的请求
首先,回到 munmap/mmap。
在实践中,我们发现 OSX 在定位方面非常出色
以这种方式映射,所以这个技巧在那里起作用。
问题:如果系统立即分配第二个映射
在虚拟内存中的第一个之后,最终是否安全
使用对 munmap() 的一次大调用来取消映射它们?