【问题标题】:How are global variables in shared libraries linked?共享库中的全局变量如何链接?
【发布时间】:2013-06-12 03:50:48
【问题描述】:

假设我与这个函数共享库,其中“i”是一些全局变量。

int foo() {
return i++;
}

当我从多个进程调用此函数时,每个进程中“i”的值独立于其他进程。

这种行为是意料之中的。

我只是想知道链接器通常如何实现这种行为?据我了解,代码在进程之间共享,因此变量必须在使用该库的每个程序的所有地址空间中具有相同的虚拟地址。这种情况对我来说似乎很难实现,所以我想我在这里遗漏了一些东西,而且它的做法有所不同。

我能否获得有关此主题的更多详细信息?

【问题讨论】:

  • 代码是共享的,而不是数据。动态链接器可能会为每个进程创建一个新的变量副本,但它不会文本(代码)段的副本。
  • @H2CO3 我很清楚这一点。但是,我要询问链接过程的详细信息。
  • 好吧,如果您需要更多详细信息,我认为您应该看看实际的实现。 Linux 中的动态链接器和 Darwin (BSD/OS X/iOS) 中的动态链接器都是开源的。

标签: c++ c linux dll


【解决方案1】:

代码在进程之间共享,因此变量必须具有 使用的每个程序的所有地址空间中的相同虚拟地址 这个库

代码并非按照您的想法共享。是的,动态共享对象只加载一次,但so 中的代码使用的内存引用或堆栈或堆不共享。仅共享包含代码的部分。

【讨论】:

    【解决方案2】:

    运行时的动态链接过程(与静态链接过程非常相似)为每个进程分配单独的数据(和 bss)段,并将它们映射到进程地址空间。只有文本段在进程之间共享。这样,每个进程都会获得自己的静态数据副本。

    【讨论】:

    • 是否有必要将数据段分配到每个地址空间中完全相同的位置?如果是这样,如果该位置已被其中一个空间中的某些其他数据使用,会发生什么情况?
    • 段被分配,映射到某处的地址空间(任何可用的),然后如果执行链接过程(重定位等和符号解析)。
    • 但是如果代码是共享的,如何在不影响其他使用相同代码的进程的情况下对其进行重定位呢?
    • 重定位取消共享代码。当加载器检测到需要重定位时,它会制作代码段的副本,然后在处理修复时写入该副本。
    • @HonzaBrabec 共享库代码被编译为与位置无关的代码,这意味着生成的代码只使用相对地址,而不是绝对地址来做事。必须由绝对地址寻址的全局符号由动态链接器(在运行时加载库时)修补到重定位地址。请记住,虚拟内存也是有效的——地址很可能是相同的,操作系统可以将物理内存映射到每个进程中的不同虚拟地址,反之亦然。
    【解决方案3】:

    每个进程都有自己唯一的地址空间,因此当一个进程访问变量时,它可以具有与另一个进程不同的值。如果进程应该共享相同的内存,他们将不得不专门设置它。共享库是不够的。

    【讨论】:

    • 我知道每个进程都有唯一的地址空间。但是,代码是共享的。并且代码是指内存中的某个地址(虚拟)。我的问题是如何处理这件事。
    • 但是即使它们指向相同的地址,内存页也是相对于它们的进程环境的。内存页由操作系统分配,因此链接器不必为此做任何特别的事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-14
    • 1970-01-01
    • 2014-05-17
    相关资源
    最近更新 更多