当前位置: 首页 > news >正文

Linux动态库和静态库

Linux动态库和静态库

  • Linux动态库和静态库
    • 动静态库的基本原理
      • 可执行程序的生成过程
      • 动静态库的本质
    • 认识动静态库
      • 背后的库支持
      • 动静态库的命名
      • 静态链接示例
    • 动静态库各自的特征
      • 静态库
      • 动态库
    • 静态库的打包与使用
      • 示例文件
      • 打包
        • 1. 生成目标文件
        • 2. 打包静态库
        • 3. 组织文件
        • 使用 Makefile
      • 使用
        • 方法一:使用选项
        • 方法二:拷贝到系统路径
    • 动态库的打包与使用
      • 打包
        • 1. 生成目标文件
        • 2. 打包动态库
        • 3. 组织文件
        • 使用 Makefile
      • 使用
        • 编译
        • 运行时问题
        • 解决方法
    • 总结


Linux动态库和静态库

在Linux开发中,动态库和静态库是代码复用和程序构建的重要工具。无论是编写小型工具还是大型项目,理解动静态库的原理和使用方法都能极大提高开发效率。本文将从基本原理出发,逐步带您认识动静态库,分析它们的特征,并通过具体示例演示静态库和动态库的打包与使用过程。让我们开始吧!


动静态库的基本原理

可执行程序的生成过程

要理解动静态库的本质,首先需要了解源代码如何变成可执行程序。在Linux下,这一过程分为四个步骤:

  1. 预处理:处理头文件展开、去注释、宏替换和条件编译,生成 .i 文件。例如,#include 会被替换为头文件内容。
  2. 编译:进行词法分析、语法分析、语义分析和符号汇总,将代码翻译成汇编指令,生成 .s 文件。
  3. 汇编:将汇编指令转换为二进制机器码,生成目标文件 .o
  4. 链接:将多个 .o 文件链接起来,生成最终的可执行程序。

假设有五个文件:test1.ctest2.ctest3.ctest4.cmain1.c,要生成可执行程序,我们需要:

  • 分别编译生成 test1.otest2.otest3.otest4.omain1.o
  • 将这些目标文件链接,生成最终程序。

如果另一个项目需要用 test1.ctest4.cmain2.c 生成新程序,过程相同。但如果这些文件频繁复用,每次都重新编译会很麻烦。这时,我们可以将 test1.otest4.o 打包成一个“库”,供不同项目直接链接使用。

动静态库的本质

动静态库本质上是目标文件(.o)的集合,是可执行程序的“半成品”。它们不包含主函数(main),只提供函数或方法的实现,供其他程序调用。库的出现解决了代码复用问题,避免了重复编译的繁琐。


认识动静态库

让我们通过一个简单程序认识动静态库:

#include <stdio.h>

int main()
{
    printf("hello world\n"); // 调用库函数
    return 0;
}

编译并运行:

gcc -o mytest mytest.c
./mytest

输出:hello world

背后的库支持

这个程序能输出 hello world,是因为 gcc 在链接时自动引入了 C 标准库。可以用 ldd 查看依赖:

ldd mytest

输出示例:

linux-vdso.so.1 =>  (0x00007fff5f5ff000)
libc.so.6 => /lib64/libc.so.6 (0x00007f9c8e5b0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9c8e9b4000)

其中,libc.so.6 是 C 标准动态库的软链接。查看其真实文件:

ls -l /lib64/libc.so.6

输出:libc.so.6 -> libc-2.17.so

进一步检查文件类型:

file /lib64/libc-2.17.so

输出:ELF 64-bit LSB shared object, x86-64, ...

这表明 libc-2.17.so 是一个动态共享库(.so 后缀)。

动静态库的命名

  • Linux
    • 动态库:.so(shared object),如 libc.so.6
    • 静态库:.a(archive),如 libm.a
  • Windows
    • 动态库:.dll(dynamic link library)。
    • 静态库:.lib

库名规则:去掉前缀 lib 和后缀(.so.a)及版本号,剩下的是库名。例如,libc.so.6 的库名是 c

静态链接示例

默认情况下,gcc 使用动态链接。若要静态链接,添加 -static

gcc -o mytest-s mytest.c -static

检查依赖:

ldd mytest-s

输出:not a dynamic executable

静态链接的可执行文件不依赖动态库,体积明显更大:

ls -lh mytest mytest-s

可能输出:

  • mytest:几十 KB(动态链接)。
  • mytest-s:几 MB(静态链接)。

动静态库各自的特征

静态库

  • 链接方式:编译时将库代码复制到可执行文件中。
  • 运行时:不依赖外部库。
  • 优点:独立性强,可单独运行。
  • 缺点:文件体积大;多个程序加载相同静态库时,内存中会有重复代码,浪费资源。

动态库

  • 链接方式:运行时才加载库代码,可执行文件中只包含函数入口地址表。
  • 运行时:由操作系统从磁盘加载到内存,多个程序共享同一份代码。
  • 优点:节省磁盘和内存空间,多个程序共享库时效率高。
  • 缺点:依赖动态库,若库缺失则无法运行。

静态库的打包与使用

示例文件

我们用以下文件演示:

  • add.h
    #pragma once
    extern int my_add(int x, int y);
    
  • add.c
    #include "add.h"
    int my_add(int x, int y) { return x + y; }
    
  • sub.h
    #pragma once
    extern int my_sub(int x, int y);
    
  • sub.c
    #include "sub.h"
    int my_sub(int x, int y) { return x - y; }
    

打包

1. 生成目标文件
gcc -c add.c -o add.o
gcc -c sub.c -o sub.o
  • -c:只编译,不链接,生成 .o 文件。
2. 打包静态库
ar -rc libcal.a add.o sub.o
  • ar:归档工具。
  • -r:替换已有文件。
  • -c:创建新库。
  • libcal.a:生成静态库。

验证:

ar -tv libcal.a

输出示例:

rw-r--r-- 0/0  1234 Mar 19 12:34 2025 add.o
rw-r--r-- 0/0  1234 Mar 19 12:34 2025 sub.o
3. 组织文件
mkdir -p mathlib/include mathlib/lib
mv add.h sub.h mathlib/include/
mv libcal.a mathlib/lib/

目录结构:

mathlib/
├── include/
│   ├── add.h
│   └── sub.h
└── lib/
    └── libcal.a
使用 Makefile
CC = gcc
AR = ar
CFLAGS = -c
TARGET = libcal.a
OBJS = add.o sub.o

all: $(TARGET)
add.o: add.c add.h
	$(CC) $(CFLAGS) add.c -o add.o
sub.o: sub.c sub.h
	$(CC) $(CFLAGS) sub.c -o sub.o
$(TARGET): $(OBJS)
	$(AR) -rc $(TARGET) $(OBJS)
output:
	mkdir -p mathlib/include mathlib/lib
	cp *.h mathlib/include/
	cp $(TARGET) mathlib/lib/
clean:
	rm -f *.o $(TARGET) mathlib -r
.PHONY: all output clean
  • make:生成库。
  • make output:组织文件。

使用

测试程序 main.c

#include <stdio.h>
#include <add.h>

int main()
{
    int x = 20, y = 10;
    int z = my_add(x, y);
    printf("%d + %d = %d\n", x, y, z);
    return 0;
}
方法一:使用选项
gcc main.c -I./mathlib/include -L./mathlib/lib -lcal -o main
  • -I:头文件路径。
  • -L:库文件路径。
  • -l:指定库名(cal)。

运行:

./main

输出:20 + 10 = 30

方法二:拷贝到系统路径
sudo cp mathlib/include/* /usr/include/
sudo cp mathlib/lib/libcal.a /lib64/
gcc main.c -lcal -o main
  • 注意:仍需 -lcal 指定库名。

动态库的打包与使用

打包

1. 生成目标文件
gcc -c -fPIC add.c -o add.o
gcc -c -fPIC sub.c -o sub.o
  • -fPIC:生成位置无关码,动态库必需。
2. 打包动态库
gcc -shared -o libcal.so add.o sub.o
  • -shared:生成共享库。
3. 组织文件
mkdir -p mlib/include mlib/lib
mv add.h sub.h mlib/include/
mv libcal.so mlib/lib/
使用 Makefile
CC = gcc
CFLAGS = -c -fPIC
TARGET = libcal.so
OBJS = add.o sub.o

all: $(TARGET)
add.o: add.c add.h
	$(CC) $(CFLAGS) add.c -o add.o
sub.o: sub.c sub.h
	$(CC) $(CFLAGS) sub.c -o sub.o
$(TARGET): $(OBJS)
	$(CC) -shared -o $(TARGET) $(OBJS)
output:
	mkdir -p mlib/include mlib/lib
	cp *.h mlib/include/
	cp $(TARGET) mlib/lib/
clean:
	rm -f *.o $(TARGET) mlib -r
.PHONY: all output clean

使用

编译
gcc main.c -I./mlib/include -L./mlib/lib -lcal -o main
运行时问题

运行 ./main 可能报错:

./main: error while loading shared libraries: libcal.so: cannot open shared object file

检查依赖:

ldd main

输出:libcal.so => not found

解决方法
  1. 拷贝到系统路径

    sudo cp mlib/lib/libcal.so /lib64/
    ./main
    
  2. 设置 LD_LIBRARY_PATH

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/user/mlib/lib
    ./main
    
  3. 配置 /etc/ld.so.conf.d/

    echo "/home/user/mlib/lib" > mylib.conf
    sudo mv mylib.conf /etc/ld.so.conf.d/
    sudo ldconfig
    ./main
    
    • ldconfig:更新动态库缓存。

总结

  • 静态库:打包用 ar,生成 .a 文件,编译时嵌入代码,独立性强但体积大。
  • 动态库:用 gcc -shared,生成 .so 文件,运行时加载,节省空间但依赖库文件。
  • 使用差异:静态库无需运行时配置,动态库需确保库路径可访问。

通过本文,您应该已经掌握了Linux下动静态库的原理和实践。希望这些知识能在您的开发中派上用场!


相关文章:

  • RAGFlow + LlamaIndex 本地知识库RAG增强架构与实现直播智能复盘
  • 【入门初级篇】布局类组件的使用(1)
  • 如何通过Python实现自动化任务:从入门到实践
  • 2025年 cocosCreator 1.8 定制 JavaScript 引擎
  • Web Component 教程(五):从 Lit-html 到 LitElement,简化组件开发
  • 用css绘制收银键盘
  • 实验三 内存管理
  • RocketMQ 架构
  • std::move
  • Unity3D开发AI桌面精灵/宠物系列 【二】 语音唤醒 ivw 的两种方式-Windows本地或第三方讯飞等
  • 一些常用的docker镜像及命令 python各版本(持续更新中)
  • pnpm config set ignore-workspace-root-check true
  • 【Spring Boot 中 `@Value` 注解的使用】
  • Python散点图(Scatter Plot):高阶分析、散点图矩阵、三维散点图及综合应用
  • 塔能智慧运维箱:智慧城市的“量子跃迁”,创新与售后的双轨驱动
  • 硬件基础(5):(1)二极管初步认识
  • Git 使用笔记
  • 基于大模型的唇裂手术全流程预测与应用研究报告
  • CLR中的marshal_context 介绍
  • redis分布式锁实现Redisson+redlock中watch dog是如何判断当前线程是否持有锁进行续租的呢?
  • 怒江州委常委、泸水市委书记余剑锋调任云南省委省直机关工委副书记
  • 新华社评论员:汇聚起工人阶级和广大劳动群众的磅礴力量
  • 太好玩了!坐进大卫·霍克尼的敞篷车进入他画笔下的四季
  • 深圳一季度GDP为8950.49亿元,同比增长5.2%
  • 外交部:欢迎外国朋友“五一”来中国
  • 我国将出台稳就业稳经济推动高质量发展若干举措,将根据形势变化及时出台增量储备政策