首先 我们要明白什么是库?

库(Library)是一组预编译的代码,提供特定的功能,可以被多个程序共享调用,避免重复编写代码。在链接步骤中,链接器将从库文件取得所需的代码,复制到生成的可执行文件中

Linux 中常见的库文件有两种,一种.a为后缀,为静态库,另一种以.so为后缀,为动态库。
静态库:在程序编译时,将库的代码“拷贝”到最终的可执行文件中;
动态库(共享库):在程序运行时,动态载入库的代码,多个程序可以共享同一份库文件。

静态库

静态库通常以.a为后缀(在Linux下),是多个目标文件(.o)组成的归档文件(Archive),里面存储着一组目标文件。

特点

  • 在链接(Linking)的时候被合并到可执行文件中
  • 可执行文件较大,因为库的代码已经复制到程序中
  • 加载快,无需在运行时再载入库文件
  • 无需在运行环境中存在库文件,便于部署

作用

  • 方便将多个目标文件打包成一个完整的可执行程序
  • 使用简单,不依赖外部共享库

创建静态库

# 先编译源文件生成目标文件
gcc -c file1.c -o file1.o
gcc -c file2.c -o file2.o# 将目标文件打包为静态库
ar rcs libmylib.a file1.o file2.o# 编译时链接静态库
gcc main.o -L. -lmylib -o myprogram

使用静态库

  • 在编译时指定库路径和库名(-L和-l参数)
    • l:指定库名(库的文件名为 libxxx.a,库名为 xxx)
    • L:指定库路径
    • static:使用静态链接
gcc -static main.c -o main -l math -L ./
// 错误:gcc -l math -static main.c -o main -L ./

特别注意,必须把 -l math 放在后面。放在最后时它是这样的一个解析过程:

  • 链接器从左往右扫描可重定位目标文件和静态库
  • 扫描 main.c 时,发现两个未解析的符号 add 和 sub,记住这两个未解析的符号
  • 扫描 libmath.a,找到了前面未解析的符号,因此提取相关代码
  • 最终没有任何未解析的符号,编译链接完成
    那如果将 -l math 放在前面,又是怎样的情况呢?
  • 链接器从左往右扫描可重定位目标文件和静态库
  • 扫描 libmath.a,由于前面没有任何未解析的符号,因此不会提取任何代码
  • 扫描 main.c,发现未解析的符号 add 和 sub
  • 扫描结束,还有两个未解析的符号,因此编译链接报错
    如果把-l math放在前面,编译结果如下:

在这里插入图片描述

我们看看最终生成的文件大小:
在这里插入图片描述
生成可执行文件大小为892k
由于最终生成的可执行文件中已经包含了add和sub相关的二进制代码,因此这个可执行文件在一个没有libmath.a的Linux系统中也能正常运行。

动态库

动态库和静态库类似,但是它并不在链接时将需要的二进制代码都“拷贝”到可执行文件中,而是仅仅“拷贝”一些重定位和符号表信息,这些信息可以在程序运行时完成真正的链接过程。Linux 中这类库的名字一般是 libxxx.so。(shared object)

符号表

  • 符号表记录了函数和变量的名字、地址等信息,是连接器、加载器用来解决引用关系的重要数据结构。
  • 在动态库中,符号表包括:
    • 导出符号:库中提供给外部调用的函数和变量
    • 需要被其他程序或库引用的符号

重定位信息

  • 重定位是指在程序或库加载到内存后,动态调整代码中的地址引用,使得符号指向正确的内存地址。
  • 重定位信息记录了哪些位置需要修正(如函数调用、全局变量访问位置)
  • 在静态链接中,重定位在链接阶段完成;在动态链接中,加载时由系统完成。

定义:

  • 动态库也称“共享库”,以.so(Shared Object)为后缀
  • 在程序运行时动态载入,多个程序可以共享同一份库文件

特点

  • 在程序加载时才载入库(动态链接)
  • 可减小可执行文件大小,因为库在运行时加载
  • 便于维护和升级,只需替换库文件,无需重新编译所有程序
  • 运行时依赖,程序必须找到对应的.so文件

作用

  • 方便库的共享和维护
  • 可以实现插件式开发或模块化设计

创建动态库

# 编译生成共享库
gcc -fPIC -c file1.c -o file1.o
gcc -fPIC -c file2.c -o file2.o
gcc -shared -o libmylib.so file1.o file2.o# 编译程序链接动态库
gcc main.o -L. -lmylib -o myprogram

注意:

  • -fPIC:生成位置无关代码(Position Independent Code)
  • -shared:生成共享库

使用动态库

步骤命令示例说明
1. 创建共享库gcc -fPIC -c mylib.c -o mylib.o
gcc -shared -o libmylib.so mylib.o
生成.so文件
2. 编译调用程序gcc main.c -L. -lmylib连接到共享库
3. 设置环境变量或放库到系统路径export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
sudo cp libmylib.so /usr/lib/
让运行时找到共享库
4. 运行程序./myprogram运行程序,调用共享库中的函数

找不到的原因

原因类别描述解决措施
库路径未在标准路径或未指定库文件所在路径未加入系统路径或未在运行时指定设置LD_LIBRARY_PATHrpath
缺少ldconfig刷新缓存添加新库后未刷新系统缓存运行ldconfig
编译参数不正确编译时未设置-rpath或路径错误使用-Wl,-rpath指定路径
依赖缺失库依赖的其他库不存在或路径错误ldd检测,安装缺失的库
权限不足库文件权限限制修改文件权限

主要原因是系统在运行时无法定位到库文件的存放路径,可能因为库路径没有配置正确、库文件放错位置、未刷新缓存或依赖缺失等。

当在运行程序时,动态库(.so文件)找不到,系统会报错,导致程序无法正常启动。这种情况常见的错误信息有:

error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory

动态库找不到时的解决办法

1.临时设置环境变量LD_LIBRARY_PATH

这是一种快速方便的方式,临时告诉系统去哪里找共享库。

export LD_LIBRARY_PATH=.:/path/to/lib:$LD_LIBRARY_PATH
./your_program

示例:假设libmylib.so在当前目录:

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./your_program

缺点:此设置只在当前终端有效,关闭终端后失效。

2.将共享库拷贝到系统标准目录

将你的共享库复制到系统的库目录(如/usr/lib或/usr/local/lib),然后刷新缓存。

sudo cp libmylib.so /usr/lib/
sudo ldconfig

说明

ldconfig命令会刷新系统的库缓存,让系统知道新加入的库。

注意:需要管理员权限。

3.修改程序的RPATH或RUNPATH(在编译时指定)

在编译时,将库的路径提前嵌入到可执行文件中,使其在运行时自动查找。

例:

gcc main.c -L/path/to/lib -Wl,-rpath=/path/to/lib -lmylib -o myprogram
  • -Wl,-rpath=路径:在可执行文件中指定库搜索路径

执行后,无需设置LD_LIBRARY_PATH,系统会自动在指定路径查找库。

4.使用LD_LIBRARY_PATH环境变量文件(配置全局)

编辑/etc/ld.so.conf或在/etc/ld.so.conf.d/目录中添加自定义路径,然后运行

sudo ldconfig

这样,系统会在指定路径中查找库。

5. 动态调试:使用ldd命令检测缺失的依赖库

运行:

ldd ./your_program

可以显示程序依赖的所有共享库,查看是否有“not found”的库。

如果发现缺失的库,可以用上面的方法解决。

小结

方法作用适用场景
LD_LIBRARY_PATH临时指定库路径调试或快速测试
拷贝到系统目录将库放到标准路径,系统自动加载部署时,保证程序可以找到库
RPATH/RUNPATH在编译时嵌入路径内部控制,避免环境变量设置
ldconfig配置全系统范围配置库搜索路径长期维护或系统级别的配置
ldd命令检测依赖查看依赖关系,找出缺失的库调试、排错

静态库与动态库的区别

特性静态库(.a动态库(.so
链接时间编译时(静态链接)运行时(动态链接)
生成文件大小较大(所用到代码要从二进制文件中拷贝一份,复制到可执行文件中)较小(库文件单独存在,仅仅复制了一些重定位和符号表信息)
占用空间运行时每个程序都持有一份库的副本多个程序共享同一份库(内存节省)
升级维护需要重新编译程序只需替换库文件,无需重新编译
依赖关系不需要库文件存在于运行环境中需要库文件存在于运行环境中
运行速度略快(没有动态加载耗时)因为需要动态加载,可能略慢

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88158.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88158.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/88158.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3-组件化-Vue核心思想之一

一.组件及组件化1.组件化的作用由于之前的代码全写在一个App.vue这个文件里面,会到导致一个文件代码过于多而且不易复用,所以有组件化的思想。2.组件的使用①创建创建一个.vue文件,使用setup的简写方式会自动导出.vue文件②导入import 组件对…

OS学习笔记

《几个基本知识点》 一、2的幂 1024210 51229 25628 12827 6426 3225 1624 823 422 221 K210 G220 M230 T240 P250 E260 Z270 Y280 R290 Q2100 二、常用的ASCII码 ‘1’0x31 ‘A’0x41 ‘a’0x61 空格0x20 换行0x0A 回车0x0D 三、存储器层次中的典型速度 CPU/寄存器&#xff1a…

嵌入式学习笔记-MCU阶段-DAY01

恭喜大家完成了C语言的学习,现在咱们来到咱们的硬件MCU阶段,咱们这里的工程用的是keil,环境搭建不再赘述,希望大家在这一阶段仍然学的愉快 1.资料部分 用的最多的就是STM32f103的手册,搭配STM32F103ZET6的开发板 2.概…

three案例 Three.js波纹效果演示

波纹效果,在智慧城市可视化开发中经常用到,这里分享一个比较好玩的案例 以下是详细的步骤: 初始化部分:设置 Three.js 环境,包括场景、相机、渲染器和控制器 几何体和纹理:创建平面几何体并加载波纹纹理 着…

Flutter-详解布局

上一章我们详细的学习了 Flutter 中的Widget,这一章我们将要学习 Flutter 的布局, 在上一章我们了解到了:Everything is a widget,在 Flutter 中几乎所有的对象都是一个 Widget ,当然也包括布局,Flutter 的…

EPLAN 电气制图:建立自己的部件库,添加部件-加SQL Server安装教程(三)上

在智能电气设计领域,EPLAN 作为主流的设计软件,其部件库的完善程度直接影响项目设计的效率与质量。本文将从实际操作出发,详细讲解如何在 EPLAN 中建立专属部件库并添加部件,为电气设计奠定坚实基础。一、部件库:电气设…

静态路由进阶实战全解

一、项目背景二、项目拓扑图三、设备命名与IP地址规划设备名接口编号IP地址规划R1GE0/0192.168.1.1/24GE0/1172.16.1.1/24R2GE0/0192.168.1.2/24GE0/1192.168.2.2/24R3GE0/0192.168.2.3/24GE0/1192.168.3.3/24GE0/2192.168.4.3/24R4GE0/0192.168.3.4/24GE0/1192.168.4.4/24GE0/…

stm32hal模块驱动(3)ssd1305 oled驱动

SD1305 OLED 驱动芯片详细介绍SSD1305 是 Solomon Systech 公司生产的一款 OLED 显示控制器/驱动器,专为 128x64 或 128x32 点阵的 OLED 显示屏设计。下面我将从多个方面详细介绍这款驱动芯片。一、SSD1305 基本特性显示分辨率:最大支持 128 segments 6…

安全为先:如何在 Python 中安全处理数据库连接与敏感信息

安全为先:如何在 Python 中安全处理数据库连接与敏感信息 引言:Python 与安全的数据库交互 自 1991 年诞生以来,Python 凭借其简洁优雅的语法和强大的生态系统,成为 Web 开发、数据科学、人工智能和数据库交互的首选语言。作为“胶水语言”,Python 不仅让开发者能够快速…

服务器经常出现蓝屏是什么原因导致的?如何排查和修复?

服务器出现蓝屏(BSOD,Blue Screen of Death)是一个严重的问题,通常表明系统内核或硬件发生了不可恢复的错误。蓝屏不仅会导致服务器宕机,还可能对业务运行造成重大影响。要有效解决蓝屏问题,需要先找到根本…

为什么elementui的<el-table-column label=“名称“ prop=“name“ label不用写成:label

在 Vue.js 中,label 和 prop 是 el-table-column 组件的普通属性,而不是动态绑定的表达式。因此,不需要使用 : 来绑定它们。 1. Vue.js 中的属性绑定 在 Vue.js 中,属性绑定有两种方式: 静态属性绑定:直接写…

分布式光纤传感:为储能安全保驾护航

储能系统是指一种能够将电能、化学能、动能等形式的能量进行转化、储存和释放的装置,广泛应用于可再生能源发电、智能电网、电动车等领域。储能行业这几年得到了稳步发展,受到政府机构、行业协会、大型能源企业、电网公司、系统集成商、检测认证机构等业…

从历史航拍图像中去除阴影

在光学遥感中,阴影是影响土地覆盖制图精度和分辨率的一个因素,无论是历史影像(黑白影像)还是近期影像(全彩影像)。阴影的产生取决于太阳光照(太阳方位角和天顶角)、相机视点&#xf…

UE material advance 学习笔记

如何体现轮胎速度的快速感:就是增加一个radial blur,会让视觉效果感觉轮胎已经转冒烟了,但是上面两个轮胎的转速其实是相同的这种磨砂的感觉,可以用上ditherAA来实现只看法线这一块,ditherAA就是让他的表面颜色有大量的…

Vue--2、Vue2 项目配置与组件化开发

一、Vue2 项目环境搭建1. 环境准备安装 Node.js:推荐使用 nvm 管理多版本 Node# 安装Node 16.20.2 nvm install 16.20.2 # 切换至指定版本 nvm use 16.20.2 # 验证安装 node -v && npm -v安装 Vue CLI 脚手架:# 国内镜像源安装 npm install --re…

虚幻基础:函数的返回节点

能帮到你的话,就给个赞吧 😘 文章目录函数的返回节点:返回执行后的值返回执行后的值若不执行第一次 返回参数的默认值第二次 返回上一次执行值示例函数的返回节点:返回执行后的值 返回执行后的值 若不执行 第一次 返回参数的默…

FFmpeg 升级指北

近期我参与了部门底层库依赖的 FFmpeg 从 3.4 升级至 7.0.2 的工作,在此分享一些经验和遇到的 API 变动。 将 FFmpeg 升级到高版本后,编译过程中遇到大量报错是常态。这些错误通常源于 API 接口变更或结构体字段调整。此时不必惊慌,核心解决…

RISCV Linux 虚拟内存精讲系列三 -- setup_vm()

在 Linux 使用虚拟地址前,需要先配置页表,这就是 setup_vm() 的作用。然而,Linux 的页表配置,并不是一次过完成的,分了两个阶段,如下:在 setup_vm() 中,主要初始化了:1. …

创客匠人:解析创始人 IP 打造的底层逻辑与知识变现路径

在数字经济时代,创始人 IP 的价值被不断放大,而知识变现作为 IP 商业闭环的核心环节,正成为无数创业者探索的方向。创客匠人深耕知识付费领域多年,见证了大量创始人从 0 到 1 打造 IP 并实现变现的全过程,其背后的逻辑…

Visual Studio 2022 MFC Dialog 添加Toolbar及Tips提示

主要步骤:在主框架类中添加消息处理函数声明在 OnCreate 函数中启用工具栏提示在消息映射中注册 TTN_NEEDTEXT 消息使用 OnToolTipText 函数实现自定义提示文本1.在主程序的.h文件中加入afx_msg BOOL OnToolTipText(UINT id, NMHDR* pNMHDR, LRESULT* pResult); 2.在…