This passage continues the previous post about low level microkernel interface provided by Mac OS X, and also all tests are on a late-2011 Mac Book Pro with a Mac OS X 10.7.4 system. This time is mainly about sharing memory.
For sharing memory, proper synchronization method should be applied. Here I chose the semaphore
provided by Mach (declared in <mach/semaphore.h>). The interface is rather straight forward:
semaphore_create, semaphore_signal, semaphore_wait. The only thing may need to explain is
the sync_policy parameter in semaphore_create, as far as I can see, the only one supported
is SYNC_POLICY_FIFO, which I think is OK for most synchronization and fast.
The first way is to use vm_inherit. This call sets the specified pages’ inheritance attribute:
NONE, COPY, SHARE. If it is set to NONE, the pages involved will not be inherited by the
child task. If it is set to SHARE, the pages will be shared by children. COPY is the default,
and is aligned with Unix fork. OpenBSD also has minherit syscall, which is the same in semantic
meaning and OS X also provides it for compability to do the same work as vm_inherit.
Another shared memory facility involves a memory_entry which I had not seen elsewhere before. They
are not mentioned in old Mach documents. In these documents, vm_inherit is the only way to share
memory without using a pager. Users can ask the pager to provide a ‘memory object’ and map (vm_map)
to the task vm space. However when I was consulting <mach/vm_map.h> I found the type used in vm_map
is a mem_entry_name_port_t. After several confirmation it may be a creation by Apple. For usage,
I found an example usage: the ‘commpage’ machanism used by OS X.
Also in the system, two set of the API co-exist. The one set is only prefixed with vm_, the other
set is prefixed with mach_vm_, mach_vm_ version is always 64bit(uint64_t),
vm_ version is 32bit(int in Mach-O i386) or 64bit(long in Mach-O x86_64). For this topic,
I have prepared a better sample code (compared to the code provided by previous post). You can
access it at this gist.
Firstly I prepare memory using mach_vm_allocate and then call
mach_make_memory_entry_64 to create a memory entry with allocated area and create a semaphore.
Next, name the memory entry and semaphore via bootstrap server (deprecated, for sample purpose).
Fork the child and use mach_vm_map to map the memory entry in the child process.
Now compare the memory inherited via fork (COW!) and the memory shared explictly.
Then I try to verify the writing. Finally I verify whether the parent process
can see the modification.
[UPDATE] Initializing of the address parameter is required, e.g., in the sample code:
mach_vm_address_t address = (mach_vm_address_t)main;
mach_vm_address_t map_address = (mach_vm_address_t)main;
The problem is that the VM space is much smaller than the 64-bit unsigned integer space
(about 50 bits in a 64-bit process). If a large integer is found in address, the kernel will
fail with no space since the kernel tries to find the first available address which is
larger or equal to address.
Sorry for long code :). Feel free to comment. Names or trademarks are owned by coresponding author(s)/orgnazition(s). Use of these names with best regards and wishes.
Yuuko PrZhu 26 August 2012