Когда вы разветвляете процесс, его память копируется с сохранением тех же адресов виртуальной памяти.
Вы просто помещаете указатель в раздел разделяемой памяти, поэтому ваш план памяти перед разветвлением будет:
+--------------------+ +--------------------+
| Shared memory | | Parent heap |
| | | |
| | | |
| Virtual address | | +---------+ |
| of --------------> | Hash | |
| | | +---------+ |
| | | |
+--------------------+ +--------------------+
После разветвления указатель ссылается на личную память каждого процесса соответственно:
+--------------------+ +--------------------+
| 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}'"
end
Parent 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
и т. Д., Но вам необходимо копировать ее в общую память и из нее каждый раз, когда вы хотите передать ее или преобразовать обратносоответственно, и вы должны знать свою (максимальную) длину строки заранее.