文章目录
- 前言
- 动态库创建和使用
- 静态库创建和使用
- 动态库和静态库差异
- 生成过程的差异
- 运行效果的差异
- 补充说明
- 库的名称的注意事项
- 库的搜索路径的方法
- 什么是位置无关代码
- 动态链接与静态链接
- /usr/lib 和 /usr/local/lib
前言
动静态库的创建和使用部分更多的是意在说明动态库和静态库在Linux操作系统中的具体存在,在此基础之上才能解说原理,分析优缺点。
动态库创建和使用
- 文件结构概览
- 文件内容展示
main.cpp
#include "myMathA.hpp"
#include "myMathB.hpp"
int main()
{
int a = 10 ;
int b = 5 ;
printf("a=%d b=%d \n", a, b);
myMathA(a,b);
myMathB(a,b);
return 0 ;
}
myMathA.cpp :
#include "myMathA.hpp"
void myMathA(int x, int y)
{
printf("%d\n", x + y);
}
myMathA.hpp :
#pragma once
#include <stdio.h>
void myMathA(int x, int y) ;
myMathB.cpp :
#include"myMathB.hpp"
void myMathB(int x, int y)
{
printf("%d\n", x - y);
}
myMathB.hpp :
#pragma once
#include <stdio.h>
void myMathB(int x, int y) ;
makefile:
main:main.cpp
g++ main.cpp -o main -L./ -lexample # 使用动态库指令
.PHNOY:test
test:
# 生成动态库指令
gcc -c -fpic myMathA.cpp myMathB.cpp
gcc -shared myMathA.o myMathB.o -o libexample.so
.PHNOY:clean
clean:
rm -f libexample.so myMathA.o myMathB.o main`
- 生成动态库指令:
gcc -c -fpic myMathA.cpp myMathB.cpp
gcc -shared myMathA.o myMathB.o -o libexample.so
- 使用动态库指令 :
g++ main.cpp -o main -L./ -lexample
- 运行程序验证指令:
./main
- 运行结果: (linexample.so即为动态库)
静态库创建和使用
- 文件结构概览:
初始文件结构和内容与动态库创建和使用结构相同,内容除了生成动态库指令和使用动态库指令不同,其他完全相同。
- 生成静态库指令
gcc -c myMathA.cpp myMathB.cpp
ar -rc libexample.a myMathA.o myMathB.o
- 生成结果 : libexampel.a 即为静态库
- 使用动态库指令
g++ main.cpp -o main -L./ -lexample
- 运行结果: 一样
动态库和静态库差异
生成过程的差异
先说普通可执行文件生成过程,普通可执行文件的生成文件的顺序是:
.c文件 —> .i文件 —> .s文件 —> .o文件 —> 链接 —> .exe文件
动态库文件生成并使用的过程:
(声明了.c文件中函数的.h文件) + .c文件 —> .i文件 —> .s文件
—>(fpic指令生成的位置无关.o文件).o文件 —> (shared指令).so动态库文件 —>
(参与其他普通可执行的链接过程)—> .exe文件
静态库文件生成并使用的过程:
(声明了.c文件中函数的.h文件) + .c文件 —> .i文件 —> .s文件 —> .o文件
—> (ar指令生成).a静态库文件 ——> (参与其他普通可执行的链接过程) —> .exe文件
- 动态链接的.o文件须是位置无关的。
- 在 “参与其他普通可执行的链接过程” 这一行为上,静态库直接把库中的代码整个链接到可执行文件中去,而动态库则仅仅将一个动态库的函数入口地址表编入可执行文件中去。
运行效果的差异
-
由于静态库是将整个库函数代码链接入可执行程序内,所以在可执行程序生成后,即使删除静态库也不会影响静态库的使用。
-
由于动态库只将函数入口地址表编入可执行文件中,所以在可执行程序运行开始执行前要按照指定动态库的位置找到动态库,并将动态库中的函数的内容(机器码)加载到内存中(这个操作由操作系统完成) ,故不能删除动态库,也不能移动动态库在文件系统中的位置。
-
由于静态库是将整个库函数代码链接入可执行程序内,所以静态库生成的可执行文件在磁盘中占用的空间相较于动态库生成的可执行文件的占用空间要大。
-
在运行时,只有被实际使用的动态库的代码和数据才会被加载到内存中,因此相比静态库,动态库的内存消耗可能会更小。
-
也 “2” 中所述现象可知,动态在运行时是被加载在内存中的,此特性结合操作系统的虚拟内存机制可以实现,多个包含了同一个动态库的可执行程序可同时使用该动态库,且只消耗一份加载该动态库的物理内存。此情况下,整体来看,相较于静态库,动态库既节省了磁盘资源又节省了物理内存资源。
-
由于静态库的代码和数据完整地复制到可执行文件中,因此在运行时不需要进行动态加载,可以直接执行。这样可以减少动态链接的开销,因此相较于动态库,静态库的执行效率更高。
补充说明
库的名称的注意事项
- 库文件的名称必须以
lib
开头。 - 使用gcc指令链接库时需要去掉库的
lib开头
和文件后缀
。
库的搜索路径的方法
- 生成可执行文件时,用gcc -L选项指定搜索路径,如果指定了多个搜索路径,会依次搜索这些路径。
gcc main.c -L./ -lexample
- 通过环境变量LD_LIBRARY_PATH搜索
可以通过设置LD_LIBRARY_PATH环境变量来指定动态库的搜索路径。该环境变量可以设置为一个或多个路径,路径之间使用冒号进行分隔。例如:
export LD_LIBRARY_PATH=/path/to/library:/another/path/to/library
追加路径的指令
export LD_LIBRARY_PATH=export LD_LIBRARY_PATH:root/example/
root/example/
为追加的搜索路径
- 配置文件/etc/ld.so.conf和/etc/ld.so.conf.d/
可以编辑配置文件/etc/ld.so.conf,将需要搜索的动态库路径添加到其中。每行一个路径,可以使用文本编辑器打开并编辑。另外,还可以在/etc/ld.so.conf.d/目录下创建以.conf为后缀的文件,每个文件列出一个路径。编辑完成后,需要运行以下命令更新动态库缓存:
sudo ldconfig
- 搜索默认库路径
默认的库路径:系统会默认搜索一些常用的库路径,如/lib和/usr/lib。通常情况下,这些路径已经包含了大部分系统和常用的库文件。
动态库搜索路径的优先级是按照上述方法的顺序进行的。即先搜索LD_LIBRARY_PATH指定的路径,然后是配置文件中的路径,最后是默认的库路径。
什么是位置无关代码
位置无关代码(Position-Independent Code,PIC)是一种计算机程序代码的编译方式,它可以在内存中的任何位置执行,而不依赖于代码在内存中的具体位置。这种编译方式主要用于动态链接库(DLL)和可执行文件等需要在不同的内存地址空间中加载和执行的程序。
传统的代码编译方式会将代码中的绝对地址直接编码为指令,这样在加载到内存中时,需要进行重定位操作,将代码中的绝对地址替换为实际的内存地址。而位置无关代码则通过使用相对地址和间接寻址等技术,使得代码中的地址都是相对于某个基地址的偏移量,从而在加载时不需要进行重定位,可以直接在内存中执行。
位置无关代码的优点是可以在不同的内存地址空间中加载和执行,提高了代码的可移植性和共享性。它可以减少代码的重定位开销,简化了程序的加载过程,同时也增加了代码的安全性,防止恶意代码利用绝对地址进行攻击。
在现代操作系统和编译器中,位置无关代码已经成为一种常见的编译方式,广泛应用于动态链接库、可执行文件和操作系统内核等领域。
请注意,生成位置无关代码可能会导致一些性能损失,因为相对寻址和间接寻址可能比绝对寻址更慢。因此,在编译时需要权衡代码的可移植性和性能需求。
动态链接与静态链接
静态链接(Static Linking):
静态链接是将库文件的代码和数据复制到可执行文件中,使得可执行文件独立于外部的库文件。在静态链接中,编译器将程序所需的库文件的代码和数据直接嵌入到最终的可执行文件中。这意味着可执行文件包含了所有依赖的库函数和数据,因此它可以在没有外部库文件的情况下独立运行。静态链接的优点是可执行文件的移植性较好,但缺点是可执行文件的体积较大。
动态链接(Dynamic Linking):
动态链接是在程序运行时,通过动态链接器将程序与库文件进行连接。在动态链接中,可执行文件只包含对库函数的引用,而不包含实际的库函数代码和数据。当程序运行时,动态链接器会在系统中搜索并加载所需的库文件,并建立程序与库文件之间的链接关系。动态链接的优点是可执行文件的体积较小,多个程序可以共享同一个库文件,但缺点是程序在运行时需要依赖外部的库文件。
/usr/lib 和 /usr/local/lib
/usr/lib:这是系统级别的库路径,用于存放系统安装的共享库文件。这些库文件是由操作系统或软件包管理器安装的,供系统中的各种程序使用。通常,这些库文件是与操作系统版本和发行版相关的。
/usr/local/lib:这是本地级别的库路径,用于存放用户自己安装的共享库文件。当用户从源代码编译并安装软件时,通常会将其安装在/usr/local目录下。因此,相关的库文件也会被安装在/usr/local/lib路径下