我刚看到这个很好的写时复制指针实现.它看起来非常通用和有用,所以我的问题是:这样的类是否包含在任何C++工具包(boost,loki等)中?如果没有,我真的很想知道为什么,因为它是一个非常有用的习惯用语,显然一个通用的实现似乎可行(就像我链接的那个).
当我使用mmap创建写时复制映射(MAP_PRIVATE)时,只要我写入特定地址,就会复制此映射的某些页面.在我的程序中的某一点,我想弄清楚哪些页面实际上已被复制.有一个名为"mincore"的调用,但它只报告页面是否在内存中,这与正在复制的页面不同.
有没有办法弄清楚哪些页面被复制了?
我是一名程序员,使用基于Linux的服务器开发多人在线游戏.我们为我们的世界使用"实例化"架构.这意味着进入世界区域的每个玩家都获得该区域的副本以与其队员一起玩,并且独立于在同一区域中玩的所有其他玩家.
在内部,我们为每个实例使用单独的过程.最初,每个实例进程都会启动,只加载给定区域所需的资源,生成它的随机地形,然后允许来自玩家的新连接.实例使用的内存量通常约为25兆,包括资源和随机生成的实体级别.
为了减少实例的内存占用并加快生成时间,我们改为创建单个主实例的方法,该实例加载任何实例可能需要的所有资源(大约150兆内存)然后当需要一个新实例时,使用fork()函数生成一个新实例并利用copy-on-write内存共享,这样新实例只需要内存来获取它的"唯一"数据集.随机生成的级别和构成每个实例的唯一数据的实体的占用空间约为3-4兆内存.
不幸的是,内存共享并没有像我想的那样好用.很多内存页面似乎都没有共享.
首先,当我们在prefork实例中加载更多的数据集时,每个分叉实例所需的内存都会关闭,但最终会出现一个拐点,在prefork中加载更多资产实际上会增加每个分叉实例使用的数据.
我们得到的最好结果是加载大约80兆的数据集前叉,然后让新的实例需要加载其余的.这导致每个实例大约7-10额外的meg和80mcg的固定成本.当然是一个很好的改进,但不是理论上最好的.
如果我加载整个150兆的数据集然后fork,每个分叉的实例使用大约50多兆的内存!简直比简单地无所事事更糟糕.
我的问题是,如何在prefork实例中加载我的所有数据集,并确保我只获得每个实例数据的最小集合,作为每个实例的内存占用量.
我有一个关于这里发生了什么的理论,我想知道是否有人能够帮助确认我是这样的.
我认为这与malloc自由链有关.prefork实例的每个内存页面可能还有一些内存空闲点.如果在随机级别生成期间分配了恰好适合页面中某个空闲点的内容,那么整个页面将被复制到分叉进程中.
在Windows中,您可以创建备用堆,并更改进程使用的默认堆.如果这是可能的,它将解决问题.有没有办法在linux中做这样的事情?我的调查似乎表明你做不到.
另一种可能的解决方案是,如果我能以某种方式丢弃现有的malloc自由链,迫使malloc从操作系统为后续调用分配新的内存.我试图查看malloc的实现,看看这是否很容易实现,但看起来可能有些复杂.如果有人对这个领域有任何想法或建议从哪个方法开始,我很乐意听到它.
最后,如果有人对这里可能出现的问题有任何其他想法,我真的很想听听他们.非常感谢!
我得到了写拷贝背后的想法.当我fork时,堆被标记为CoW,当任何进程尝试更改它时,会生成一个副本.问题是:我是否必须在孩子的过程中释放它?假设一个父有一个动态char*数组,然后它就是forks.子进程打印一些const char,然后退出.子进程根本没有改变堆.会有内存泄漏吗?
编辑:我的子进程在堆上打印数组,但不修改它.Valgrind说如果我不释放那个阵列就会有泄漏.我释放它时没有泄漏/内存错误.
当我不断向redis写入数据时,写时复制使用的内存不断增加。即使我将程序编写为睡眠足够长的时间,以便 Redis 能够完成所有后台保存(最后一个内存消息是写时复制使用的内存为 0 MB),下一次后台保存将返回到高位数字。
例子,
牛使用了1300MB内存
牛使用1400MB内存
牛使用了 0MB 内存
牛使用了1500MB内存
这些到底意味着什么?据我所知,如果写时复制内存不断增加,就不可能有足够的内存。此外,每次后台保存都使用大量内存时,redis 似乎不起作用。Jedis总是遇到socket超时异常。
我注意到在Python中,字符串对象只保留一份副本。就像下面的代码:
>>> s1='abcde'
>>> s2='abcde'
>>> s1 is s2
True
Run Code Online (Sandbox Code Playgroud)
s1和s2指向同一个对象。
当我编辑 s1 时,s2 仍然保留对象('abcde'),但 s1 指向一个新副本。这种行为就像写时复制。
>>> s1=s1+'f'
>>> s1 is s2
False
>>> s1
'abcdef'
>>> s2
'abcde'
Run Code Online (Sandbox Code Playgroud)
那么Python真的在字符串对象上使用写时复制机制吗?
var absences = [0, 2, 0, 4, 0, 3, 1, 0]
let midpoint = absences.count / 2
var firstHalf = absences.prefix(upTo: midpoint)
let secondHalf = absences.suffix(from: midpoint)
Run Code Online (Sandbox Code Playgroud)
来自Apple的报价:
firstHalf和secondHalf slice都不会分配任何新的存储空间.相反,每个都提供了缺席数组存储的视图.
当我尝试变异firstHalf如下:
firstHalf[1] = 19
Run Code Online (Sandbox Code Playgroud)
firstHalf更改的值但原始数组absences保持不变(firstHalf[1]等于19,而absences[1]等于2)那么在后台会发生什么.我是否通过改变数组切片来实例化一个新数组?提前致谢.
当我们malloc内存时,只有虚拟内存可用,而且它实际上指向零页。当我们尝试写入malloced内存时,将分配真正的物理内存,此时,将出现copy-on-wright,将零从零页复制到通过页错误映射的物理内存。我的问题是,在linux源代码中如何/在何处实现按需补零,我想禁用此功能来进行一些测试。我猜这可能发生在页面错误过程中,而不是 brk() 或 mmap() 中。
正如标题所说,我试图证明自己在 Swift 中的 String 支持 COW(写入时复制)。但我找不到证据。在尝试了以下代码后,我在 Array 和 Dictionary 上证明了 COW:
func address(of object: UnsafeRawPointer) -> String {
let addr = Int(bitPattern: object)
return String(format: "%p", addr)
}
var xArray = [20, 30, 40, 50, 60]
var yArray = xArray
// These two addresses were the same
address(of: xArray)
address(of: yArray)
yArray[0] = 200
// The address of yArray got changed
address(of: yArray)
Run Code Online (Sandbox Code Playgroud)
但是对于 String 类型,它不起作用。
var xString = "Hello World"
var yString = xString
// These two addresses were …Run Code Online (Sandbox Code Playgroud) 根据mmap()手册页:
MAP_PRIVATE
创建私有写时复制映射。映射的更新对于映射同一文件的其他进程来说是不可见的,并且不会传递到底层文件。 未指定 mmap() 调用后对文件所做的更改是否在映射区域中可见。
问题:如何防止在 mmap() 处理文件后对底层文件的更改对我的程序可见?
背景:我正在为文本编辑器设计一个数据结构,旨在允许有效地编辑巨大的文本文件。数据结构类似于磁盘上的绳索,但实际字符串是指向原始文件中 mmap() 范围的指针。
由于文件可能非常大,因此设计有一些限制:
不得将整个文件加载到 RAM 中,因为该文件可能大于可用的物理 RAM
不得在打开时复制文件,因为这会使打开新文件变得非常慢
必须适用于不支持写时复制 ( cp --reflink/ ioctl_ficlone)的文件系统,例如 ext4
不得依赖强制文件锁定,因为这已被弃用,并且需要-o mand文件系统中的特定挂载选项
只要更改在我的 mmap() 中不可见,底层文件就可以在文件系统上更改
只需要支持最近的Linux并且使用Linux特定的系统API就可以了
我正在设计的数据结构将通过将范围的开始和结束索引存储到 mmap()-ed 缓冲区中来跟踪文件中未编辑和已编辑范围的列表。当用户浏览文件时,从未被用户修改过的文本范围将直接从mmap()原始文件中读取,而交换文件将存储用户已编辑但已更改的文本范围。没有被拯救。
当用户保存文件时,数据结构会使用copy_file_range来拼接交换文件和原始文件以组装新文件。为了使这种拼接发挥作用,我的程序所看到的原始文件必须在整个编辑会话期间保持不变。
问题:在我的文本编辑器中进行未保存的更改后,用户可能同时让其他程序修改同一文件,可能是其他文本编辑器或就地修改文本文件的其他程序。
在这种情况下,编辑器可以使用 inotify 检测此类外部更改,然后我想为用户提供两个关于如何继续的选项:
放弃所有未保存的更改并从磁盘重新读取文件,实现此选项相当简单
允许用户继续编辑文件,稍后用户应该能够将未保存的更改保存在新位置或覆盖其他程序所做的更改,但实现这一点似乎很棘手
由于我的编辑器在打开文件时没有复制该文件,因此当其他程序覆盖该文件时,我的数据结构正在跟踪的文本范围可能会变得无效,因为磁盘上的数据已更改,并且这些更改现在已更改通过我可见mmap()。这意味着,如果我的编辑器在从另一个进程修改文件后尝试写入未保存的更改,则可能会使用新文件中的数据来拼接旧文件中的文本范围,这可能意味着我的编辑器可能会生成保存未保存的更改时损坏的文件。
我不认为咨询锁在所有情况下都能挽救这种情况,因为其他程序可能不支持咨询锁。
我理想的解决方案是,当其他程序覆盖该文件时,系统应该透明地复制该文件,以允许我的程序继续看到旧版本,而其他程序完成对磁盘的写入并使其版本在文件中可见。文件系统。我认为 ioctl_ficlone 可以使这成为可能,但据我了解,这只适用于像 btrfs 这样的写时复制文件系统。
这样的事可能吗?
任何其他解决此问题的建议也将受到欢迎。