Linux---动静态库

news2024/9/28 3:29:23

动静态库的相关概念

  • 静态库(.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.coloradmin.cn/o/1429516.html

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

相关文章

【HarmonyOS】鸿蒙开发之ArkTs初步认识——第2.1章

ArkTs简介 ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript&#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继承了TS的所有特性&#xff0c;是TS的超集。 以下图可以展示Js&#xff0c;TS&#xff0c;ArkTs的关系 ArkTs基础语…

比收费还好用,6个自学python必看网站

今天给大家分享几个自学python经常用到的网站&#xff0c;非常实用&#xff0c;建议收藏&#xff01; 1.中文版官方教程 https://docs.python.org/zh-cn/3/tutorial/errors.html#defining-clean-up-actions 你可以从这里下载Python、使用、学习Python。官方文档自然是最权威的…

【vue】报错 Duplicate keys detected 解决方案

错误描述&#xff1a;Duplicate keys detected. This may cause an update error.错误直译&#xff1a;检测到重复的键。这可能会导致错误。错误原因&#xff1a;有相同父元素的多个子元素的v-for有相同的key值。 解决方法&#xff1a; return:{dataList:[{name:张三&#xf…

10秒搞定!隔壁奶奶都能搞定的幻兽帕鲁、雾锁王国开服指南

最近《幻兽帕鲁》和《雾锁王国》非常火热&#xff0c;玩过的小伙伴们都说非常上头&#xff01;有跟朋友对战需求的小伙伴们可以通过本文拥有一台高性价比的专用服务器&#xff0c;随时可以用来跟朋友一起玩游戏&#xff01; 敲重点&#xff01;&#xff01;&#xff01; 步骤…

86.网游逆向分析与插件开发-物品使用-物品丢弃的逆向分析与C++代码的封装

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;物品使用的逆向分析与C代码的封装-CSDN博客 码云地址&#xff08;ui显示角色数据 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;7563f86877c…

关于bypassuac的探究——思考

我们所使用的几个api&#xff0c;如RegCreateKeyExA、RegSetKeyExA都是直接修改注册表的操作&#xff0c;这种操作应该被归类为敏感操作&#xff0c;那么这里会不会被杀软拦截呢&#xff0c;去测试一下 windows defender正常上线 获取到的权限也是bypassuac后的权限 再看一下…

备战蓝桥杯---搜索(BFS基础1)

如果DFS是时光回溯&#xff0c;那么BFS则是影子分身。 下面是它的定义&#xff1a; 下面直接看题&#xff1a; 十分经典&#xff0c;在这注意存的时候可以用i*mj的形式&#xff0c;可以当作模板&#xff0c;下面是AC代码&#xff1a; #include<bits/stdc.h> using name…

阿里云AI通义千问出bug,今天修复了,一切都是莫名其妙,国产AI又可以了?

怎么隔一天就好了&#xff1f; 引言我的处理感想再次提问AI代码结尾 引言 前天我的阿里云AI 通义千问 不是抽风了嘛 详情见 阿里云AI通义千问出bug,解决不了直接弃,开始对国产AI由支持变失望 就是我的一些对话莫名消失了 我的处理 我在这里进行了反馈 但是没有回应 我以为…

NUXTJS安装始终报错无法正常运行问题解决

近日在了解NuxtJS&#xff0c;按照官方给出方法进行安装后&#xff0c;不是报错&#xff0c;就是安装成功后运行不了。执行npm run dev后始终运行出错&#xff0c;判断肯定是对应版本问题&#xff0c;沿着这方向研究&#xff0c;最终运行成功了。 文档地址&#xff1a;安装 - …

后端——go系统学习笔记(不断更新中......)

数组 固定大小 初始化 arr1 : [3]int{1, 2, 3} arr2 : [...]int{1, 2, 3} var arr3 []int var arr4 [4]int切片 长度是动态的 初始化 arr[0:3] slice : []int{1,2,3} slice : make([]int, 10)len和cap len是获取切片、数组、字符串的长度——元素的个数cap是获取切片的容量—…

telnet笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、场景二、介绍1.测试端口2.访问百度3. 简单的爬虫 前言 最近telnet命令用的比较多&#xff0c;所以记录一下。 一、场景 ping应该是大家最常用的命令&…

算法学习——华为机考题库6(HJ36 - HJ40)

算法学习——华为机考题库6&#xff08;HJ36 - HJ40&#xff09; HJ36 字符串加密 描述 有一种技巧可以对数据进行加密&#xff0c;它使用一个单词作为它的密匙。下面是它的工作原理&#xff1a;首先&#xff0c;选择一个单词作为密匙&#xff0c;如TRAILBLAZERS。如果单词中…

51单片机学习笔记 --步进电机驱动说明

文章目录 工作原理代码编写驱动方式全步进驱动半步进驱动微步进驱动 工作原理 工作原理简要说明&#xff0c;和单片机一起配合使用的步进电机多为28BYJ28 五线四相步进电机&#xff0c;配合ULN2003驱动板进行控制&#xff0c;如图所示&#xff0c;对于扭矩、精度要求较高的还有…

HiSilicon352 android9.0 开机视频调试分析

一&#xff0c;开机视频概念 开机广告是在系统开机后实现播放视频功能。 海思Android解决方案在原生Android基础上&#xff0c;增加了开机视频模块&#xff0c;可在开机过程中播放视频文件&#xff0c;使用户更好的体验系统开机过程。 二&#xff0c;模块结构 1. 海思自研开机…

舔狗送女生生日祝福在线源码,可设置查看密码,附带搭建教程

&#x1f389; 女神专属生日祝福源码 &#x1f389; ✨ 在这个特别的日子里&#xff0c;想要为心中的女神送上一份独一无二的生日祝福吗&#xff1f;快来试试这款专为舔狗设计的生日祝福源码吧&#xff01; &#x1f48c; 这款源码不仅可以展示你满满的诚意和祝福&#xff0c…

C++ 日期类的实现

目录 前言 日期类中的成员函数和成员变量 日期类中成员函数的详解和实现 1.天数前后的判断 2.天数加减的实现 3.前置 && 后置 4.计算天数差值 前言 日期类的实现将综合前面所学的&#xff08;类的6个默认成员函数&#xff09;&#xff0c;进一步理解和掌握类的…

面试八股文(4)

文章目录 1.sleep和wait区别2.为什么调用start()方法会执行run()方法&#xff0c;为什么不能直接调用run()方法3.synchronized关键字4.并发编程的三个重要特性5.synchronized和volatile关键字区别6.ThreadLocal7.为什么要用线程池&#xff1f;8.实现Runnable接口和Callable接口…

数据图表方案,企业视频生产数据可视化

在信息爆炸的时代&#xff0c;如何将复杂的数据转化为直观、生动的视觉信息&#xff0c;是企业在数字化转型中面临的挑战。美摄科技凭借其独特的数据图表方案&#xff0c;为企业在数据可视化领域打开了一扇全新的大门。 一、数据图表方案的优势 1、高效便捷&#xff1a;利用数…

模拟钉钉官网动画

实现思路&#xff1a;利用粘性定位sticky&#xff0c;以及滚动事件实现。首先我们应该设置滚动动画开始位置和结束位置 &#xff0c;然后根据位置计算透明度或者transform&#xff0c;scale的值。 首先根据上述图线计算属性值&#xff0c;代码如下&#xff1a; function creat…

技术分享 | app测试中常用的Android模拟器

Emulator Emualor 是 Android Studio 自带的模拟器&#xff0c;是官方提供的工具&#xff0c;Android 开发最常使用的就是这一款。 它功能非常齐全&#xff0c;电话本、通话等功能都可正常使用。用户可以使用键盘输入&#xff0c;鼠标点击模拟器按键输入&#xff0c;甚至还可…