Linux---动静态库

动静态库的相关概念

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
     

我们以如下的几个头文件和源文件作为测试用例

//add.h
#pragma once
#include<stdio.h>
int add(int,int);//add.c
#include "add.h"
int add(int x,int y)
{return x+y;
}//sub.h
#pragma once 
#include<stdio.h>
int sub(int,int);//sub.c
#include"sub.h"
int sub(int x,int y)
{return x-y;
}//mul.h
#pragma once
#include<stdio.h>
int mul(int,int);//mul.c
#include"mul.h"
int mul(int x,int y)
{return x*y;
}

一、为什么要有库?

如果我们要编译TestMain.c这个文件,我们一般的做法是将TestMain.c和其他源文件一起编译链接成一个可执行文件,具体操作如下

(之所以没有在gcc编译选项里带头文件,是因为编译器会在当前目录和指定目录下查找头文件

但是如果有其他的文件也需要生成可执行文件,并且也都包含了上面的几个头文件,那么我们就又需要将add.c  sub.c mul.c 这几个源文件重新进行编译,这样太浪费时间了,所以我们要先将头文件对应的源文件分别编译成为.o文件,然后我们只需要将我们需要的.o文件和我们需要编译的文件进行链接生成可执行文件即可,省去了源文件重复预处理、编译、汇编的过程

具体操作如下

(Makefile的语法不了解的,可以去了解一下,这里简单说一下这个文件的内容:用 .o文件生成All这个目标文件,但是当前目录没有.o文件,所以它会在文件中能不能找到规则推导出需要的.o文件,也就是第四、五行的作用,最后三行是用来删除生成文件的.o文件)

执行的效果如下

然后我们只要将TestMain.c文件编译成.o文件在和其他的.o文件链接即可生成可执行文件

具体操作如下

上面的操作虽然省略了源文件的重复预处理、编译、汇编的过程,但是这样写还是太费劲了,如果有很多的.o文件,我们手敲也很容易出错,所以我们可以将头文件和它们对应的.o文件打包起来,方便我们操作。也就是形成库。

二、如何生成静态库?

生成静态库
[root@localhost linux]# ar -rc libXXX.a xxx.o xxx.o
ar是gnu归档工具,rc表示(replace and create)


查看静态库中的目录列表
[root@localhost linux]# ar -tv libXXX.a
t:列出静态库中的文件
v:verbose 详细信息

所以静态库本质就是将库中的源代码直接翻译成为.o目标二进制文件,然后打包

注意:静态库文件有前缀lib和后缀.a,静态库的文件名是XXX的部分。

演示如下

现在我们只要有头文件,静态库和.c文件就能生成可执行文件,具体操作如下

有人可能对我们用的C语言的库不需要指明路径和名字感到奇怪,具体原因是因为我们写的叫第三方库,gcc不认识,本质是gcc的默认搜索路径中没有我们的库


我们用ldd命令去查看a.out这个文件依赖的动态库时,会发现没有libmymath.a这个库,为什么?因为libmymath.a是静态库,静态库中的内容会被拷贝到可执行文件中,所以可执行文件不需要找到静态库


这里讲一下gcc编译加不加-static选项的区别,加-static表示依赖的库都需要是静态库,否则报错,所以我们这里不加static选项,gcc默认用动态库,如果没有动态库就会选择用静态库,即遵循动态库优先的原则


当然,我们一般还会将头文件和库文件放到目录中,在需要用的时候就直接找这个目录即可

而我们正常所说的配置环境等,就是将该文件的压缩包下载,然后解压,将文件放到相应的系统目录下(比如头文件放到/usr/include,库文件放到/lib64等)然后我们就能正常使用了

当然我们这个写的是测试样例就不将它放到系统路径下了。那么如果不放到系统中,我们该怎么编译生成可执行文件呢?(我们现在要处理的文件如下)

如果我们直接编译就会报错,gcc找不到我们包含的头文件,因为头文件不在当前目录下(注意当前目录仅仅只有我们看到的文件,头文件在mymath_lib目录中,不属于当前目录!!!)

方法一:可以在包含头文件时,可以直接加上头文件文件所在的目录

方法二:把头文件所在路径告诉给编译器,让gcc也去我们给的路径下去找头文件

但是还是编译不通过,因为gcc找不到库文件,我们也需要把链接的库文件也告诉gcc

(如果我们将头文件和静态库安装到系统中,我们就不需要新增头文件和库的搜索路径了)

三、如何生成动态库?

生成动态库
shared:表示生成共享库格式
fPIC:产生位置无关码(position independent code)
库名规则:libxxx.so
示例:

[root@localhost linux]# gcc -fPIC -c sub.c add.c

[root@localhost linux]# gcc -shared -o libmymath.so *.o

通过指令,我们就能将头文件和动态库放到一个目录下

现在我们要处理的文件如下

看着和用静态库进行链接时一样,但是操作上会有所差异

生成可执行文件的语句和静态库一样,但是当我们运行a.out时,程序报错说找不到库,这就很奇怪了,明明在生成可执行文件的时候已经告诉gcc库文件在哪里了,为什么这里说找不到呢?

在回答这个问题之前,我们来回忆一下,动态库和静态库的区别:

  • 用静态库链接生成可执行文件,本质是将静态库拷贝到可执行文件中
  • 用动态库链接生成可执行文件,本质是让可执行文件在执行时去找动态库,从而调用库函数,也就是说动态库和可执行文件要同时被加载到内存
  • 总的还说,无论是动态库,还是静态库,都需要被加载到内存,程序才能执行,只不过动态库需要单独加载,并且当有多个程序运行时,动态库只需要加载一份,而静态库则是被包含在程序中一起被加载了多份

现在我们再回过头去回答一下上面的问题:我们在运行程序时,需要将链接的动态库也加载到内存,而我们刚刚只是告诉 gcc 动态库的路径,但是现在是shell命令行在运行程序,这两个是独立的进程,也就是说 gcc知道路径 != 命令行知道路径 ,所以我们还需要将动态库的路径告诉命令行。下面的结果也能说明这一点

这里提供四种处理方法:

方法一:将头文件和库文件直接拷贝到系统(推荐使用,但不推荐你自己写的库这么用)

(如果要自己测试,记得把头文件和库从系统中删除)

方法二:通过使用软连接,查找动态库

(在运行程序时,系统会在当前目录下找库,所以直接把动态库/软连接放在当前目录也可以)

方法三:使用往环境变量 LD_LIBRARY_PATH 添加路径的方式,让系统找到动态库,(系统除了会在默认路径找库,还会去LD_LIBRARY_PATH这个环境变量包含的路径中去查找)

(当然这只是内存级的修改,当我们重启Linux时,该环境变量就会恢复原样)

方法四:在/etc/ld.so.conf.d/目录下添加配置文件,向配置文件中写入动态库的路径即可

四、动态库加载原理

1、动态链接的程序在运行时,可执行程序和动态库都要被加载到内存

2、程序没有被加载之前,程序内部有地址吗?有的。当程序被编译成二进制目标文件时,程序中的函数名,变量名就会被换成二进制的地址,因为计算机只认识二进制,程序中的变量名,函数名是方便给人看的,一旦要交给机器也就没必要存在了。

=> 编译时,需要对代码进行编址,如何编址?基本遵循虚拟地址空间的规则,注意:虚拟地址空间,不仅仅是OS中的概念,编译器编译的时候,也要按照这样的规则编译程序,这样才能在加载时,形成从磁盘到内存的对应关系,即编址方式和内存管理进程地址的方式相同,方便映射。

编译时的虚拟地址,又称为逻辑地址(采用基地址+偏移量的方式) 这里基地址为0

3、编址有两种:绝对编址,相对编址,动态库采用相对编址。(我们写的可执行程序是绝对编址,因为在虚拟地址空间中代码区的位置是固定的)

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

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

相关文章

2024 年 11 款值得收藏的 iPhone 数据恢复软件和应用

数据丢失是任何人都无法承受的&#xff0c;因为它对每个人都至关重要。但是数据丢失的原因有很多&#xff0c;一些常见的原因是数据意外删除、设备被盗、iOS 越狱、硬件损坏、病毒感染等。我们列出了 iOS 的顶级恢复工具&#xff0c;其中将帮助您方便地恢复数据。 这是 11 款最…

k8s之安装部署及kuboard发布应用

目录 环境准备 系统规划 配置免密 将桥接的IPv4流量传递到iptables的链 系统基础配置 安装docker 安装docker及基础依赖 配置docker的仓库下载地址 部署k8s 添加阿里云的k8s源 安装kubeadm&#xff0c;kubelet和kubectl 初始化masteer节点 部署node节点 部署flanne…

docker相关问题解决(file exists、not a directory

背景 以下环境为wsl file exists 缓存没删干净 docker-compose down -v not a directory flags: 0x5000: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? 明明我确定报错指示的位置就是文件而不是文件夹...相当神奇的错误 …

MyBatis-Plus的saveBatch批量插入为何效率很低耗时长详解及解决方案

MySQL数据库 针对MySQL数据库saveBatch批量插入效率比较低&#xff0c;是比较好解决的&#xff0c;一般都是由于数据库连接url上没有配置批量操作的属性&#xff0c;只需要在url上加上如下属性即可&#xff1a; rewriteBatchedStatementstruejdbc:mysql://数据库地址/数据库名…

【C++初阶】--入门基础(二)

目录 一.C输出与输入 二.缺省参数 1.概念 2.缺省参数分类 (1) 全缺省参数 (2)半缺省参数 三.函数重载 1.概念 2.C支持函数重载的原理--名字修饰 四.引用 1.概念 2.语法 3.引用的特性 (1)引用在定义时必须初始化 (2)引用时不能改变指向 (3)一个变量…

java—vector文档与集合框架

一.构造器 Constructor and DescriptionVector() 构造一个空向量&#xff0c;使其内部数据数组的大小为 10 &#xff0c;标准容量增量为零。 Vector(Collection<? extends E> c) 构造一个包含指定集合元素的向量&#xff0c;按照集合的迭代器返回的顺序。 Vector(int in…

“挖金矿”系列:Python的标准库的快速调用

Python的标准库包含了众多功能强大的模块&#xff0c;以下是一些常用模块及其主要功能&#xff1a; os&#xff1a;提供了与操作系统交互的各种功能&#xff0c;如文件和目录操作&#xff08;创建、删除、重命名等&#xff09;、环境变量读取、进程管理等。 示例&#xff1a; …

《Python基础教程》05 算术运算符

05 算术运算符 《Python基础教程》05 算术运算符 算术运算符 在Python中&#xff0c;使用以下符号进行算术运算&#xff1a; &#xff1a;加法-&#xff1a;减法*&#xff1a;乘法/&#xff1a;除法&#xff0c;得到的结果是一个小数%&#xff1a;取余&#xff0c;得到的是…

安装Debian 11 留档

在清华、中科大、山大 163等镜像里&#xff0c;竟然那没有找到debian11的安装盘&#xff0c;只有10或者只2&#xff0c;真奇怪&#xff0c;后来是在csdn发现有人推荐了华为源&#xff0c;才找到下载的地方。 https://repo.huaweicloud.com/debian-cd/11.0.0/amd64/iso-cd/ 我…

UE 代码构建(BuildSystem)与源码编译相关

年底了&#xff0c;把之前的草稿文章整理一下&#xff0c;整理好的发出来 UnrealBuildTool简介 参考&#xff1a;https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/BuildTools/UnrealBuildTool/ UE里的项目代码、包括UE本身的源码&#xff0c;都是划分成一个…

flv视频格式批量截取封面图(不占内存版)--其他视频格式也通用

flv视频格式批量截取封面图&#xff08;不占内存版&#xff09;--其他视频格式也通用 需求&#xff08;实现的效果&#xff09;功能实现htmlcssjs 需求&#xff08;实现的效果&#xff09; 批量显示视频&#xff0c;后端若返回有imgUrl,则直接显示图1&#xff0c; 若无&#xf…

Socket套接字类编译测试

目录 类设计 类实现 测试 测试服务器 测试客户端 测试结果 这一节相当于整合了之前的一些东西&#xff0c;重新过了一遍&#xff0c;这个就显得相对之前的版本更加完善一点 类设计 // 套接字类 #define MAX_LISTEN 1024 class Socket {private:int _sockfd;public:Socke…

PHP面试知识点--echo、print、print_r、var_dump区别

echo、print、print_r、var_dump 区别 echo 输出单个或多个字符&#xff0c;多个使用逗号分隔无返回值 echo "String 1", "String 2";print 只可以输出单个字符返回1&#xff0c;因此可用于表达式 print "Hello"; if ($expr && pri…

闲人闲谈PS之五十三——离散制造中的魔鬼--物料套裁

惯例闲话&#xff1a;最近和老婆大人商议买车事宜&#xff0c;闲人以为会陷入买油车还是电车的纠结&#xff0c;没想到老婆大人无比坚定&#xff0c;买电车。在买车这方面&#xff0c;老婆的想法居然比闲人超前。闲人对车定位在代步工具&#xff0c;2年前&#xff0c;对车还是印…

SAP下载word

事务代码&#xff1a;STRANS 启动转换器 步骤 1. 将参数填入模板&#xff0c;并另存为word 2003 xml文档 2.使用网页打开xml文档&#xff0c;并将xml拷贝到转换器tt:template中&#xff0c;添加参数 3.替换参数&#xff0c;部分xml可能存在错误或者跑偏根据实际情况检查修改 …

为什么游戏APP选择不上架?

游戏APP选择不上架的原因有很多&#xff0c;主要包括以下几个方面&#xff1a; 节省成本&#xff1a;自己运营游戏可以省去向应用商店缴纳的分成费用&#xff0c;降低运营成本。避免与竞争对手比较&#xff1a;有些公司不希望自己的游戏在应用商店中与竞争对手的产品进行比较&…

洛谷 P1980 [NOIP2013 普及组] 计数问题

题目背景 NOIP2013 普及组 T1 题目描述 试计算在区间 1 到 n 的所有整数中&#xff0c;数字 x&#xff08;0≤x≤9&#xff09;共出现了多少次&#xff1f;例如&#xff0c;在 1 到 11 中&#xff0c;即在 1,2,3,4,5,6,7,8,9,10,11 中&#xff0c;数字 1 出现了 4 次。 输入…

Ubuntu18.04安装Matlab流程笔记

提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 Ubuntu18.04 安装Matlab流程 下载安装包和破解文件安装Matlab注册并运行 下载安装包和破解文件 matlabR2019A源码 提取码:2ztb 下载的Linux matlab2018a文件夹内有三个文件&#xff1a; # 解压Matlab201…

<网络安全>《15 移动安全管理系统》

1 概念 移动安全管理系统&#xff0c;MSM&#xff0c;Mobile security management,提供大而全的功能解决方案&#xff0c;覆盖了企业移动信息化中所涉及到安全沙箱、数据落地保护、威胁防护、设备管理、应用管理、文档管理、身份认证等各个维度。移动安全管理系统将设备管理和…

freeswitch对接FunASR实时语音听写

1、镜像启动 通过下述命令拉取并启动FunASR软件包的docker镜像&#xff1a; sudo docker pull \registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.7 mkdir -p ./funasr-runtime-resources/models sudo docker run -p 10096:10095 -i…