快捷搜索:   服务器  安全  linux 安全  MYSQL  dedecms

在 Linux 中使用共享对象(2)


       }

    第 6-35 行
    客户机进程获得指向三个共享对象的指针,建立对它们的数据成员的三个引用,并且 —— 依赖于命令行的输入 —— 调用三个虚函数。

    第 36-39 行
    没有被调用的 pthread_create() 函数用来强制链接到另一个共享库。来自所有共享库的任何方法都可以满足这一目的。

    共享库和客户机可执行文件的两个实例的编译方法如下:

    gcc shared g shm_server.cpp o libshm_server.so lstdc++
    gcc -g shm_client.cpp -o shm_client1 -lpthread -lshm_server -L .
    gcc -g shm_client.cpp -o shm_client2 -lshm_server -L . lpthread

    注意,交换了 shm_client1 和 shm_client2 中 shm_server 和 pthread 的链接顺序,以确保 shm_server 共享库在两个可执行文件中的基址不同。可以使用 ldd 命令进一步验证这一点。示例输出通常如下所示:
    清单 4. shm_client1 的库映射

    ldd shm_client1

    libpthread.so.0 => (0x4002d000)
    libshm_server.so => (0x40042000)
    libc.so.6 => (0x4005b000)
    ld-linux.so.2 => (0x40000000)
    清单 5. shm_client2 的库映射
    <ccid_nobr>
    <table width="400" border="1" cellspacing="0" cellpadding="2"
     bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center">
    <tr>
        <td bgcolor="e6e6e6" class="code" style="font-size:9pt">
        <pre><ccid_code> ldd shm_client2

    libshm_server.so => (0x40018000)
    libpthread.so.0 => (0x40046000)
    libc.so.6 => (0x4005b000)
    ld-linux.so.2 => (0x40000000)

   这里的主要目的是使构建的两个客户机二进制文件具有不同的服务器库基址。在这个示例程序的上下文中,使用不被调用的 pthread_create() 函数和不同的共享库链接顺序来达到这一目标。不过,没有具体规则或统一步骤可以作用于所有链接;需要根据不同的情况采取不同的方法。

    例 1:shm_client1 与 shm_client1
    在下面的输出中,首先在 shell 中调用 shm_client1。由于现在没有共享对象,于是 shm_client1 创建了它们,引用它们的数据成员,调用它们的虚函数,然后退出 —— 将对象留在了内存中。第二次,进程只是引用数据成员和虚函数。

    清单 6. shm_client1 与 shm_client1 的输出日志

    $ ./shm_client1

       Created the shared memory
       1073844224 1073845248 1073846272
       1
       Object type: A
       2
       Object type: B
       3
       Object type: C


       $ ipcs

       ------ Shared Memory Segments --------

       key shmid owner perms bytes nattch status
       0x000004d2 2260997 sachin 666 3072 0


         $ ./shm_client1
         1073840128 1073841152 1073842176
         1
         Object type: A
         2
         Object type: B
    ->   0
    ->   Segmentation fault (core dumped)

    当第二个进程试图通过类型 C * 的指针去引用数据成员 A::m_nA 时(您会记得 C 虚拟继承自 A),共享对象内的基子对象(base sub-object)指针会被读取。共享对象是在现在不存在的进程的上下文中构造的。因此,读取 A::m_nA 和 C::WhoAmI() 时读入的是内存垃圾。

    因为 Vee-Table 和虚函数位于 shm_server 共享库内部,恰巧在同一虚拟地址被重新加载,所以,再次引用类型 A * 和 B * 的指针时不会观察到任何问题。

    因此,GNU 所采用的 C++ 对象模型没有成功地处理虚拟继承。

    例 2:shm_client1 与 shm_client2
    在下一个示例输出中,在命令行中首先执行 shm_client1,然后执行 shm_client2:
    清单 7. shm_client1 与 shm_client2 的输出日志

    $ ./shm_client1

       Created the shared memory
       1073844224 1073845248 1073846272
       1
       Object type: A
       2
       Object type: B
       3
       Object type: C

       $ ipcs

       ------ Shared Memory Segments --------
       key shmid owner perms bytes nattch status
       0x000004d2 2359301 sachin 666 3072 0

       $ ./shm_client2
       1073942528 1073943552 1073944576
       1
    -> Segmentation fault (core dumped)

       $ ./shm_client2 3
       1073942528 1073943552 1073944576
       2
    -> Segmentation fault (core dumped)

       $ ./shm_client2 5
       1073942528 1073943552 1073944576
    -> 1048594
    -> Segmentation fault (core dumped)

    然而,Vee-Table 位于 shm_server 共享库内部:在 shm_client1 和 shm_client2 中它被加载到不同的虚地址。这样,读取 A::WhoAmI() 和 B::WhoAmI() 时读入的都是内存垃圾。

    运用共享内存

    在共享内存中具体使用 C++ 对象时您应该考虑两个主要问题。首先,Vee-Table 指针用于访问虚函数,而对数据成员的访问直接使用编译时偏移量实现。因此,对于所有这种共享对象来说,所有进程中的 Vee-Table 和虚函数都应该具有相同的虚地址。关于这一点,没有一成不变的规则,不过,为相关的共享库采用适当的链接顺序大部分时候都会管用。

    另外,永远不要忘记,虚拟继承的对象有指向基对象的基指针。基指针引用进程的数据段,而且永远是特定于进程的。难以确保所有的客户机进程中都有相同的数字值。因此,假如使用 C++ 对象模型,要避免在共享内存中构造虚拟继承的对象。但是,也不要忘记,不同的编译器采用的是不同的对象模型。例如,Microsoft Compiler 使用进程无关的偏移量来为虚拟继承类指定基对象,因而不存在这个问题。重要的是,确保所有客户机进程中的共享对象的地址相同。

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论