« 顿悟? | 返回首页 | 父亲 »

如何给指定地址空间拍一个快照

需求来自于,我希望可以对 lua 虚拟机中的内容做持久化,却又不希望 stop the world 。这需要利用 os 的功能,对内存做一个快照。简单的 fork 就可以达到快照的要求,但是 fork 会快照整个进程的地址空间,这不是我想要的。

这两天和几位同学讨论了各种方案,比如 memcpy ,比如 fork+exec 传递 shm_open 的 fd , fork 后 munmap 不用的区域等等。最后我认为如下方案相对更满意一些。我并没有实现出来, 写 blog 只是做个记录。

在启动主进程之初,把需要快照的地址空间用 mmap 设置好。使用 MAP_SHARED 方式。这个时候,子进程是干净的,占用的物理内存很小。这个子进程休眠待命。

在主进程中创建 lua state ,自定义 alloc ,指向前面 mmap 的空间。这里这样做的前提是,确定 lua state 占用的空间不会超过预留的空间。

当主进程想做 lua state 的 snapshot ,通知待命子进程。再由子进程 fork 一份出来,并使用 minherit 把之前 mmap 的地址段修改为 MAP_PRIVATE 的,至此,快照完成。

做完快照的进程,可以把 lua state 指针指向那块地址空间里,按部就班的做持久化工作了。

这里需要使用一个锁,保证主进程下达 snapshot 操作指令后一直等待最终持久化进程做完 minherit 的修改完成,这样才确保 snapshot 的时刻是确定的。


这样做是比直接 fork 要繁琐需要,适合 lua state 占用的内存仅仅是主进程数据区的一小部分的情况。且持久化时间较长,如此可回避 fork 后,大量不必要的内存页复制。

如果整个系统都由自己设计,也可以 fork 后调用 munmap 去掉显然不再需要的数据区,不过这需要对进程内其它模块有足够了解,我比较怀疑可以做的好。


我在这方面经验不足,或许还有更简单有效的方案。

Comments

学习了啊,方法很不错啊,呵呵,风的博客www.feng521.com

了解了。。对C10K系统没什么经验,闹笑话了:D 多谢云风大人指点

流程如下:
1,主进程停止工作
2,主进程通知子进程进行内容持久化
3,子进程创建子进程2;
4,子进程2 minherit 备份内存为Private;
5,主进程继续执行工作;
6,子进程2 进行持久化工作,该过程耗时比较长;
以上过程从1~5主进程停止了工作;
该设计使得耗费时间成的持久化工作在其他进程执行;

不用那么麻烦啊,你就mmap一个private的地址,然后需要保存的时候直接fork个子进程去做就可以了,不需要同步,OS会做COW的。

@xixi

对于 C10K 系统,并发的 10K 次页面脏,对系统就是一个影响。

一般的程序设计,每个链接上的 buffer 都一定是存在与独立内存页上的。

“影响”指的性能影响,而不是正确性的影响。

主进程处理大量网络 IO ,这些数据都有对应的 buffer 。对于 C10K 系统,每个链接上只要有一个字节数据的变更,就轻易影响到至少 10K 个内存页,甚至更多。

COW的时候,只会对自己所持有的内存做一次copy,然后再修改,这个动作不可能影响到其他内存页上的。

我来学习了

公开信息自然会有人回的,网上也很容易搜到。

我不想好为人师,大家都是同学。

如果专门写 email ,通常我会回的,但无论怎样,我都认为学习并无捷径。往往慢即使快,肯花时间,无功利心的去学就好了。

这方面的文章我写过好几次了。

云风为什么很少回答一些新手提出的关于如何学习的问题?

redis不是干这事么,可以参考一下。

骗人,HTML 标签根本不能用

就是说一共有三个进程:
<ol>
<li>主进程
<li>只持有lua数据的子进程
<li>序列化时的临时进程
</ol>
对吗?

不能象debugger那样attach到该进程读取内存么?

@xiao_ming
从实践中学习语言、算法、汇编、操作系统
你打算把gcc手册从头翻到尾来学gcc么
最最注意的学习方式是死的 人是活的 2012还没来

@felix021

举个实际例子: " fork 后,大量不必要的内存页复制"

主进程处理大量网络 IO ,这些数据都有对应的 buffer 。对于 C10K 系统,每个链接上只要有一个字节数据的变更,就轻易影响到至少 10K 个内存页,甚至更多。

对于子进程,对这些 buffer 的改动造成的内存页复制就是不必要的。

当内存紧缺时,额外多占用的内存会导致向磁盘交换。

ps. 任何一种方案都是分析过需求后做出的。当然,证明一个需求并不存在,是解决问题的途径之一。只不过,讨论解决特定需求的方法和论证需求是否存在是两个问题 :)

云风大哥,您好。
我是一个刚学习一年编程的新手。我非常非常喜欢编程,也喜欢把几乎所有时间花在上面。我希望能得到您学习方式上的指点。
对于我这种没有专业背景、起步较晚的新手,是应该先打好基础、系统学习语言、算法、汇编、操作系统等课程,还是应该完全从实践中学习?
在愿意付出努力、也能充分体会快乐的情况下,需要最最注意的学习方式是什么?
我拜读过您的文章和书,获益匪浅,也看到过您对学习的一些见解并仔细体会。如果能得到您直接的回答,不胜荣幸。
谢谢!

os的cow不会用掉你多少内存的,除非你有大量数据变动。kiss。。。。

" fork 后,大量不必要的内存页复制"是不存在的,正如楼上所言,OS对内存页的实现都是Copy On Write的,所以fork的实现只是把页表copy了一份,并设置为只读,在需要写入的时候才产生页错误,然后由OS进行LAZY Copy&Write

地址相同,把 lua state 指针直接指过去即可。但不可以调用其中的 C function 。

如果需要调用 C function , 需要在主进程初始化好 lua state , 或静态链接 lua 用到的 C 库。

没有lua state里的那些信息索引及信息,子进程自身能识别这些数据并完成序列化吗?

云风,你好。我最近刚好开始着手一个新项目,也遇到了与你类似的需求。我的情况是把一些服务器的公用数据,如工会之类的每5分钟保存一次。之前开发的项目除了人物以外,这些公用数据都是很久或者服务器关闭才保存的,所以一旦服务器崩溃了,回档就会很大。由于数据量比较大,而且比较频繁,所以我认为如果LOCK下来保存会导致服务器卡;通过网络投递到数据库服务器保存,又觉得太多。所以我还是使用了内存映射,并拷贝了一份数据出来,在另一个线程中“慢慢”存储,以达到不影响主线程的运行。这样的处理不知道您有什么看法,会不会有什么不妥

os 是对内存页 copy on write 的。

不大明白,在做快照的过程中主进程不还是要停住么?怎么不是stop the world?

Post a comment

非这个主题相关的留言请到:留言本