【Linux】动静态库知识大梳理

亲爱的读者朋友们😃,此文开启知识盛宴与思想碰撞🎉。

快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。

        在 Linux 系统编程中,动静态库是重要的组成部分,理解它们的原理、制作方法和使用技巧,对于开发者来说至关重要。


目录

一、动静态库基础概念

二、静态库的原理与操作

2.1 原理剖析

2.2 制作流程

2.3 使用方法及路径问题

2.4 errno 的作用

2.5 库的安装

三、动态库的原理与操作

3.1 原理与命令

3.2 动静态库分离实践

3.3 动态库加载问题

3.4 解决加载问题的方法

3.5 ncurses 库介绍

四、动态加载的底层原理

4.1 动态库加载机制

4.2 进程地址空间详解

4.3 动态库地址处理


一、动静态库基础概念

        库是已编写好、可复用的代码,以二进制形式存在,能被操作系统载入内存执行,分为静态库和动态库。

  • 静态库(.a)在程序编译链接时,代码被链接到可执行文件中(因此一般文件内存会更大),程序运行时不再依赖静态库;
  • 动态库(.so)即 “Shared Object” 的缩写,在程序运行时才链接代码,多个程序可共享使用。

📌注意: 

  • 一个可执行程序可能用到许多的库,这些库运行时有的是静态库,有的是动态库。我们的编译默认使用动态链接库,只有在该库下找不到动态 .so 文件时,才会采用同名静态库。我们也可以使用 gcc 的 -static 选项强制设置链接静态库。
gcc -static main.c -o main_static

二、静态库的原理与操作

2.1 原理剖析

 

        静态库的原理是将相关源文件编译为.o 目标文件,再打包成 libXXX.a 文件。

        当主程序的 main.c 编译为 main.o 时,会自动与静态库链接形成可执行程序。在这个过程中,ar 命令用于生成静态库,如ar -rc libmymath.a add.o sub.o,其中-rc选项表示若目标静态库文件存在则替换,不存在则创建。

📌解释: 

  • ar -rc libmymath.a add.o sub.o 这条命令的作用是,把 add.o 和 sub.o 这两个目标文件添加到 libmymath.a 静态库中。如果 libmymath.a 库文件不存在,就会创建一个新的;如果已经存在,就把 add.o 和 sub.o 插入到库中,若库中已有同名文件,会用新文件替换旧文件。而且,在创建或更新库文件的过程中不会显示提示信息。 

2.2 制作流程

以制作一个简单的数学库为例,先编写源文件,如 mymath.c 定义数学运算函数,mymath.h 声明函数。然后编写 Makefile 文件,内容如下:

# 定义一个变量 lib,其值为静态库的文件名 libmymath.a
lib=libmymath.a# 这是一个目标规则,目标是生成静态库 libmymath.a
# 此规则表明,生成 libmymath.a 依赖于 mymath.o 文件
$(lib):mymath.o# 使用 ar 命令将 mymath.o 文件打包成静态库 libmymath.a# $@ 代表目标文件,即 libmymath.a# $^ 代表所有的依赖文件,即 mymath.oar -rc $@ $^# 这是一个目标规则,目标是生成目标文件 mymath.o
# 此规则表明,生成 mymath.o 依赖于 mymath.c 文件
mymath.o:mymath.c# 使用 gcc 编译器将 mymath.c 文件编译成目标文件 mymath.o# -c 选项表示只进行编译,不进行链接# $^ 代表所有的依赖文件,即 mymath.cgcc -c $^# .PHONY 声明了一些伪目标,伪目标不是真正的文件,而是一个动作
# 这里声明了 clean 为伪目标,即使当前目录下存在名为 clean 的文件,make 也会执行 clean 对应的命令
.PHONY:clean# 这是一个伪目标规则,用于清理生成的中间文件和静态库文件
# 执行 make clean 命令时,会删除当前目录下所有的 .o 文件和 .a 文件
clean:rm -f *.o *.a

        执行 Makefile 文件,就能生成静态库。之后可将库文件和头文件整理到相应目录,方便分发使用。

2.3 使用方法及路径问题

⭐    使用静态库时,需注意头文件和库文件的路径设置若头文件路径未指定,gcc 默认在系统路径(如 /usr/include)和当前目录查找。可通过-I选项指定头文件搜索路径,如gcc main.c -I./lib/include 。对于库文件,gcc 默认在系统路径(如 /lib64、/usr/lib)和当前目录查找,若找不到,需用-L选项指定库路径,如gcc main.c -L./lib/mymathlib此外,还需用-l选项指明要链接的库名(去掉前缀 lib 和后缀.a),如gcc main.c -I./lib/include -L./lib/mymathlib -lmymath

  • -I: 指定头文件搜索路径
  • -L: 指定库路径
  • -l: 指定库名
  • 测试目标文件生成后,静态库删掉,程序照样可以运行
  • 库文件名称和引入库的名称:去掉前缀 lib,去掉后缀 .so.a,如:libc.so -> c

2.4 errno 的作用

        库中常提供全局变量 errno 用于指示运行时错误。如在数学库的除法函数中,当除数为 0 时,可设置 errno 表示错误,方便调用者判断结果正确性及获取错误原因。

2.5 库的安装

        库的安装本质上是将库文件和头文件放置到系统可查找的路径下,可通过拷贝文件或建立软连接实现。如将头文件拷贝到 /usr/include,库文件拷贝到 /lib64 ,之后编译时只需用-l选项指定库名即可。但不建议随意将第三方库安装到系统路径,以免污染系统库。


三、动态库的原理与操作

3.1 原理与命令

        动态库的制作同样先将源文件编译为.o 文件,但编译时需添加-fPIC选项,用于生成位置无关码,使库能在虚拟内存的任意位置加载。生成动态库使用gcc -shared -o libmymethod.so *.o命令,-shared选项告诉 gcc 生成共享库。

📌解释: 

  • 假设当前目录下有 add.o 和 sub.o 两个目标文件,执行 gcc -shared -o libmymethod.so *.o 命令后,gcc 会将 add.o 和 sub.o 链接成一个名为 libmymethod.so 的动态链接库。

3.2 动静态库分离实践

        在实际开发中,可将动静态库分离管理。通过编写 Makefile 文件,分别生成静态库和动态库,如:

# 定义动态库变量,值为动态库文件名libmymethod.so
dy-lib=libmymethod.so
# 定义静态库变量,值为静态库文件名libmymath.a
static-lib=libmymath.a# 声明all为伪目标,伪目标不是真正的文件,用于定义一组操作
.PHONY:all
# all目标,依赖于动态库和静态库的生成,执行make或make all时会构建这两个库
all: $(dy-lib) $(static-lib)# 构建静态库libmymath.a的规则,依赖于mymath.o文件
$(static-lib):mymath.o# 使用ar工具将mymath.o打包成静态库libmymath.a# $@代表目标文件,即libmymath.a# $^代表所有的依赖文件,即mymath.oar -rc $@ $^# 从mymath.c生成mymath.o的规则
mymath.o:mymath.c# 使用gcc编译器将mymath.c编译成目标文件mymath.o# -c选项表示只编译不链接gcc -c $^# 构建动态库libmymethod.so的规则,依赖于mylog.o和myprint.o文件
$(dy-lib):mylog.o myprint.o# 使用gcc编译器生成共享库(动态库)libmymethod.so# -shared选项用于生成共享库# $@代表目标文件,即libmymethod.so# $^代表所有的依赖文件,即mylog.o和myprint.ogcc -shared -o $@ $^# 从mylog.c生成mylog.o的规则
mylog.o:mylog.c# 使用gcc编译器将mylog.c编译成目标文件mylog.o,并生成位置无关码# -fPIC选项用于生成位置无关码,使生成的目标文件可用于创建共享库gcc -fPIC -c $^# 从myprint.c生成myprint.o的规则
myprint.o:myprint.c# 使用gcc编译器将myprint.c编译成目标文件myprint.o,并生成位置无关码# -fPIC选项用于生成位置无关码,使生成的目标文件可用于创建共享库gcc -fPIC -c $^# 声明clean为伪目标,用于清理生成的文件
.PHONY:clean
# clean目标,执行make clean时会删除所有的.o、.a、.so文件以及mylib目录
clean:rm -rf *.o *.a *.so mylib# 声明output为伪目标,用于整理和输出库文件及头文件
.PHONY:output
# output目标,执行make output时会创建mylib目录结构,并将头文件、静态库和动态库文件复制到相应位置
output:# 创建mylib/include目录,如果目录已存在则不报错mkdir -p mylib/include# 创建mylib/lib目录,如果目录已存在则不报错mkdir -p mylib/lib# 将所有.h头文件复制到mylib/include目录cp *.h mylib/include# 将所有.a静态库文件复制到mylib/lib目录cp *.a mylib/lib# 将所有.so动态库文件复制到mylib/lib目录cp *.so mylib/lib

        这样可清晰管理不同类型的库,提高项目的可维护性。

 

3.3 动态库加载问题

        生成可执行程序后,运行时可能出现找不到动态库的情况,如这是因为系统加载器在运行时找不到动态库路径,即使编译时指定了库路径,运行时也需重新告知系统。 

3.4 解决加载问题的方法

 

  1. 解决动态库加载问题有多种方法。可将动态库拷贝到系统默认库路径(如 /usr/lib64);
  2. 也可在系统默认库路径建立软连接,如sudo ln -s /home/whb/108/Lesson24/test/mylib/lib/libmymethod.so /lib64/libmymethod.so
  3. 还能将库所在路径添加到环境变量 LD_LIBRARY_PATH 中,但该方法在关闭 shell 后失效,若想长久生效,需将环境变量写入系统启动配置文件(如~/.bash_profile);
  4. 另外,可在 /etc/ld.so.conf.d 目录下创建配置文件,添加动态库路径后执行 ldconfig 重新加载。

3.5 ncurses 库介绍

         ncurses 是基于终端的图形库,可用于控制终端界面。在 CentOS 系统中,可通过sudo yum install ncurses-devel命令安装,安装后开发者能利用其丰富功能创建交互式终端应用程序,提升用户体验。


四、动态加载的底层原理

4.1 动态库加载机制

        当 CPU 执行代码时,若调用库函数,会先在共享区查找。若库文件未加载进内存,会触发缺页中断,系统将动态库文件加载进来,并建立与页表的映射关系。此后,程序在进程地址空间中执行。系统会管理多个动态库的加载情况,确保程序正确运行。

 进程如何看到动态库的:

 

 进程间如何共享库的:

4.2 进程地址空间详解

        程序编译好后,内部已有地址概念,采用平坦模式编址(0 - 4GB),此时的地址为逻辑地址,也是虚拟地址。编译形成可执行程序时,会生成存储各段地址的表,表头地址即 main 函数地址,CPU 从该地址开始执行。执行过程中,CPU 读取的指令可能包含数据或虚拟地址。若虚拟地址在页表中无映射关系,会引发缺页中断,将所需内容加载进内存并建立映射。

4.3 动态库地址处理

        由于动态库可能有多个,难以保证每个库都加载到固定位置,因此采用相对编址方式。gcc 的-fPIC选项让编译器用偏移量对库中函数编址,库加载时,通过起始地址加偏移量的方式找到库函数,实现库在虚拟内存任意位置的加载!


        动静态库的各个环节紧密相连,就像一条完整的 “生产线”,每个环节都不可或缺,共同支撑着 Linux 系统编程的高效运行🚀。

欢迎关注我👉【A charmer】 

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

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

相关文章

06-公寓租赁项目-后台管理-公寓管理篇

尚庭公寓项目/公寓管理模块 https://www.yuque.com/pkqzyh/qg2yge/5ba67653b51379d18df61b9c14c3e946 一、属性管理 属性管理页面包含公寓和房间各种可选的属性信息,其中包括房间的可选支付方式、房间的可选租期、房间的配套、公寓的配套等等。其所需接口如下 1.1…

Links for llama-cpp-python whl安装包下载地址

Links for llama-cpp-python whl安装包下载地址 Links for llama-cpp-python whl安装包下载地址 https://github.com/abetlen/llama-cpp-python/releases

为境外组织提供企业商业秘密犯法吗?

企业商业秘密百问百答之九十六:为境外组织提供企业商业秘密犯法吗? 在日常的对外交流中,企业若暗中为境外的机构、组织或人员窃取、刺探、收买或非法提供商业秘密,这种行为严重侵犯了商业秘密权利人的合法权益,更深远…

grep 命令详解(通俗版)

1. 基础概念 grep 是 Linux 下的文本搜索工具,核心功能是从文件或输入流中筛选出包含指定关键词的行。 它像“文本界的搜索引擎”,能快速定位关键信息,特别适合日志分析、代码排查等场景。 2. 基础语法 grep [选项] "搜索词" 文件…

JSVMP逆向实战:原理分析与破解思路详解

引言 在当今Web安全领域,JavaScript虚拟机保护(JSVMP)技术被广泛应用于前端代码的保护和反爬机制中。作为前端逆向工程师,掌握JSVMP逆向技术已成为必备技能。本文将深入剖析JSVMP的工作原理,并分享实用的逆向破解思路…

【youcans论文精读】弱监督深度检测网络(Weakly Supervised Deep Detection Networks)

欢迎关注『youcans论文精读』系列 本专栏内容和资源同步到 GitHub/youcans 【youcans论文精读】弱监督深度检测网络 WSDDN 0. 弱监督检测的开山之作0.1 论文简介0.2 WSDNN 的步骤0.3 摘要 1. 引言2. 相关工作3. 方法3.1 预训练网络3.2 弱监督深度检测网络3.3 WSDDN训练3.4 空间…

基于Contiue来阅读open-r1中的GRPO训练代码

原创 快乐王子HP 快乐王子AI说 2025年04月03日 23:54 广东 前面安装了vscode[1]同时也安装了Coninue的相关插件[2],现在想用它们来阅读一下open-r1项目的代码[3]。 首先,从启动训练开始(以GRPO为例子) 第一步,使用TRL的vLLM后端…

JVM深入原理(六)(二):双亲委派机制

目录 6.5. 类加载器-双亲委派机制 6.5.1. 双亲委派机制-作用 6.5.2. 双亲委派机制-工作流程 6.5.3. 双亲委派机制-父加载器 6.5.4. 双亲委派机制-面试题 6.5.5. 双亲委派机制-代码主动加载一个类 6.6. 类加载器-打破双亲委派机制 6.6.1. 打破委派-ClassLoader原理 6.6.…

Linux 文件系统超详解

一.磁盘 磁盘是计算机的主要存储介质,它可以存储大量二进制数据,即使断电后也可以保证数据不会丢失。下面我们将了解磁盘的物理结构、存储结构以及逻辑结构。 磁盘的存储结构 1. 磁盘寻址的时候,基本单位既不是bit也不是byte,而…

2025年大模型与Transformer架构:重塑AI未来的科技革命

引言:一场关于智能的革命 想象一下,当你向一个虚拟助手提问时,它不仅能够准确理解你的需求,还能生成一段流畅且富有逻辑的回答;或者当你上传一张模糊的照片时,系统可以快速修复并生成高清版本——这一切的…

GO语言学习(16)Gin后端框架

目录 ☀️前言 1.什么是前端?什么是后端?🌀 2.Gin框架介绍 🌷 3.Gin框架的基本使用 -Hello,World例子🌷 🌿入门示例 - Hello,World 💻补充(一些常用的网…

深入解析 Git Submodule:从基础到高级操作指南

深入解析 Git Submodule:从基础到高级操作指南 一、Git Submodule 是什么? git submodule 是 Git 提供的一个强大功能,允许在一个 Git 仓库(主仓库)中嵌入另一个独立的 Git 仓库(子模块)。主仓…

电子电气架构 --- EEA演进与芯片架构转移

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…

如何用deepseek生成流程图

软件准备: 在线流程图【Flowchart Maker & Online Diagram Software】或【process on】 步骤: 1、用 【DeepSeek】生成 结构化内容(Mermaid文件) 1.1、向deepseek输入指令:【帮我用mermaind写出“某某”的具体…

【华为OD技术面试真题 - 技术面】- Java面试题(17)

华为OD面试真题精选 专栏:华为OD面试真题精选 目录: 2024华为OD面试手撕代码真题目录以及八股文真题目录 文章目录 华为OD面试真题精选虚拟机分区1. **虚拟磁盘分区**2. **虚拟机的内存分区**3. **CPU分配**4. **虚拟网络分区**5. **存储虚拟化和分区**6. **虚拟机分区管理**…

Linux | I.MX6ULL内核及文件系统源码结构(7)

01 类型 描述 备注 ARM 交叉编译器 版本:4.9.4 提供软件工具 Uboot 版本:2016.03 提供源码 支持LCD显示;支持网口; 支持 EMMC,NAND FLASH; 支持环境变量修改保存 Linux 内核 版本:4.1.15 提供…

0基础入门scrapy 框架,获取豆瓣top250存入mysql

一、基础教程 创建项目命令 scrapy startproject mySpider --项目名称 创建爬虫文件 scrapy genspider itcast "itcast.cn" --自动生成 itcast.py 文件 爬虫名称 爬虫网址 运行爬虫 scrapy crawl baidu(爬虫名) 使用终端运行太麻烦了,而且…

鸿蒙NEXT小游戏开发:猜小球

1. 引言 “猜小球”是一个经典的益智游戏,通常由一名表演者和多名参与者共同完成。表演者会将一个小球放在一个杯子下面,然后将三个杯子快速地交换位置,参与者则需要猜出最终哪个杯子下面有小球。本文将介绍如何使用HarmonyOS NEXT技术&…

网络购物谨慎使用手机免密支付功能

在数字经济蓬勃发展的当下,“免密支付”成为许多人消费时的首选支付方式。 “免密支付”的存在有其合理性。在快节奏的现代生活中,时间愈发珍贵,每节省一秒都可能带来更高的效率。以日常通勤为例,上班族乘坐交通工具时&#xff0c…

记录 | Android getWindow().getDecorView().setSystemUiVisibility(...)设置状态栏属性

纯纯的一边开发一边学习,是小白是菜鸟,单纯的记录和学习,大神勿喷,理解有错望指正~ getWindow().getDecorView().setSystemUiVisibility(…) 该方法用于控制系统 UI(如状态栏、导航栏)的可见性…