本文作为 Linux 基础 IO 系列的收官篇,聚焦三大核心模块:静态库与动态库的构建命令、链接方式、底层差异(如静态拷贝 vs 动态共享、加载机制),深入剖析进程地址空间的本质,解释程序加载后虚拟地址与物理内存的映射关系,并补充内存管理(页框、slab 分配器)、IO 数据拷贝流程、函数参数求值顺序等关键细节。
动态库和静态库
库的安装通常指将库文件放置到系统默认的库路径中。库文件中不能包含 main 函数,因为库是被别人调用使用的。
静态库
静态库是将源代码编译后打包成 libxxx.a 文件(命名需遵循 lib.xxx.a 格式)。静态库给单个程序一份副本,互不干扰,因此删除源静态库不影响已生成的可执行程序。
生成静态库命令:
ar -rc libmymath.a add.o sub.o
-rc 表示存在就替换,不存在就创建。
链接命令示例:
gcc main.c -I ./lib/include/ -L ./lib/mymathlib/ -lmymath
说明:
-I:指定头文件的搜索目录。-L:指定库文件的搜索目录。-l:指定要链接的库的名称(去掉 lib 和 .a 后缀)。gcc默认调用动态库,除非只有静态库或指定-static。
动态库(共享库)
动态库允许同时被多个程序使用。库名格式为 libxxx.so(lib 和 .so 必须保留)。
生成动态库后,若要让链接后的可执行程序能加载它,需要确保操作系统能找到该库。编译器知道位置但加载器不知道。
查看依赖的动态链接库列表:
ldd 文件名
fPIC
fPIC(Position Independent Code)即位置无关码。动态库被加载到内存地址空间的位置是不固定的,因此需要使用偏移量对库中函数进行编址,而不是绝对编址。
让动态库能被加载的方法
- 拷贝到系统默认库路径(如
/lib64或/usr/lib64)。 - 在系统默认库路径下建立软链接。
- 将库所在路径添加到环境变量
LD_LIBRARY_PATH。 - 在
/etc/ld.so.conf.d中建立配置文件,然后执行ldconfig。
问题:为何动态库需要被加载,而静态库不用? 动态库是共享的,只有一份存在于物理内存中,程序通过共享区对应的页表访问,所以需要被加载。静态库在链接时已被完整拷贝到可执行文件内部。
进程地址空间的深入了解
程序未加载到内存前,内部采用平坦模式(0-4GB),地址从 0 开始,称为虚拟地址(或逻辑地址)。
程序加载后:
- CPU 获取入口虚拟地址。
- 发生缺页中断,将对应数据加载到内存。
- 更新页表,建立虚拟地址与物理地址的映射。
CPU 读取指令时,若涉及虚拟地址,会交给 MMU 通过页表查询。
几点补充
物理内存和磁盘交换数据以 4KB 为单位。物理内存中的单位叫页框,磁盘中叫页帧。
以 4KB 为单位的好处:
- 减少 IO 次数(硬件层面)。
- 利用局部性原理实现预加载(软件层面)。
若存储数据远小于 4KB,可使用 slab 分配器细分。
局部性原理:访问数据时,周围数据后续被用到的概率极高。
操作系统管理内存:
- 每个页框信息放入
struct page结构体。


