【Linux】动静态库的制作和使用

news2025/1/10 10:57:20

目录

一、可执行文件的形成

二、静态库

2.1 制作静态库

2.2 使用静态库

三、动态库

3.1 动态库的制作

3.2 动态库链接

2.3 动态库的加载


一、可执行文件的形成

在C语言中,函数库文件分为两种类型:

  • 静态库(库程序是直接注入目标程序的,不分彼此,库文件通常以.a结尾)
  • 动态库(库程序是在运行目标程序时(中)加载的,库文件通常以.so结尾)

 静态库链接源文件生成可执行文件的过程如下:

 接下来我们回顾一下源文件到可执行文件的编译过程。【Linux】基础开发工具——gcc/g++使用

预编译/处理

预编译是使用预编译器进行处理.c源文件和.h头文件,最终生成一个.i的文件。预编译过程就是处理源代码中以#开头的预编译指令,如#include #define 等。预编译过程等价于如下命令:

gcc -E hello.c -o hello.i 

#include 就是将包含的头文件全部展开到#include的位置,所以一个.c源文件如果包含多个头文件,头文件的顺序是需要注意的地方。

编译

编译的过程就是将 预处理 完的文件进行一系列的词法分析、语法分析、语义分析及优化,最后生成 .s 汇编代码文件。编译过程等价如下命令:

gcc -S hello.i -o hello.s

编译过程是整个程序构建的核心部分,也是最复杂的部分之一。

汇编

汇编器是将汇编代码转变成机器可以执行的指令, 每一条汇编代码几乎都对应着一条机器指令。最后生成一个 .o 目标文件。汇编过程等价如下命令:

gcc -c hello.s -o hello.o

汇编器的汇编过程相对简单一些,只需要根据汇编指令和机器指令对照表一一翻译就可以了。

链接

将我们编译出来的目标文件和我们代码所用到的库文件一起打包成一个可执行文件的过程。例如myprint.c中的打印函数Print,这个函数不是凭空出现的,在链接的过程中就要连同对应库文件一起打包,最终可执行文件才能正常运行。

二、静态库

首先明确一点:无论动态库还是静态库,库中都不能有main函数

静态库,其中的代码会被拷贝进目标程序的。编译的过程中已经载入到可执行文件中,所以最后生成的可执行文件相对较大。

而动态库在实际没有被链接之前就会编译好,让可执行程序和动态库产关联。

静态库的名字一般是libxxx.a 在编译的时候直接编译进可执行文件中,运行环境中可以不用存在库文件,但是如果库文件更新了,可执行文件需要重新编译。

接下来我们制作静态库。

2.1 制作静态库

首先我们创建两个文件一个头文件和函数实现的文件,.h文件中写入声明,.c文件中是实现

然后编写源文件main.c和mymath.c、myprint.c一起进行编译并运行一下:

程序暂时跑起来没有问题,因为我们要制作库,所以将库中的文件都放到 mklib 文件夹中。

我们只需要将源文件编译为.o,然后将所有的.o文件与 main.o 链接起来即可形成一个可执行。

此时我们再将.o、.h文件放到main.c的目录下

然后我们再把main.c文件汇编为.o文件,再将所有的 .o 文件链接起来,就形成可执行程序了。

但是这个.o 文件太多了,我们想让所有的.o文件打包方便使用,那这个包就是静态库

Linux下使用ar命令进行操作静态库:

-- ar archivefile objfile -- 

参数:

  • archivefile:archivefile是静态库的名称
  • objfile: objfile是已.o为扩展名的中间目标文件名,可以多个并列

选项:

  • -r 将objfile文件插入静态库尾或者替换静态库中同名文件
  • -c 创建静态库文件
  • -r 将objfile文件插入静态库尾或者替换静态库中同名文件
  • -d 从静态库中删除文件objfile
  • -s 重置静态库文件索引

  • -v 创建文件冗余信息

注意!!!

  • 库的前缀必须是 lib(规定),静态库库的后缀必须是 .a(规定) 

这样的操作同样麻烦,我们还可以使用Makefile的方式来打包文件。

那接下来我们要将我们打包的这个库进行发布

库中有两个文件夹

  • include(库中的所有头文件) 
  • lib(对应的库文件)

此时makefile中添加一组伪方法,将头文件放在include文件夹中,库文件放到lib文件夹中:

接下来我们 make 生成一下:

2.2 使用静态库

接下来就是使用我们打包好的库了。

 有两种使用方法:

  1. 将库拷贝到系统的路径下(gcc的默认搜索路径)
  2. 直接指定库的位置

第一种方法:

头文件gcc的默认搜索路径是:/usr/include

库文件默认搜索路径:/lib64 或 /usr/lib64

所以我们将hello/include中的文件全部拷贝到 /usr/include下即可使用hello中的头文件;将hello/lib中的文件拷贝到/lib64目录下即可。

 接下来我们编译main.c文件

(报错表示函数未定义,即表示gcc找到了头文件,没找到库文件)

为什么没找到库文件,不是已经放在了默认的搜索路径下了吗?

因为我们编写的库属于第三方库,不是系统内部或gcc/g++的库,gcc不能直接查找到,所以我们在编译时要加上-l选项告诉gcc/g++具体链接哪个库。(注意,需要去掉前缀和后缀,前缀为lib,后缀为.a)

而刚刚将拷贝库到系统默认路径下,就叫做库的安装。

但是不推荐将第三方库添加到系统的默认路径下,因为会污染系统默认搜索路径,接下来我们赶快把文件删掉

第二种方法:

是直接在当前路径下找头文件,gcc中-I选项可以在指定路径下寻找头文件,-L指定库文件所在目录。

可是,gcc还是无法找到库中的函数,因为我们无法保证该目录下只有一个库,所以我们还是要指定-l选项告诉gcc/g++具体链接哪个库。

 ​​​​​​三个选项的具体介绍:

三、动态库

3.1 动态库的制作

还是以上面的mymath.c 和myprint.c举例。我们先将mymath.c 和myprint.c 汇编为.o文件。但是要添加一个特殊选项:

-fPIC 是创建与地址无关的二进制目标文件(pic,position independent code),这样动态库就形成了可以在系统中的任意位置进行加载,是为了能够在多个应用程序间共享。

然后我们将.o文件进行归档打包,使用 -shared 选项指定生成动态链接库。

使用 -shard 选项的作用是告诉 gcc 接下来链接的是动态库,而不是可执行文件(因为可执行文件要有main函数)

 在Makefile中生成动态库的编写:

接下来让我们使用Makefile生成动静态库吧~

3.2 动态库链接

 接下来我们使用一下动态库,一些选项的意义我们重温一下:

接下来我们运行可执行程序:

文件是生成了,可以无法执行。

因为在我们指定库的时候,只指定了 hello (去掉了前缀和后缀),而静态库和动态库都叫hello,那gcc 默认使用的是静态库还是动态库呢?

 如果我们将动态库移出,gcc使用静态库,a.out文件可以正常运行。

所以,如果只有静态库,那只能将静态库以静态链接的方式拷贝到可执行文件中。其他的动态库再使用动态链接。如果动静态库同时存在,默认使用动态库。

如果动静态库同时存在,想指定使用静态库,要添加-static选项。

好的,现在来讨论为什么这个动态库链接失败了。

2.3 动态库的加载

首先我们来了解以下动态库是如何被我们C语言程序所调用的。

  • 动态库是一个独立的库文件
  • 动态库和可执行文件,分批加载。

可执行文件和动态库都存放在磁盘中,当我们执行可执行程序时,a.out被加载到内存。

当我们要调用动态库时,内存就会加载动态库,然后通过页表将动态库的数据加载到地址空间的共享区供代码区的调用。

那为什么 a.out 显示找不到libhello.so库呢?

因为我们形成可执行程序时,添加那么多选项是给 gcc 设置的,可是当gcc执行结束,gcc退出之后,a.out 运行起来后就找不到 libhello.so文件了。

所以可以理解为,我们还需要提前在系统中配置,当我们使用到 libhello.so 时,让操作系统把libhello.so 加载到内存中。

方法一:

添加到系统的环境变量中LD_LIBRARY_PATH(LD表示加载,LIBRARY表示库,PATH表示路径)表示库加载时的搜索路径。

但是LD_LIBRARY_PATH这种方法有一个缺点,如果你退出终端的话,设置的LD_LIBRARY_PATH环境变量就被重置了

 因为这个环境变量是内存级的环境变量。所以这个方法不是很推荐。

方法二:

修改系统的配置文件,即修改动态库默认搜索的库文件。

第一步在/etc/ld.so.conf.d中自定义添加一个 conf 文件。

然后将动态库所在路径放在 .conf 文件中。、

最后使用 ldconfig 加载conf文件的文本信息即可。

这样a.out就也可以正常运行了。 
方法三:
在 /lib64 中建立一个软链接。
在系统默认的动态库目录中建立一个软连接指向我们的 libhello.so
sudo ln -s /home/wzh/23.1.6/uselib/output/lib/libhello.so /lib64/libhello.so
然后我们就可以正常运行了 ,其中动态库的链接地址就是/lib64/libhello.so

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

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

相关文章

想要做成一件事,就得有方法,怎么做到呢

想要完成一件事,没有详细的计划和方法是做不好的。如果没有计划,没有方案,没有策略,没有规划,没有方法,那就是一只无头苍蝇,到处乱撞,不能达到什么目的。想做成一件事,就…

后端人眼中的Vue(二)

三、Vue基本语法 3.1、展示数据 3.1.1、v-text 3.1.1.1、简介 ​ 和插值表达式({{}})一样,用于获取vue实例中data属性声明的数据。用法是在哪个标签上获取就直接在哪个标签上定义v-text或者是v-html。相当于javascript中的innerText。直接…

笔记:Android 系统架构

Android系统架构 1.应用层(System Apps) 系统内置应用程序和非系统应用程序,源码目录中的 packages 目录对应系统应用层 2.应用架构层(Framework) 为开发人员提供应用程序开发所需要的API,向下与c/c程序…

J-Tech Talk|跨模态视频检索进阶,一起探索CLIP模型的新天地!

J-Tech Talk由 Jina AI 社区为大家带来的技术分享围绕 Python 的相关话题工程师们将深入细节地讲解具体的问题分享 Jina AI 在开发过程中所积累的经验CLIP 模型在零样本图像分类、跨模态检索中效果拔群,它的出现同时推动了 NLP 和 CV 的发展,为解决许多实…

【自学C++】C++转义字符

C转义字符 C转义字符教程 在编程中有一些字符是打不出来的,比如换行,提示音,下一个制表位等等,于是程序语言的开发者就设计了转义序列(escape sequence)这种东西用来表达这些字符。 所谓转义&#xff0c…

jvm学习的核心(二)---运行时数据区概述

文章目录1.方法区( Method Area)2.堆(Heap)3.本地方法栈(Native Method Stacks)4.程序计数器(pc Register)5.虚拟机栈(Virtual Machine Stacks)运行时数据区 …

IDEA启动项目通过https进行访问,nginx配置https访问

一、IDEA启动项目通过https进行访问 1、获取证书 证书申请网络有很多方法,这里腾讯云的nginx版证书举列 2、证书转换 springboot是识别p12证书的,所有这里需要吧pem证书进行转换。转换工具OpenSSL-Win64(自行百度下载)下载完…

nodejs操作MySQL数据库

1、操作数据库的步骤 安装操作 MySQL 数据库的第三方模块(mysql)通过 mysql 模块连接到 MySQL 数据库通过 mysql 模块执行 SQL 语句2、安装与配置 mysql 模块 2.1、安装 mysql 模块 mysql 模块是托管于 npm 上的第三方模块。它提供了在 Node.js 项目中…

【Centos】服务管理、解/压缩、磁盘、进程管理相关命令

文章目录一、服务管理1 service2 chkconfig设置后台服务器的自启配置3 systemctl设置后台服务器自启配置防火墙关闭4 开关机重启5 搜索查找类6 locate快速定位文件路径二、压缩、解压1 gzip2 zip3 tar三、磁盘查看和分区类1 du2 df3 lsblk4 mount5 fdisk四、进程管理类1 iotop2…

Vue基础入门小demo——图片切换(初阶)

文章目录 📋前言 🎯demo介绍 🎯完整代码 🎯最终效果 🎯案例解析 📋前言 图片切换是一个很经典的Vue入门学习案例,在你学习完一些基本的v-指令后,你可以尝试去写一个简单的demo去…

吴恩达【神经网络和深度学习】Week3——浅层神经网络

文章目录Shallow Neural Network1、Neural Networks Overview2、Neural Network Representation3、Computing a Neural Networks Output4、Vectorizing Across Multiple Examples5、Explanation for Vectorized Implementation6、Activation Functions7、Why do you need Non-L…

年底固定资产盘点的正确招数

企业年终盘点的必要性 年终盘点的最终目的是摸清资产现状,掌握资产情况,为日常管理和来年采购预算做有力数据支撑,指导我们接下来应该怎么去做,对任何一家企业的管理都有着重要意义。 随着企业规模的扩大,员工和企业设…

SCI论文解读复现【NO.4】FINet:基于合成雾和改进YOLOv5的绝缘子数据集和检测基准(代码已复现)

此前出了目标检测算法改进专栏,但是对于应用于什么场景,需要什么改进方法对应与自己的应用场景有效果,并且多少改进点能发什么水平的文章,为解决大家的困惑,此系列文章旨在给大家解读发表高水平学术期刊中的SCI论文&am…

Redisson获取/释放分布式锁流程中使用的方法以及watchDog机制相关源码分析

Redisson获取/释放分布式锁原理以及watchDog机制相关源码分析使用到的重点类继承结构RedissonLockExpirationEntry获取锁的代码逻辑tryLock()tryLock(long waitTime, long leaseTime, TimeUnit unit)tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId)t…

变量作用域 和 多文件编程

变量作用域 目录&#xff1a;变量作用域概念typedef声明局部变量全局变量静态变量c存储类auto 自动存储类static 静态存储register 注册存储类extern 外部存储类多文件编程多文件编程概念步骤include <> 和 #include ""的区别防止头文件重复包含使用宏定义避免…

编写playbook ansible(5)

目录 题目&#xff1a; 1.按照要求定义以下变量。&#xff08;可以在多个位置下定义实现相应操作即可&#xff09; 2.编写任务模块在node1和node2主机中根据以上变量值创建对应文本文件以及用户名和安装软件包。 题目&#xff1a; 1.按照要求定义以下变量。&#xff08;可以…

2. 向量、向量索引、向量修改、向量运算

课程视频链接&#xff1a;https://www.bilibili.com/video/BV19x411X7C6?p1 本笔记参照该视频&#xff0c;笔记顺序做了些调整【个人感觉逻辑顺畅】&#xff0c;并删掉一些不重要的内容 系列笔记目录【持续更新】&#xff1a;https://blog.csdn.net/weixin_42214698/category_…

RMQ问题的ST算法实现(详细解析+图片演示+ST模板代码)

文章目录RMQ问题问题引入ST算法倍增ST递推公式查询任意区间的最值代码实现RMQ问题 RMQ&#xff08;Range Minimum/Maximum Query&#xff09;问题&#xff0c;又叫做区间最值问题&#xff0c;即对于长度为n的数列A&#xff0c;回答若干询问RMQ(A,i,j)(i,j<n)&#xff0c;返…

Triumph X 的 I LOVE KARACTER——NFT 系列来啦!

I LOVE KARACTER 是一个由韩国角色组成的元宇宙世界&#xff0c;其主要商业模式&#xff08;BM&#xff09;是一个基于角色的元宇宙模型代理&#xff0c;可以在元宇宙宣传中心使用选定的角色作为模型。 为庆祝与 The Sandbox 的合作&#xff0c;Triumph X 发布了 I LOVE KARACT…

Vivado 综合约束实用命令(更新中……)

引言本文记录一些用于 Vivado 综合约束的实用命令&#xff0c;欢迎补充~本文会适当结合一些特定设计进行解释&#xff0c;并结合相关工程进行具体的综合实现分析&#xff0c;不只是理论知识还有实际操作。演示使用的Vivado 版本&#xff1a;2018.3FPGA芯片型号&#xff1a;xc7a…