文章目录
- 动静态库的区别
- 动态库-共享库
- 动态库的加载
- 动态库的管理
- 总结
动静态库的区别
-
动态库(Dynamic Libraries)
-
链接方式
动态链接,程序在运行时(而不是在编译时)与动态库链接;
操作系统负责加载动态库文件;
-
文件大小
使用动态库的应用程序通常其可执行文件大小更小;
因为库代码在运行时将会从单独的库文件进行加载;
-
内存使用
如果多个程序同时使用一个动态库,则该库在内存中只需要加载一次,节省系统资源;
-
更新和发布
更新动态库较为方便;
只需替换动态库文件本身即可而不需要重新编译使用它的程序;
使得动态库适用于需要频繁更新或者维护的软件组件;
-
-
静态库(Static Libraries)
-
链接方式
静态链接,程序在编译时与静态库进行链接,静态库的代码数据被复制到最终的可执行文件中;
-
文件大小
使用静态库的应用程序通常会有更大的可执行文件,因为其静态库的代码和数据将被集中到应用程序中;
-
内存使用
每个使用静态库的应用程序都会有自己的库代码副本;
在内存中可能会导致重复冗余的内容;
-
更新和发布
更新你需要使用到静态库的组件时必须重新编译整个程序;
这意味着静态库适合不经常更新的库,或者用户希望应用程序是完全独立且不依赖外部其他库文件时;
-
动态库-共享库
与静态库不同,动态库在进程运行时要被加载;
一般情况下常见的动态库会被所有的可执行程序动态链接,故动态库又被称为共享库;
这意味着动态库在系统中加载后将会被所有的进程共享;
磁盘中的文件需要被管理,动态库是文件也需要被管理;
当一个可执行程序需要加载这个动态库时将会以特定的路径将动态库加载至内存;
若是一个可执行程序依赖于多个动态库时将会把这些依赖的动态库同样加载至内存当中;
这些被加载至内存的动态库将以 先描述再组织 的方式被操作系统进行管理,而若是有其他进程需要调用这些动态库时只需要间接在操作系统的管理下找到这个动态库并与其他进程进行共用即可;
动态库的共享属性主要围绕以下几点:
-
资源共享
动态库允许其代码在物理内存中至有一份拷贝而可以被多个正在运行的进程所共享;
这种方式与静态库形成对比,静态库的代码被复制到每个使用它的可执行文件当中,导致相同的代码在内存中形成冗余;
动态库通过减少重复的代码加载有效减少了系统的内存占用从而提高内存使用效率;
-
动态链接
程序在运行时加载动态库被称为动态链接,类比
C++
中的多态;这意味可执行程序将在运行时寻找并链接其依赖的动态库;
它们可以在运行时共享这个库的同一个实例而不需要在每个程序中进行加载;
-
跨进程共享
操作系统负责管理动态库的链接和加载;
当第一个程序请求加载特定的动态库时操作系统将会把这个动态库加载进内存并对其管理;
此后如果有其他进程也依赖这个动态库时将直接在操作系统的管理下找到这个库并进行链接;
-
减少磁盘和内存使用
动态库通过其共享属性减少系统中重复的代码存储和加载从而介绍磁盘空间和运行时的内存占用;
动态库的加载
-
可执行程序的执行
用户请求执行一个可执行文件;
当操作系统接收到执行请求时将会从文件系统重读取指定的可执行文件;
这个文件包含了程序的代码数据以及其动态库的依赖信息;
-
进程的创建
-
创建进程控制块
task_struct
当程序被执行时其将加载进内存并成为进程;
操作系统将会为新的进程分配一个
task_struct
结构体用于存储进程的所有信息;包括进程状态,
PID
,进程地址空间指针,页表指针等等; -
分配进程地址空间
操作系统将会为进程分配一个虚拟地址空间并根据可执行文件的内容初始化代码段和数据段;
-
-
动态库的加载
加载器解析可执行文件中的动态库依赖信息,并根据依赖信息在磁盘上查找对应的动态库文件;
这个过程可能会参考
LD_LIBRARY_PATH
环境变量,/etc/ld.so.cache
文件以及标准路径如/lib64/
或/usr/lib
;找到动态库文件后,加载器将会把动态库的内容加载进物理内存;
如果涉及多个进程依赖同一个动态库,操作系统将会让这些进程共享内存中的同一份动态库以节省内存;
具体的方法是在当前被管理的动态库中寻找新进程所依赖的动态库是否已经在当前物理内存中留有副本,若是有则直接进行链接从而减少
I/O
次数;当动态库被加载进内存时同样需要被页表进行映射以保证其安全性;
一般情况下动态库将会被映射至每个依赖该动态库的进程地址空间中用户空间的共享区当中;
若是某个进程需要对其中的内容进行写入操作时将发生写时拷贝,将会为这个进程单独拷贝一份私有的内容为这个进程服务;
-
进程的运行
加载器对动态库进行重定位处理,解析程序对动态库中符号的引用以确保这些引用指向正确的内存地址;
当所有依赖的动态库被加载完成后,操作系统将开始执行进程的代码,进程也将进入运行状态;
-
进程的管理
操作系统将通过
task_struct
管理进程的执行,根据调度策略在不同进程间切换以确保系统资源的合理分配使用;
动态库的管理
操作系统通过间接的方式了解当前系统中所有已经被加载的库与未被加载的库;
动态库的管理同样是采用 “先描述后组织” 的方式进行;
在操作系统中存在一个结构体为link_map
;
这个结构体用来表示每个加载的动态库(共享库);
其结构体可能包含库的内存地址,库的名称,指向库的动态段的指针等;
struct link_map {/* 库的加载地址 */ElfW(Addr) l_addr;/* 库的路径名 */char* l_name;/* 指向库的动态段(.dynamic节)的指针 */ElfW(Dyn)* l_ld;/* 链表中的下一个和上一个link_map结构的指针 */struct link_map *l_next, *l_prev;
};
总结
动态库和静态库是软件开发中两种主要的代码库链接方式;
其中动态库在程序运行时被加载并允许代码在物理内存中只有一份拷贝而被多个进程共享从而减少系统资源的消耗;
相比之下,静态库在程序编译时被整合进可执行文件;
导致每个程序都包含了一份库的副本,增加了程序的大小和内存占用;
动态库的管理依赖于操作系统中的结构体,如link_map
;
它记录了库的加载地址,名称等信息,确保动态链接和库间依赖关系的正确处理;
通过高效地管理和加载动态库,系统能够提高内存使用效率,简化库的更新过程,并支持跨进程的库共享;