摘自深度探索C++对象模型第七章:
7.4 效率有了,弹性呢?
传统的C++对象模型提供有效率的执行期支持,这份效率再加上与C之间的兼容性,造成了C++的广泛被接受度。然而在某些领域,像是动态共享函数库(dynamically shared libraries),共享内存(shared memory),以及分布式对象(distributed object)方面,这个对象模型的弹性还是不够。
动态共享函数库 (Dynamic Shared Libraries)理想中,一个动态链接的shared library应该像"突然造访"一样。也就是说,当应用程序下一次执行时,会透明化地取用新的library版本。新的版本不应该对旧的应用程序产生侵略性,应用程序也不应该需要为此重新建造一次。但是,在目前的C++对象模型中如果新版的library中的 class object布局有所变更,上述的"library无侵略性"的说法就有待商榷了。这是 因为 class 的大小以及其每一个直接(或继承而来)的members的偏移量(offset)都在编译时期就已经固定(虚拟继承的members除外)。这虽然带来效率,却在二进制层面影响了弹性:如果object布局改变,应用程序就必须重新编译。
共享内存 (Shared Memory) 概念:共享内存(Shared Memory)就是允许多个进程访问同一个内存空间,是在多个进程之间共享和传递数据最高效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的地址空间中,如果某个进程修改了共享内存中的数据,其它的进程读到的数据也将会改变。
问题:当一个shared library被加载,它在内存中的位置由runtime linker决定,一般而言与执行中的进程(process)无关。然而在C++对象模型中,当一个动态的shared library支持一个 class object,其中含有 virtual functions(被放在shared memory中),上述说法便不正确。
问题并不在于"将该object放置于shared memory中"的那个进程,而在于"想要经由这个shared object附着并调用一个virtual function"的第二个或更后继的进程。除非 dynamic shared library被放置于完全相同的内存位置上,就像当初加载这个shared object的进程一样。否则 virtual function会死的很难看,可能的错误包括segment fault或bus error。原因在于每一个 virtual function在 virtual table中的位置已经被固定了。
总结:如果程序使用到动态库里的一个对象,而该对象有一个虚表指针,由于每个进程所对应的动态库地址是不一样的,会导致两个进程使用同一个共享内存,同一个对象,相同的虚表指针,但是指向的地址,内容,却大大的不太,导致问题。
解决:目前的解决办法是属于程序层面,程序员必须保证让跨越进程的shared libraries有相同的坐落地址(在SGI中,使用者可以根据所谓的so-location文件,指定每一个shared library的精确位置)。至于编译系统层面上的解决办法,势必要牺牲原本的 virtual table实现模型所带来的高效率。



