news 2026/4/24 7:08:13

Linux库制作与使用(一):静态库与动态库入门

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux库制作与使用(一):静态库与动态库入门

目录

一、什么是库

1. 简单认识库

2. 演示代码

二、目标文件

三、静态库

1. 静态库概念

2. 静态库生成

3. 静态库使用

四、动态库

1. 动态库概念

2. 动态库生成

3. 动态库使用

五、库搜索路径

1. 编译 / 运行时路径

2. 如何像系统库一样使用

3. 实际演示

七、外部库

1. apt 安装原理

2. ncurses 库使用示例

总结


一、什么是库

在之前的文章中,我们详细讲解了文件系统及内核相关数据结构。本文将视角转向应用层开发中不可或缺的核心内容:库(Library)


1. 简单认识库

设想每次开发新项目时,都需要从零开始编写 "屏幕字符输出"、"平方根计算" 或 "网络协议栈处理" 这些基础功能,开发效率将会变得极其低下

库的出现主要解决了三个痛点:

  • 代码复用:将通用的功能封装起来,一次编写,到处运行

  • 模块化开发:大型项目可以拆分成多个模块,由不同团队维护,互不干扰

  • 分发便捷:你可以只提供库文件和头文件给别人使用,而不需要暴露底层源代码

在 Linux 环境下,库的本质是一组预先编译好的目标文件的集合。它就像一个工具箱,提供了可以直接调用的函数和变量

根据链接时机的不同,库分为两类:

  • 静态库(Static Library):链接时将库代码直接拷贝到可执行程序中

  • 动态库(Shared Library):链接时仅建立引用关系,程序运行时才去加载库


2. 演示代码

为了方便演示,我们准备一套简单的数学工具代码。它包含两个文件:一个头文件 my_math.h 定义接口,一个源文件 my_math.c 实现逻辑

头文件:my_math.h

#ifndef __MY__MATH__ #define __MY__MATH__ // 简单的加法 int add(int a, int b); // 简单的减法 int sub(int a, int b); #endif

实现文件:my_math.c

#include "my_math.h" int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; }

测试程序:main.c

#include <stdio.h> #include "my_math.h" int main() { int x = 10, y = 5; printf("add: %d\n", add(x, y)); printf("sub: %d\n", sub(x, y)); return 0; }

二、目标文件

在谈论库之前,我们必须先理解它的前身——目标文件

执行gcc -c my_math.c时,编译器并不会生成一个可以直接运行的程序,而是生成了一个后缀为 .o 的文件,即目标文件

定义与特点:

  • 中间产物:它是源代码经过预处理、编译、汇编后的二进制文件

  • 不可直接执行:虽然它包含了机器码,但由于文件内部的函数地址尚未重定位,操作系统无法直接运行它

  • 文件格式:在 Linux 下,目标文件和库文件通常遵循ELF格式

底层逻辑:构建程序如同建造房屋:源代码是设计图纸,目标文件(.o)如同预制好的砖块,而库文件则是分类打包的建筑材料包。就像不能直接住进砖堆里一样,必须通过链接过程,按照图纸将这些材料组装起来,才能建成最终可执行程序这座房子

三、静态库

静态库在 Linux 中通常以 .a 作为后缀,其本质就是将多个 .o 文件打包压制成一个单一文件


1. 静态库概念

静态库在程序编译链接阶段会被完整地复制到可执行程序中

  • 优点:程序运行速度快(无需动态寻址);发布程序时不需要携带库文件,因为库已经被加载进程序里了

  • 缺点:浪费空间(每个程序都包含一份库的副本);维护困难(库代码更新时,所有依赖程序都需要重新编译)


2. 静态库生成

我们将使用前文准备的 my_math.c 来生成名为 libmymath.a 的静态库

第一步:将源文件编译为目标文件

我们需要 .o 格式的机器码,但不需要它变成可执行程序

第二步:使用 ar 工具打包

ar 是 Linux 下专门用于创建、修改和提取归档文件的工具

命名规范

Linux 下的库文件必须以 lib 开头,以 .a 结尾。中间的 mymath 才是它的真实逻辑名称

ar命令常用选项说明:

  • - r (replace):若库中已存在同名文件,则替换它;若不存在,则添加

  • - c (create):创建一个库


3. 静态库使用

有了 libmymath.a 和 my_math.h,我们就可以编译 main.c 了。为了模拟真实开发环境,我们假设头文件在 include 目录,库文件在 lib 目录

使用示例:

关键选项

这是 Linux C/C++ 开发中最基础也最核心的三个选项:

选项全称含义与作用
-IInclude指定头文件搜索路径。告诉编译器去哪里找 #include "..." 里的文件
-LLibrary Path指定库文件搜索路径。告诉链接器去哪个文件夹里找库文件
-lLink Library指定要链接的库名。注意:这里要去掉前缀 lib 和后缀 .a。例如 libmymath.a 只写 mymath

链接器在工作时,会根据 -lmymath 拼凑出 libmymath.a 这个文件名,然后在 -L 指定的路径中挨个寻找。如果找到了,就会把 libmymath.a 中被 main.c 用到的那部分机器码链接到最终的 my_app 二进制文件中

验证静态库

我们可以使用 ldd 命令来查看生成的可执行程序依赖哪些库:

我们发现结果中找不到 libmymath。这证明了 mymath 的代码已成功整合到 main 的内部,它不再依赖外部的库文件即可独立运行

四、动态库

相较于静态库在编译时将代码整合至可执行文件中的方式,动态库(共享库)采用运行时加载与内存共享的机制,是现代操作系统中核心的库实现方式

在 Linux 系统下,动态库文件一般以 .so 作为后缀名


1. 动态库概念

动态库的核心特征在于:代码不在编译时拷贝,而是在运行时加载

  • 编译阶段:链接器仅在可执行文件中记录 "我需要这个库" 的符号信息(索引),而不复制实际代码

  • 运行阶段:当程序启动时,操作系统的动态链接器会将库文件加载到内存中

  • 多进程共享:如果 100 个程序都使用了同一个 libc.so,内存中只会有一份该库的代码副本,所有程序通过虚拟地址空间映射到同一块物理内存


2. 动态库生成

制作动态库比静态库多了一个关键步骤:生成 "位置无关代码"

第一步:生成目标文件

-fPIC告诉编译器产生的机器码不要使用绝对地址,而要使用相对地址。因为动态库在运行时会被加载到进程空间的任何位置,如果写死了地址,换个地方就跑不通了

第二步:生成动态库

为什么制作动态库不使用 ar?

静态库的生成本质上是利用 ar 工具对目标文件(.o)进行的物理归档,仅涉及文件的封装与索引维护,不触发链接动作;而动态库作为遵循 ELF 格式的可加载二进制对象,其生成过程必须由链接器参与,以完成符号表的合并、依赖关系的导出以及位置无关代码(PIC)的逻辑编排等。因此,动态库的构建属于编译链接范畴,而非简单的文件归档


3. 动态库使用

动态库的使用分为链接加载两个完全独立的阶段

阶段一:链接(编译时)

这一步和静态库完全一样。编译器检查 libmymath.so 是否存在,以及里面的函数签名是否对得上。如果通过,它会在生成的 main 里打上一个标签:"我依赖 libmymath.so"

阶段二:加载(运行时)

当我们尝试执行 ./main 时,会看到这样的报错:

因为此时是操作系统在找库,而不是编译器。操作系统默认只会在 /lib64 或 /usr/lib64 等标准目录下寻找。由于我们的库在当前目录,系统看不见

为了加深理解,我们对比一下两者在程序生命周期中的表现:

静态库动态库
集成时机编译链接时完全嵌入程序运行时按需加载
程序体积较大(包含所有库代码)较小(仅包含引用描述)
内存占用多个程序运行会有重复副本物理内存中仅存一份,全系统共享
更新维护必须重新编译整个程序只需替换 .so 文件,程序重启即生效
运行依赖独立运行,不依赖库文件运行时必须能找到对应的库文件

五、库搜索路径

在 Linux 开发中,程序对库文件的定位分为编译链接时运行时两个独立阶段。理解这两个阶段的搜索路径及其优先级,是解决找不到库文件问题的核心


1. 编译 / 运行时路径

编译时路径

在编译阶段,链接器通过开发者指定的选项来查找静态库或动态库的符号信息

  • -L选项:显式指定链接器搜索库文件的路径

  • -l选项:指定具体的库名称(去掉 lib 前缀与后缀)

  • 标准路径:如果未指定 -L,链接器默认搜索 /lib、/usr/lib 以及 /usr/local/lib 等系统目录

运行时路径

对于动态链接的可执行程序,当其启动时,系统的动态链接器负责将程序依赖的 .so 文件加载进内存。由于动态链接器不直接读取编译时的 -L 参数,它依赖以下机制按优先级进行查找:

(1) 环境变量 LD_LIBRARY_PATH

  • 定义:这是一个进程级的环境变量,用于临时指定动态库的搜索目录。

  • 应用场景:常用于开发调试或在不具备系统权限的情况下,指向非标准目录中的库文件

  • 生效方式

    export LD_LIBRARY_PATH=/path/to/your/lib:$LD_LIBRARY_PATH
  • 特点:优先级高,仅对当前终端及其子进程生效,属于临时配置

(2) 配置文件 /etc/ld.so.conf

  • 定义:这是一个系统级的配置文件,用于永久保存全局动态库的搜索路径

  • 结构:通常该文件会包含 /etc/ld.so.conf.d/ 目录下的所有 .conf 文件,建议为每个第三方库在该目录下创建独立的配置文件

  • 应用场景:安装长期运行的服务或系统级组件时使用

(3) 缓存工具 ldconfig

  • 作用:动态链接器为了提高查找效率,并不会在程序启动时实时遍历所有路径,而是读取一个预先生成的二进制缓存文件 /etc/ld.so.cache

  • 机制:ldconfig 程序负责读取 /etc/ld.so.conf 中的路径,扫描这些目录下的动态库,并更新 /etc/ld.so.cache

  • 必要性:当你在系统目录(如 /usr/lib)或 /etc/ld.so.conf 指定的目录中新增 .so 文件后,必须以 root 权限执行 ldconfig,否则系统无法感知库的更新

(4) 默认系统目录

  • 如果以上路径均未命中,动态链接器将最后检索标准系统路径,主要是 /lib 和 /usr/lib。

优先级查找机制性质
1编译时指定的 RPATH嵌入在 ELF 文件内部的路径(最高优先级)
2LD_LIBRARY_PATH环境变量,用户临时干预
3/etc/ld.so.conf由 ldconfig 根据ld.so.conf生成的系统级缓存
4/lib 和 /usr/lib操作系统默认的标准库存放目录

2. 如何像系统库一样使用

要让系统像识别标准库一样识别我们的 libmymath.so,需要完成头文件库文件的双向集成

(1) 头文件的集成

内核与编译器默认的头文件搜索路径是 /usr/include。内核与编译器默认的头文件搜索路径是 /usr/include

  • 操作:将 my_math.h 拷贝至 /usr/include 目录下

  • 在代码中引用时,可以将 #include "my_math.h" 改为 #include <my_math.h>。编译器在预处理阶段会自动在此标准路径下命中该文件,从而无需在编译中指定头文件搜索路径

(2) 库文件的集成

系统的默认库文件搜索路径通常包括 /lib64 或 /usr/lib64(64位系统)

  • 操作:将 libmymath.so 拷贝至 /usr/lib64(或 /usr/lib)

  • 解决报错:由于动态链接器(ld.so)默认会检索该目录,将库移动并执行 ldconfig 刷新缓存。至此可以直接解决运行程序时出现的 cannot open shared object file 错误

ldconfig 的必要性

仅仅将 .so 文件拷贝到系统目录是不够的。如前所述,动态链接器为了性能会读取缓存文件 /etc/ld.so.cache。所以在拷贝完库文件后,必须以 root 权限执行 ldconfig

该命令会重新扫描 /usr/lib64 等标准目录,将新发现的 libmymath.so 加载到二进制缓存映射表中。此时,程序启动时便能通过缓存瞬间定位到该动态库的物理地址


3. 实际演示

在完成上述集成步骤后,我们的开发工作流将极大简化

简化后的编译指令:

# 此时不再需要 -I 指定头文件路径,也不需要 -L 指定库路径 gcc main.c -o main -lmymath

运行测试:

结果分析

  1. 编译时:链接器在 /usr/lib64 找到了 libmymath.so,校验符号成功

  2. 运行时:动态链接器通过 /etc/ld.so.cache 找到了库的物理路径,成功将其加载至进程地址空间,程序正常执行

七、外部库

1. apt 安装原理

在 Linux 中,使用 sudo apt install 安装一个开发库时,系统并非简单地下载文件,而是执行了一系列符合文件系统层次标准的自动化部署动作

以安装 libncurses5-dev 为例,其幕后流程如下:

  1. 二进制分发:从镜像源下载预编译好的 .so 动态库文件、.a 静态库文件以及 .h 头文件。

  2. 路径归档

    • 头文件解压至 /usr/include/ 目录

    • 库文件解压至 /usr/lib/ 目录

  3. 符号链接创建:自动创建版本号之间的软链接(例如让 libncurses.so 指向真实的实体文件 libncurses.so.5.9),确保编译器通过 -lncurses 就能找到最新版本

  4. 缓存更新:自动触发 ldconfig 程序,将新安装的 .so 路径写入 /etc/ld.so.cache 缓存,确保护程序在运行时能瞬间完成动态装载

在 Linux 环境下调用外部库,通常需遵循以下三个步骤:

  • 包含:在源码中使用 #include <xxx.h>。由于 apt 已将头文件放入标准目录,无需再手动指定 头文件搜索路径

  • 查找:在编译时使用-l选项。链接器会自动在标准库目录下搜索名为 libxxx.so 或 libxxx.a 的文件

  • 链接:确定链接方式。默认情况下,如果同时存在静态库和动态库,编译器优先选择动态链接


2. ncurses 库使用示例

ncurses 是一个提供独立于终端的屏幕绘制和键盘处理能力的函数库

(1) 环境准备

sudo apt install libncurses5-dev

(2) 示例程序:demo.c

该程序演示了如何初始化图形模式、在特定坐标打印文字并捕获键盘输入

#include <ncurses.h> // 必须包含头文件 int main() { // 初始化窗口,进入 ncurses 模式 initscr(); // 禁用行缓冲,输入字符立即传递给程序 cbreak(); // 屏幕不回显输入的字符 noecho(); // 在屏幕中心附近移动光标并打印信息 mvprintw(12, 30, "Welcome to VFS & Library World!"); mvprintw(14, 30, "Press any key to exit..."); // 刷新逻辑屏幕以显示内容 refresh(); // 等待用户输入 getch(); // 退出 ncurses 模式,恢复标准终端 endwin(); return 0; }

(3) 编译指令

由于 ncurses 不是标准库,必须在指令末尾显式链接

gcc demo.c -o demo -lncurses

执行效果:

总结

综上所述,从目标文件到静态库与动态库的制作与使用,我们梳理了代码复用在工程中的基本实现路径。无论是 .a 还是 .so,本质上都是对目标文件的组织与链接方式的不同选择:前者在编译阶段完成整合,后者则将绑定延迟到运行时,从而带来更高的灵活性

与此同时,通过库的搜索路径与系统库的使用方式,我们也进一步理解了程序从编写代码到运行执行之间的完整链路

但更深一层的问题是:这些目标文件与库文件内部究竟是如何组织的?程序在加载与运行时又是如何被解析与链接的?在下一篇中,我们将以 ELF 格式为切入点,深入探讨程序的底层结构与运行机制

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 7:07:40

KForge框架:跨平台AI加速器程序合成技术解析

1. KForge框架概述&#xff1a;跨平台AI加速器程序合成新范式在AI计算领域&#xff0c;编写高性能计算内核一直是开发者面临的核心挑战。传统开发流程需要工程师同时掌握算法原理、硬件架构特性和底层编程语言&#xff08;如CUDA、Metal等&#xff09;&#xff0c;这种复合技能…

作者头像 李华
网站建设 2026/4/24 7:07:38

AI三重劫:影子AI、深度伪造与供应链投毒如何瓦解金融业信任基石

当2025年2月一家欧洲中型银行的财务总监在视频会议上"亲自"批准了一笔2200万欧元的跨境转账时&#xff0c;没有人意识到这是一场精心策划的AI骗局。直到三天后真正的财务总监出差归来&#xff0c;银行才发现这笔钱已经通过17个国家的加密货币账户洗劫一空。更令人震惊…

作者头像 李华
网站建设 2026/4/24 7:07:06

华为OD机试真题 新系统 - 计费时段计算 (Java/Py/C/C++/Js/Go)

计费时段计算 2026 华为OD机试真题 4月22日华为OD上机新系统考试真题 100 分题型 点击查看华为 OD 机试真题完整目录&#xff1a;2026最新华为OD机试新系统卷 双机位C卷 真题题库目录&#xff5c;全覆盖题库 逐点算法考点详解 题目描述 电力公司的电费根据用电的时间&#…

作者头像 李华
网站建设 2026/4/24 7:00:21

Real Anime Z效果对比:与SDXL-Refiner联用后真实系细节增强效果评估

Real Anime Z效果对比&#xff1a;与SDXL-Refiner联用后真实系细节增强效果评估 1. 工具介绍 Real Anime Z是基于阿里云通义Z-Image底座模型与Real Anime Z专属微调权重开发的高精度二次元图像生成工具。该工具专为真实系二次元风格优化&#xff0c;通过多项技术创新实现了高…

作者头像 李华
网站建设 2026/4/24 7:00:21

从AI到抗量子:下一代金融基础设施正在发生什么变化?

在过去几年中,金融科技行业的讨论焦点,经历了几次明显的转移:从区块链性能,到Web3应用,再到AI与自动化。而当这些技术逐渐交叉,一个更底层的问题开始浮现:来的金融系统,应该建立在什么样的基础之上?这不仅是技术问题,更是结构问题。一、AI正在改变的,不只是效率,而是“决策方式…

作者头像 李华
网站建设 2026/4/24 6:59:21

跨越 CRUD 内卷:半导体产业链与算力基建下的软件工程新生态

在留学生计算机科学&#xff08;CS&#xff09;与软件工程的求职圈中&#xff0c;长期存在着一种极度拥挤的路径依赖&#xff1a;大量的候选人涌入前端开发、移动端应用或是后端的增删改查&#xff08;CRUD&#xff09;业务线。随着全球互联网红利期的放缓以及 AI 代码生成工具…

作者头像 李华