谈静态动态库和显式隐式加载

#Technomous

静态库与动态库

从程序编译期的角度来看,我们可以将库分为静态库和动态库。静态库在链接时,编译器会将目标文件和静态文件组织成一个可执行文件。而动态库则是在程序运行时才会完成加载。

静态库的优势在于:

静态库的劣势:

动态库的优势在于:

动态的劣势:

我们需要根据具体的应用场景和需求来确定使用动态库还是静态库,在某些场景下,可以同时使用两者以发挥各自的优势。例如,一个应用程序可以使用动态库以减小可执行文件的大小,同时静态链接一些特定的库以提高性能。

显式与隐式加载

从程序运行时的角度来看,动态库的加载又可以分为隐式加载和显式加载。隐式加载又叫载入时加载,指在程序载入内存时搜索动态库,并将动态库载入内存。显式加载又叫运行时加载,指主程序在运行过程中需要动态库中的函数时再加载。隐式加载需要在编译时将头文件包含进源码中,然后需要在编译时告诉编译器库的搜索路径,这样编译器才能找到库中的符号(例如函数或变量)进行连接。例如,在使用 GCC 编译器时,使用 -L 来指定库的搜索路径。值得注意的是,动态在运行时也需要被找到。这通常是通过设置环境变量(LD_LIBRARY_PATH)或者将库放在系统已知的搜索路径(例如 /usr/lib)来实现的。如果运行时无法找到库,程序将无法启动。

隐式加载

隐式加载是动态链接器(例如 ld.sold-linux.so)在程序启动时负责搜索并加载共享库。这是一种自动的过程,程序本身无需显式指定加载共享库的代码,而是由动态链接器根据默认规则和环境变量自动完成。隐式加载中如果库文件位于非默认路径,可以通过以下方式指定搜索路径:

值得注意的是如果我们是采用修改运行时动态链接器的配置文件,修改完成后我们需要通过 sudo ldconfig 命令来更新动态链接器的缓存。

隐式加载的优势在于:

隐式加载的劣势:

显式加载

显式加载是程序本身通过调用动态链接器提供的函数(例如 dlopen、dlsysm)来主动加载共享库。这种方式允许程序在运行时动态选择加载哪些共享库,以及何时加载。程序可以根据需要进行加载和卸载,提供了更大的灵活性。Linux 下的显式加载方式可以参考Linux 动态库显式加载方法

显式加载的优势在于:

显式加载的劣势:

开发调试技巧

使用 LD_LIBRARY_PATH 设置库路径

使用 LD_LIBRARY_PATH 环境变量设置动态库的搜索路径。

export LD_LIBRARY_PATH=/path/to/library:$LD_LIBRARY_PATH

使用 ldd 命令查看动态库依赖

ldd 命令可以查看一个可执行文件或共享库依赖的动态库列表。

ldd your_program

查看动态库的符号信息

使用 nm 命令可以列出共享库中的符号信息,包含函数和变量。

nm -D your_library.so

使用 strace 跟踪系统调用

使用 strace 命令可以跟踪程序执行期间的系统调用,包括动态库加载。

strace ./your_program