前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >[操作系统] 动静态库制作与原理

[操作系统] 动静态库制作与原理

作者头像
DevKevin
发布2025-03-07 10:23:35
发布2025-03-07 10:23:35
13800
代码可运行
举报
文章被收录于专栏:Base_CDNKevinBase_CDNKevin
运行总次数:0
代码可运行
在Linux操作系统中,库(Library)是编程中不可或缺的一部分,它为开发者提供了现成的、成熟的、可复用的代码,帮助程序员减少重复工作并提高开发效率。本文将基于学习Linux操作系统时的课件内容,系统地讲解库的制作与原理,涵盖静态库和动态库的定义、生成、使用以及相关问题与解决方案。

什么是库?

库本质上是一组可执行代码的二进制形式,可以被操作系统加载到内存中执行。它的存在意义在于:

  • 复用性:库是已经编写好的成熟代码,开发者无需从零开始编写底层功能。
  • 依赖性:现实中的每个程序通常依赖多个基础底层库(如C标准库libc、C++标准库libstdc++)。

在Linux(以及Windows)中,库主要分为两种类型:

  • 静态库:文件扩展名为.a(Linux)或.lib(Windows)。静态库在程序编译链接时会被直接嵌入到可执行文件中,程序运行时无需依赖外部库文件。
  • 动态库:文件扩展名为.so(Linux)或.dll(Windows)。动态库在程序运行时才会被加载,多个程序可以共享同一份动态库代码,从而节省内存和磁盘空间。

举例:常见的库文件

在Linux中,你可以通过ls命令查看系统中的库文件。例如:

  • 静态库:<font style="color:black;">/lib/x86_64-linux-gnu/libc.a</font>
  • 动态库:<font style="color:black;">/lib/x86_64-linux-gnu/libc-2.31.so</font>

同样,C++的标准库也有静态和动态版本:

  • 静态库:<font style="color:black;">/usr/lib/gcc/x86_64-linux-gnu/9/libstdc++.a</font>
  • 动态库:<font style="color:black;">/usr/lib/gcc/x86_64-linux-gnu/9/libstdc++.so</font>(通常是一个软链接,指向具体的版本如<font style="color:black;">libstdc++.so.6</font>)。

静态库

静态库的定义

静态库(.a文件)在程序编译链接阶段会被完全链接到可执行文件中。生成的可执行文件包含所有需要的库代码,运行时无需再查找或加载外部库文件。这使得静态链接的程序更加独立,但也可能导致可执行文件体积较大。

静态库的生成

生成静态库通常使用ar(GNU归档工具)命令。以下是一个典型的Makefile示例,用于生成静态库libmystdio.a****:

代码语言:javascript
代码运行次数:0
复制
libmystdio.a: my_stdio.o my_string.o
    @ar -rc $@ $^
    @echo "build $^ to $@ ... done"

%.o: %.c
    @gcc -c $<
    @echo "compiling $< to $@ ... done"

.PHONY: clean
clean:
    @rm -rf *.a *.o stdc*
    @echo "clean ... done"

.PHONY: output
output:
    @mkdir -p stdc/include
    @mkdir -p stdc/lib
    @cp -f *.h stdc/include
    @cp -f *.a stdc/lib
    @tar -czf stdc.tgz stdc
    @echo "output stdc ... done"
  • ar -rc: r表示替换已有文件,c表示如果库文件不存在则创建新文件。
@</font><font style="color:black;">是目标文件,</font><font style="color:black;">

^是所有依赖文件。

  • 查看静态库内容:使用ar -tv libmystdio.a可以列出库中包含的对象文件(如my_stdio.o和my_string.o)。

静态库的使用

使用静态库时,需要通过gcc编译并链接。以下是几种常见场景:

场景1:头文件和库文件安装到系统路径下

假设libmystdio.a和相关头文件(如my_stdio.h)已安装到系统默认路径(如/usr/lib和/usr/include),编译命令如下:

代码语言:javascript
代码运行次数:0
复制
gcc main.c -l mystdio

场景2:头文件和库文件与源文件在同一路径下

如果libmystdio.a和头文件与main.c在同一目录,编译命令为:

代码语言:javascript
代码运行次数:0
复制
gcc main.c -L. -l mystdio
  • -L.:指定当前目录作为库搜索路径。
  • -l:指定库名,去掉lib前缀和.a后缀(如libmystdio.a对应-lmystdio)。

场景3:头文件和库文件有独立路径

如果头文件和库文件位于自定义路径,编译命令为:

代码语言:javascript
代码运行次数:0
复制
gcc main.c -I头文件路径 -L库文件路径 -lmystdio
  • -I:指定头文件搜索路径。
  • -L:指定库文件搜索路径。

特点:生成的可执行文件包含静态库的全部代码,即使删除静态库文件,程序仍可正常运行。

使用-static选项:如果希望强制链接静态库,可以使用gcc -static main.c -lmystdio,但这会增加可执行文件的大小。

动态库

动态库的定义

动态库(.so文件)在程序运行时才被加载到内存中。相比静态库,动态库具有以下优点:

  • 共享性:多个程序可以共享同一份动态库代码,节省内存和磁盘空间。
  • 灵活性:动态库可以在程序运行时动态加载或卸载,便于更新和维护。
  • 体积小:可执行文件只包含函数入口地址表,而非完整的机器码。

然而,动态库的缺点是程序运行时需要依赖外部库文件,如果库文件缺失或路径错误,程序可能无法启动。

动态库的生成

生成动态库需要使用gcc的-shared选项,并确保生成位置无关代码(Position Independent Code, PIC)。以下是Makefile示例:

代码语言:javascript
代码运行次数:0
复制
libmystdio.so: my_stdio.o my_string.o
    gcc -o $@ $^ -shared

%.o: %.c
    gcc -fPIC -c $<

.PHONY: clean
clean:
    @rm -rf *.so *.o stdc*
    @echo "clean ... done"

.PHONY: output
output:
    @mkdir -p stdc/include
    @mkdir -p stdc/lib
    @cp -f *.h stdc/include
    @cp -f *.so stdc/lib
    @tar -czf stdc.tgz stdc
    @echo "output stdc ... done"
  • -shared:指定生成共享库(动态库)。
  • -fPIC:生成位置无关代码,确保代码可以在不同地址加载时正确运行。
  • 动态库命名规则:libxxx.so,如libmystdio.so。

动态库的使用

使用动态库的编译方式与静态库类似,但运行时需要确保动态库文件可被找到。以下是几种场景:

场景1:头文件和库文件安装到系统路径下

代码语言:javascript
代码运行次数:0
复制
gcc main.c -lmystdio

场景2:头文件和库文件与源文件在同一路径下

代码语言:javascript
代码运行次数:0
复制
gcc main.c -L. -lmystdio

场景3:头文件和库文件有独立路径

代码语言:javascript
代码运行次数:0
复制
gcc main.c -I头文件路径 -L库文件路径 -lmystdio

查看依赖:使用ldd命令检查可执行文件或动态库的依赖。例如:

代码语言:javascript
代码运行次数:0
复制
ldd a.out

输出可能如下:

代码语言:javascript
代码运行次数:0
复制
linux-vdso.so.1 => (0x00007fff4d396000)
libmystdio.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007fa2aef30000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa2af2fe000)

如果libmystdio.so => not found,说明动态库未被找到,需要解决路径问题。


库运行时的搜索路径

问题:动态库找不到

在运行动态链接的可执行文件时,如果动态库(如libmystdio.so)未被找到,程序会报错。例如:

代码语言:javascript
代码运行次数:0
复制
./a.out: error while loading shared libraries: libmystdio.so: cannot open shared object file: No such file or directory

这是因为操作系统默认的库搜索路径中没有包含libmystdio.so。

解决方案

以下是解决动态库找不到问题的几种方法:

  1. 将.so文件复制到系统共享库路径 将动态库文件复制到系统默认共享库路径,如/usr/lib、/usr/local/lib或/lib64:bash
代码语言:javascript
代码运行次数:0
复制
cp libmystdio.so /usr/lib
  1. 建立软链接 在系统共享库路径下建立指向动态库的软链接:bash
代码语言:javascript
代码运行次数:0
复制
ln -s /path/to/libmystdio.so /usr/lib/libmystdio.so
  1. 设置环境变量****LD_LIBRARY_PATH 临时修改LD_LIBRARY_PATH环境变量,使其包含动态库路径:bash
代码语言:javascript
代码运行次数:0
复制
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/library

然后重新运行程序。

  1. 使用ldconfig配置 编辑/etc/ld.so.conf或其子配置文件(如/etc/ld.so.conf.d/bit.conf),添加动态库路径:bash
代码语言:javascript
代码运行次数:0
复制
echo "/path/to/library" >> /etc/ld.so.conf.d/bit.conf
ldconfig

ldconfig会重新加载库搜索路径,使新添加的路径生效。

结论

  1. gcc/g++默认使用动态库。
    1. 一定要静态链接的话,使用-static,且必须存在对应的静态库
    2. 如果只存在静态库,对于该库,只能静态链接
  2. 在Linux下,默认情况安装的大部分库,默认都是有限安装动态库
  3. 一个动态库对应多个应用程序,因为是动态链接,每个exe都可以使用
  4. visual studio不仅可以形成可执行程序,也可以形成动静态库

总结

  • 静态库适合需要独立运行的程序,但会增加可执行文件体积。生成和使用静态库需要ar工具和gcc的-static选项。
  • 动态库更节省资源,适合共享使用的场景,但需要确保运行时库文件可被找到。生成动态库需要-shared和-fPIC选项。
  • 库路径管理是动态库使用中的关键,常用LD_LIBRARY_PATH或ldconfig解决库文件找不到的问题。

通过理解静态库和动态库的原理与使用方法,开发者可以更高效地利用Linux下的库资源,提升程序的开发和运行效率。如果你在实践中遇到具体问题,可以进一步探讨或查阅相关文档。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-03-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是库?
  • 静态库
    • 静态库的定义
    • 静态库的生成
    • 静态库的使用
  • 动态库
    • 动态库的定义
    • 动态库的生成
    • 动态库的使用
  • 库运行时的搜索路径
    • 问题:动态库找不到
    • 解决方案
  • 结论
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档