我正在实现一个为共享内存提供数据结构的多进程库。但我现在遇到了麻烦,我修改了子进程中的共享Hash对象,但父进程仍然没有读取更改后的值。
示例代码:https://play.crystal-lang.org/#/r/6n34
用同样的指针修改,为什么不生效?
发布于 2019-04-04 17:19:10
当您派生一个进程时,它的内存将被复制,同时保留相同的虚拟内存地址。
您只需将一个指针放入您的共享内存区,那么fork之前的内存布局是:
+--------------------+ +--------------------+
| Shared memory | | Parent heap |
| | | |
| | | |
| Virtual address | | +---------+ |
| of --------------> | Hash | |
| | | +---------+ |
| | | |
+--------------------+ +--------------------+在fork之后,指针分别指向每个进程的私有内存:
+--------------------+ +--------------------+
| Shared memory | | Parent heap |
| | | |
| | | |
| Virtual address | | +---------+ |
| of --------------> | Hash | |
| | | | +---------+ |
| | | | |
+--------------------+ +--------------------+
|
|
| +--------------------+
| | Child heap |
| | |
| | |
| | +---------+ |
+--------> | Hash | |
| +---------+ |
| |
+--------------------+因此,当您取消引用子堆中的指针时,您只接触到了子堆中的对象。
相反,您需要做的是将所有实际数据放入共享内存。对于标准的Crystal数据类型来说,这很难做到,因为它们依赖于能够请求新的内存并由垃圾收集器进行管理。因此,您需要实现一个可以在共享内存上工作的GC。
然而,如果你只有一个固定数量的数据,比如说几个数字或一个固定大小的字符串,你可以利用Crystal的值类型来使事情变得更好一些:
module SharedMemory
def self.create(type : T.class, size : Int32) forall T
protection = LibC::PROT_READ | LibC::PROT_WRITE
visibility = LibC::MAP_ANONYMOUS | LibC::MAP_SHARED
ptr = LibC.mmap(nil, size * sizeof(T), protection, visibility, 0, 0).as(T*)
Slice(T).new(ptr, size)
end
end
record Data, point : Int32 do
setter point
end
shared_data = SharedMemory.create(Data, 1)
shared_data[0] = Data.new 23
child = Process.fork
if child
puts "Parent read: '#{shared_data[0].point}'"
child.wait
puts "Parent read: '#{shared_data[0].point}'"
else
puts "Child read: '#{shared_data[0].point}'"
# Slice#[] returns the object rather than a pointer to it,
# so given Data is a value type, it's copied to the local
# stack and the update wouldn't be visible in the shared memory section.
# Therefore we need to get the pointer using Slice#to_unsafe
# and dereference it manually with Pointer#value
shared_data.to_unsafe.value.point = 42
puts "Child changed to: '#{shared_data[0].point}'"
endParent read: '23'
Child read: '23'
Child changed to: '42'
Parent read: '42'https://play.crystal-lang.org/#/r/6nfn
这里需要注意的是,您不能将任何Reference类型(如String或Hash )放入结构中,因为这些类型只是指向每个进程的私有地址空间的指针。Crystal提供了类型和API来使字符串共享变得更容易,例如Slice和String#to_slice等,但每次你想传递它或将其转换回来时,你都必须将它复制到共享内存或从共享内存中复制它,并且你必须提前知道你的(最大)字符串长度。
https://stackoverflow.com/questions/55503944
复制相似问题