Shared Memory Tests
As part of the effort to
optimize LHC physics software applications to run on multi-core cpus, this page shows some test examples that clarifies the usage of shared memory within C++. In order to reproduce the examples it is needed the
Boost.Interprocess library version 1.35 and
GDB version 6.8.
Download sources from
here.
Test 1: Two Programs Accessing the Same Shared Objects (read-only)
Description: Two shared objects are created in the
master.cc program using boost.interprocess library. The first object is an instance of a
POD class (named
PODClass). The second object is an instance of a class with virtual functions (
VirtualClass inherits from
ParentClass). The
slace.cc program tries to access to the shared objects, however when it tries to read the
VirtualClass instance it raises a segmentation fault. The
PODClass instance can be accessed without any problems.
Explanation: When the program
slave.cc tries to access the virtual method it crashes with a segmentation fault because the
virtual table is not stored in the same
virtual address for both programs. When the second program tries to resolve the virtual method address using a wrong virtual table address we get the segmentation fault.
Test 2: Two Forked Process Accessing the Same Shared Objects (read-only)
Description: Two shared objects are created before [[http://en.wikipedia.org/wiki/Fork_%28operating_system%29][forking]. The first is a POD object and the second has a virtual const method. Both process are created from a single program using
fork(). In that case the
PODClass and
VirtualClass instances can be accessed without any error. As in the
Test 1 we try to call a virtual method from the
CHILD process.
Explanation: In that case we do not have a segmentation fault crash while accessing the virtual method of the
VirtualClass instance. Why? This happens because the
copy-on-write fork implementation assures that the virtual address of the virtual table location remains unchanged. Therefore, the
CHILD process can resolves the address of the virtual table because the virtual addresses has not changed. The virtual table never changes at run-time. This implies that it is possible to use virtual methods in forked process if the classes (or libraries) has been loaded before the forking.
Test 3: Two Forked Process Accessing non-shared Objects
Description: Two non-shared objects are created before forking. The first is a POD and the second has a virtual method. After forking the
PARENT reads and then modifies the string value inside the objects. However, if the CHILD process isnot able to see the changes.
Explanation: This program does not show a segmentation fault. For read-only access
copy-on-write implementation of fork() assures that we share the objects (i.e. it's physical memory) as long as we do not modify them. It is a simple way to share read-only memory.
Test 4: Two Forked Process Accessing the Same Shared Objects (read-write)
Description: The
PARENT forked process creates two shared objects. The first is a POD object and the second has a virtual method. Both process are created from a single program using fork(). After creating the process in the
PARENT we modify its value. We can not see the modified string value in the
CHILD process. However, It does not crash.
Comment: In order to debug this test case it is needed [[][GDB 6.8]]. This version provides the functionality to
debug programs with multiple processes.
Explanation: In that case the virtual table is shared among process, therefore as we have seen in
Test 2 we can resolve the addresses of the virtual methods. The problem in that case is that we are not using the same physical memory to store the std::string. Why? Because the char* inside the std::string is allocated using the default allocator (heap space) and therefore this memory region is not shared at all. This situation is quite general due to the fact that inside C++ objects it is possible to allocate additional memory using the
new operator.
Conclusion
The following conclusions has been derived from the usage of a concrete shared memory library (i.e. Boost Interprocess). However, they are general and applies to all of them.
Using shared memory IT IS POSSIBLE to use classes that contains:
- Virtual methods provided that we create the process using fork() and the libraries are loaded before forking.
- POD without pointers. If pointers are needed an smart pointer should be used instead.
Using shard memory IT IS NOT POSSIBLE to use classes that contains:
- Static members will not be shared. The static members as the virtual table resides in the data segment. This implies that static data members can only be used for read only purposes.
- Pointers
- Virtual classes in different programs
- Virtual classes in shared libraries loaded after the fork
The previous points implies that the usage of shared memory can not be transparent to C++ developers.
References
U. Drepper,
What Every Programmer Should Know About Memory, 2007.
GDB Debugger Documentation
Downloads
multhreading20080528.zip: Test sources. Requisites: Boost 1.35, gcc 3.4 and gdb 6.8