为什么使用共享库时,Linux上的应用启动速度较慢?

在我正在开发的embedded式设备上,启动时间是一个重要的问题。 整个应用程序由多个使用一组库的可执行文件组成。 由于FLASH存储空间有限,我们希望使用共享库

编译和链接共享库时,应用程序照常工作,并且闪存的数量按预期减less。 与链接到静态库的版本的区别在于应用程序的启动时间长了大约20秒,我不知道为什么。

该应用程序运行在Linux 2.6.17 OS,16 MB FLASH(JFFS文件系统)和32 MB RAM的180 MHz ARM9 CPU上。

Bacause共享库必须在运行时链接,通常通过dlopen()或类似的东西。 静态库没有这样的步骤。

编辑:一些更多的细节。 dlopen必须执行以下任务。

  • 找到共享库
  • 将其加载到内存中
  • 递归加载所有依赖关系(及其依赖关系….)
  • 解决所有符号

这需要相当多的IO操作来完成。

在一个静态链接的程序中,上述所有内容都是在编译时完成的,而不是运行时完成的。 因此加载一个静态链接的程序要快得多。

在你的情况下,差异是由你的代码运行相对较慢的硬件夸大。

这是速度和空间的经典权衡的一个很好的例子。

您可以静态链接所有的可执行文件,以便它们更快,但是它们将占用更多的空间

要么

您可以拥有占用更少空间的共享库,但也有更多时间加载。

所以决定你想要牺牲什么。

这种差异有很多因素(操作系统,编译器等),但是可以在这里找到一个好的列表。 基本上共享库是由于空间原因而创建的,大部分涉及到的“魔法”使它们工作时性能受到影响。

(作为历史记录,Linux / Unix上的原始Netscape导航器是一个静态链接的大型可执行文件)。

这可能会帮助其他类似的问题:

在我的情况下启动需要这么长时间的原因是,GCC的默认设置是导出库中的所有符号。 一个很大的改进就是设置一个编译器设置“-fvisibility = hidden”。

lib必须导出的所有符号都必须用语句扩充

__attribute__ ((visibility("default")))

见gcc wiki
和非常好的文章如何写共享库

好吧,我现在已经知道,共享库的使用有其速度的缺点。 我发现这篇文章关于动态链接和加载启发。 加载过程似乎比我预期的要长得多。

有趣的..通常加载一个共享库的时间是从一个胖的应用程序静态链接是不明显的。 所以我只能猜测,系统要么从闪存中加载库非常慢,要么加载的库以某种方式被检查(例如,.NET应用程序为所有加载的DLL运行校验和,大大减少了启动时间一些案例)。 这可能是共享库正在按需加载,然后卸载,这可能表明配置问题。

所以,对不起,我不禁要说为什么,但是我认为它是ARM设备/操作系统的一个问题。 你有没有尝试启动代码,或静态链接1最常用的库,看看是否有很大的不同。 同时将共享库放在与应用程序相同的目录下,以减少搜索FS所需的时间。

对我而言,一个显而易见的选择是将这几个程序静态链接到一个二进制文件中。 这样你就可以继续共享尽可能多的代码(可能比以前更多),但是你也可以避免动态链接器的开销,并且保存动态链接器在系统上的空间。

把几个可执行文件合并到一个文件中是非常简单的,通常你只需要检查一下argv,然后根据这个文件来决定调用哪个例程。