Ruby 好书推荐:pragmatic.working.with.unix.processing

blackanger · 2012年08月04日 · 最后由 blackanger 回复于 2013年08月20日 · 3795 次阅读

分享第 13 章部分读书笔记,讲的特别条理,细致。


Chapter 13 Processes are Friendly

Being CoW Friendly

在 fork 那个章节,我们说,fork(2) 创建一个新的进程,会复制主进程的一切。物理复制所有数据是一笔很大的开销,所以,现代 Unix 系统采用了一个叫 Cow(copy-on-write 写时复制技术)的东西来处理。顾名思义,Cow 只在需要写入的时候才开始复制数据。所以一个主进程和一个子进程实际上是共享内存里相同的物理数据,直到有一个进程需要修改某些数据,这个时候才复制。

arr = [1,2,3] fork do # At this point the child process has been initialized. # 这时候 child process 已经被初始化了 # Using CoW this process doesn't need to copy the arr variable, # 使用 CoW 这个进程不需要被复制 arr 变量 # since it hasn't modified any shared values it can continue reading # 因为它还没有被修改任何的共享值。 # from the same memory location as the parent process. # 它还可以从主进程的相同内存中读数据 p arr end

arr = [1,2,3] fork do # At this point the child process has been initialized. # Because of CoW the arr variable hasn't been copied yet. # 因为 CoW 还没有复制 arr 这个变量 arr << 4 # The above line of code modifies the array, so a copy of # the array will need to be made for this process before # it can modify it. The array in the parent process remains # unchanged. puts arr # 上面的代码已经修改了那个数组,所以这个子进程会在修改之前 copy 一个数组的副本。 # 而在主进程中的数组依然不会被改变。 end puts arr

输出
[1, 2, 3, 4] => 1188 [1,2,3]

可以看出,arr 并未被改变。

CoW 是牛逼的,不幸的是它不支持 MRI 或 Rubinius.

为了让 CoW 正常的工作,需要按照 CoW 的方式管理内存,可惜 MRI 和 Rubinius 并不是这样做的。

为什么不呢?

MRI 的垃圾回收器(GC)使用了标记和扫描算法。这意味着,GC 调用的时候,必须遍历所有已知的对象和写入,看它们是否应该被收集。这就导致了,每次 GC 运行的时候,内存中的每个对象都会被写入。因此,在 fork 之后,GC 运行一次,就打乱了 CoW 的机制。

这也是 REE 被创建的主要原因之一。可以去 google 一下 REE 是如何解决这个问题的。

(译注:不幸的是,Ruby1.9 仍然不是 Cow Friendly 的,不过有传言说 Ruby2.0 会是 CoW Friendly 的,期望如此)

ruby 好像发展的很慢啊,github 上好几天才会有人提交一下

Pragmatic 的书都不错,这本可以去看看,130 页

#1 楼 @kikyous

我觉得你不适合用 Ruby, 去玩 Java 吧。估计 C 也不适合你。

楼主解释的是够清楚!赞一个。

你好像没提及,Windows 下也用不了。

master (2.0) 的 GC 已经 cow friendly 了,用 unicorn 终于真的可以省内存了

#3 楼 @zw963 你是摆摊子看相的大仙?失敬失敬

确实是好书,感谢汉东

@blackanger 是啊好久不见了,有空出来聚聚

@linjian815 好啊,有空把哥几个都叫上。忙完这段时间我来组织。

#11 楼 @blackanger 你是张汉东? 我晕啊,混了 RubyChina 这么久,没有吧你的 id 和真人对上号,话说前几年突然没你的消息了,然后江湖上传言你去开餐厅写小说了......

@lgn21st 哈哈,几年前是开过餐厅。小说没写完呢。

好书,感谢

看完了,不错

@blackanger,请教一个问题

a = [1, 3, 4]
fork do
  a << 5
  puts "in fork object_id: #{a.object_id}"
  puts "in fork a: #{a}"
end
Process.wait
puts "object_id: #{a.object_id}"
puts "a: #{a}"

运行结果: in fork object_id: 12721400 in fork a: [1, 3, 4, 5] object_id: 12721400 a: [1, 3, 4]

问题如下:

fork 中,根据 Cow,当 a 改变的时候,会 copy 一份,

那么

为什么两次的 object_id 还是一样的?

#16 楼 @meeasyhappy 这个应该和 Ruby 没有关系,你去了解一下关于进程 fork 的知识就明白了。进程被 fork 之后,整个进程就相当于一个完全 copy,当然包括里面内存空间的对象了,相当于一个平行空间,都有相同的 object_id, 但实际上他们不在同一片内存中。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号