Linux文件:动静态库制作 动态库链接原理解析

Linux文件:动静态库制作 & 动态库链接原理解析(Centos7)

  • 前言
  • 一、静态库制作
    • 1.1 静态库制作方法
    • 1.2 实例
    • 1.3 用户使用模拟
      • 1.3.1 大致框架
      • 1.3 链接第3方库文件
  • 二、动态库制作
    • 2.1 动态库制作方法
  • 2.2 操作系统查找动态库
      • 2.2.1 直接安装到系统中
      • 2.2.2 形成软连接
      • 2.2.3 将路径添加到环境变量`LD_LIBRARY_PATH`
      • 2.3.4 更改系统动态库配置文件
  • 三、OS默认链接库种类 & 外部库使用(ncurses库)
    • 3.1 OS默认链接库种类
    • 3.2 外部库使用(ncurses库)
  • 四、动态库链接原理
    • 1)为啥程序运行时动态库也要加载到内存
    • 2)位置无关码
    • 3)虚拟地址和物理地址统一

前言

 下面博主以实现简单的计算器为例,分别将计算机功能函数(+、-、*)就如何打包生成动静态库,以及模拟使用者如何!
 实际上库中提供的通常是各种方法函数的目标文件xxx.o,并不会直接提供源代码!这样做原因在于,当你需要使用该库时不需要重新编译节省时间,厂家也能闭源。其二在于当不同模块使用不同的库函数功能时,仅需将不同模块的目标文件组合链接即可,这样可有防止对同一段代码多次编译!!
 除此之外,我们通常将头文件和实现方法放到特定路径下,方便用户查找安装!
(头文件放到xxx/include, 实现方法放到xxx/lib,和内核保持一致)

函数功能源代码:
【加法】:

#pragma once    
#include <stdio.h>    extern int Add(int, int); 
#include "Add.h"    int Add(int x, int y)    
{    return x + y;                                                                                                                                        
} 

【减法】:

#pragma once    
#include <stdio.h>    extern int Sub(int, int); 
#include "Sub.h"    int Sub(int x, int y)    
{    return x - y;                                                                                                                                        
} 

【乘法】:

#pragma once    
#include <stdio.h>    extern int Mul(int, int); 
#include "Mul.h"    int Mul(int x, int y)    
{    return x * y;                                                                                                                                        
} 

一、静态库制作

1.1 静态库制作方法

 静态库是指在编译期间将库的代码链接到可执行程序中。程序运行时将不在需要静态库!Linux中,静态库通常以.a结尾!
在Linux中使用ar -rc指令将多个目标文件进行打包生成静态库!其中ar指令是gun的归档工具,rc选项表示(replace and create)
 我们可以使用ar -tv查看静态库中的目录列表!t:列出静态库中的文件;v:verbose 详细信息。

[root@localhost linux]# ls
add.c add.h main.c sub.c sub.h
[root@localhost linux]# gcc -c add.c -o add.o
[root@localhost linux]# gcc -c sub.c -o sub.o
生成静态库
[root@localhost linux]# ar -rc libmymath.a add.o sub.o 
ar是gnu归档工具,rc表示(replace and create)
查看静态库中的目录列表
[root@localhost linux]# ar -tv libmymath.a 
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 add.o
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 sub.o
t:列出静态库中的文件
v:verbose 详细信息
[root@localhost linux]# gcc main.c -L. -lmymath
-L 指定库路径
-l 指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行。

1.2 实例

 下面我们将上述简单函数功能编译为目标文件,然后将其打包形成静态库libmymath.a。由于用于调用库时,我们不仅需要提供静态库(使用方法),还要提供相关头文件!所以我们将静态库和头文件放到指定目录下,方便查找。具体如下:

【Makefile内部实现】:

static-lib=libmymath.a //重命名$(static-lib):Add.o Sub.o Mul.o Div.o //生成静态库   ar -rc $@ $^    
%.o:%.c//将源文件依次生成对应的目标文件                                                                                                                                                  gcc -c $<  //模拟发布过程,将静态库和头文件放到指定路径下发布供用户使用  
.PHONY:output    
output:    mkdir -p mymath_lib/include    mkdir -p mymath_lib/lib    cp -f *.h mymath_lib/include    cp -f *.a mymath_lib/lib    .PHONY:clean//清理
clean:    rm -rf *.o *.a mymath_lib 

【运行结果】:
ng)

1.3 用户使用模拟

1.3.1 大致框架

 我们将上述简单计算器功能发布后,我们将mymath_lib拷贝到用户目录模拟用户的下载,然后将用户代码和函数包中的目标函数链接形成可执行程序!!
在这里插入图片描述
在这里插入图片描述


1.3 链接第3方库文件

链接第3方库时,gcc默认不认识,所以我们需要通过-l指令指定目标库名称,告诉编译器查找路径!

 由于编译器只会在当前目录和系统指定目录下查找头文件,显然我们需要通过-I选项指定头文件所在路径,告诉编译器还要在指定目录下查找头文件!!

【具体代码】:

在这里插入图片描述
在这里插入图片描述

Tips:

  • 链接代码gcc Testlib.c -I mymath_lib/include/ -lmymath -L mymath_lib/lib的意思是:gcc Testlib.c表示gcc编译Testlib.c文件,-lmymath告诉编译器将静态库mymath链接、 -L mymath_lib/lib表示告诉编译器动态库在./mymath_lib/lib目录下查找,-I mymath_lib/include/告诉编译器查找头文件时,不仅要在当前目录、系统指定路径下查找,还要再./mymath_lib/include下查找!!
  • 静态库制作到处就结束!我们能深刻感受到静态库本质就是:将库中源文件直接翻译为.o目标文件,然后打包
  • gcc默认动态链接的,但个别库如果你只提供.a,gcc也没办法,只能局部性的把你指定的.a进行静态链接,其他库动态链接。如果-static,就必须要.a

二、动态库制作

2.1 动态库制作方法

 动态库生成和静态库几乎一样,不同在于:

  1. 生成.o目标文件时,需要带fPIC选项,生成位置无关码(position independent code)
  2. 库名规则:libxxx.so
  3. 生成动态库时,使用gcc加shared选项。例如:gcc -shared -o libmymath.so *.o表示将所有.o文件生成共享库,而不是可执行文件!!
dy-lib=libmymath.so //重命名  $(dy-lib):Add.o Sub.o Mul.o Div.o //生成动态库   gcc -shared -o $@ $^    
%.o:%.c //将源文件依次生成对应的目标文件、位置无关码   gcc -fPIC -c $<    .PHONY:output // 发布,将头文件和共享库放到指定路径  
output:    mkdir -p mymath_solib/include    mkdir -p mymath_solib/lib    cp -f *.h mymath_solib/include    cp -f *.so mymath_solib/lib                                      .PHONY:clean    
clean:    rm -rf *.so *.o mymath_solib 
  • 链接生成可执行程序和静态库完全一样,就不多说了!
  • l:链接动态库,只要库名即可(去掉lib以及版本号)
  • L:链接库所在路径.
  • I:头文件所在路径

2.2 操作系统查找动态库

 代码链接动态库和链接静态库完全一样。但形成的可执行程序是无法直接运行的!
在这里插入图片描述
 原因在于,静态库在编译期间就将第3方库代码添加到了自己代码中,形成可执行程序后便和静态库无关。但对于动态库而言,形成的可执行程序和第3方库只是在地址上产生一些关联罢了。

 当可执行程序加载到内存时,第3方库也需要被加载到内存被调度。形成可执行程序时,我们需要告诉编译器动态库文件位置;同理进行执行可执行程序时,操作系统也需要知道动态库文件位置。
 但上述代码我们只是将相关信息告诉了gcc编译器,没有告诉OS!!ldd显示动态库的位置!

在这里插入图片描述

  • ldd是一个Linux命令行工具,全称为“Library Dependency Detector”。它用于检测共享库(动态链接库)文件依赖的其他库及其版本信息。当你运行一个程序时,ldd可以帮助你查看哪些库被该程序所链接,并显示它们的位置。

 让进程找到第3方库通常有如下4中方式:

2.2.1 直接安装到系统中

 系统默认会在/usr/include路径下查找所调用函数的声明,在/lib64查找实现方法(动态库)

 所以我们可以将计算器的功能函数头文件直接添加到/usr/include下,将动态库添加到/lib64即可!!或者将动态库的软连接添加到/lib64中!

tips:

  1. 对于官方提供的源码,只是最推荐的方式。而自己手写的不建议直接安装到系统中,会污染系统!
  2. 同一组库,如果同时提供静态库和动态库,gcc默认使用动态库。如果要链接静态库,只能把动态库删除。

2.2.2 形成软连接

 我们可以通过ln -s,形成 动态库对应的软连接。此时操作系统可以在当前目录下查找到实现方法!
在这里插入图片描述
在这里插入图片描述

2.2.3 将路径添加到环境变量LD_LIBRARY_PATH

 执行可执行程序时,操作系统是知道要执行那个动态库的,只是找不到罢了。

 所以我们可以将动态库的路径添加到环境变量LD_LIBRARY_PATH中即可。此时系统不仅会在默认路劲下查找,还会在环境变量LD_LIBRARY_PATH中查找动态库。
在这里插入图片描述

  • 但这是在内存级别导入,下一次登录时会消失

2.3.4 更改系统动态库配置文件

 在系统中,会存在/etc/ld.so.conf.d配置文件,这些文件是系统管理动态库加载的相关文件。每一个文件中存放的是动态库的查找路径!
在这里插入图片描述
 所以我们可以在该文件下,创建文件xxx.conf,并将动态库查找路径写入该文件后wq!退出即可(整个操作都要提权)
 然后使用ldconfig指令刷新文件即可!!
在这里插入图片描述

三、OS默认链接库种类 & 外部库使用(ncurses库)

3.1 OS默认链接库种类

 下面我们特意将同一段功能函数生成的动态库和静态库放在同一个目录下,并且执行可执行程序时,没有明确指定链接那个库。看看会发生什么?
在这里插入图片描述

  • 我们发现,当同时存在动态库和静态库时,gcc默认先链接动态库!!

3.2 外部库使用(ncurses库)

 我们可以使用下面指令安装ncurses库:

sudo yum install -y ncurses-devel

 当我们安装好库后,我们一定可以在系统中查找到ncurses库的相关头文件、动态库等信息:

在这里插入图片描述

【使用外部库】:

 上述我们已经看到了ncuses库的头文件和动态库都在系统的默认查找路径下。所以我们在使用第三方库时,只需要指明链接那个库即可!
在这里插入图片描述

四、动态库链接原理

1)为啥程序运行时动态库也要加载到内存

 代码编译为可执行程序时,是有格式的,在Linux下为ELF格式。
 ELF格式基本遵循进程地址空间那套,也存在正文区、已初始化静态区、未初始化静态区。除此之外,在可执行程序的开头存在类似于符号表的结构。在该结构中保存了程序运行时所用到的库函数地址(准确来说记录了(如printf)方法在哪个动态库中,在库中的地址)。所以程序编译为可执行程序后,只是和动态库产生关联(知道用到了那个库中的那个方法),我们将这称之为动态连接。

 可执行程序只是知道了方法在库中的地址,但并没有方法的实现。所以我们也需要将动态库加载到内存中!
在这里插入图片描述

2)位置无关码

 制作动态库生成.o目标文件时,需要带fPIC选项,生成位置无关码。那位置无关码如何理解?

 程序还未被加载,在编译完成后,程序中的函数、变量就已经变成地址了。编译的时候,对代码的编址基本遵循虚拟地址空间那一套。但在磁盘中,我们更喜欢将其称为逻辑地址,也就是基地址 + 偏移量(虚拟地址空间不仅是OS概念,编译器也是按照这样的规则进行编址,这样才能在加载的时候,进行磁盘文件到内存的映射)

 现代操作系统已经将逻辑地址和虚拟地址空间进行统一,所以磁盘上的可执行程序的编址方式为:基地址(全都为0)+ 偏移量(0~FFFFFFFF)。在编译原理中将这种编码方式称为平坦模式。

 上面这种编址方式就是典型的绝对编址,但在库中,我们采用相对编址。即函数方法的地址为该函数当前位置距离所在库起始位置的偏移量。OS会加载和维护众多的库,这些库最终会被映射到不同进程的共享区中(即这部分代码在OS中只维护一份,所以进程通过地址空间进行映射使用)。一个进程所依赖的库映射到共享区的地址不是固定的,但无所谓,我们采用的是相对编址。所以实际编码的程序中,函数编码后填充的地址是相对编址(即该方法在对应库中的偏移量)是固定不变的。所以我们将其称为与位置无关码。

在这里插入图片描述

3)虚拟地址和物理地址统一

 程序被编译器编译完成后,就是虚拟地址。而虚拟地址加载到内存后,就产生出物理地址。并且动态库需要被映射到进程地址空间的共享区中。库在共享区中的位置是未知的,但只要库加载好后,在虚拟地址空间中的未知就是固定的。所以我们在加载时,处理完成映射,还会将已经编码好ELF格式中的相关函数所依赖的库地址进行替换。

在这里插入图片描述
 所以在ELF程序被加载时,本身全是虚拟地址,加载到内存后,都应有对应的物理地址,然后即可填充页表。在ELF可执行程序的开头的符号表中保存了程序main函数的起始位置。所以在加载完成后,首先会将该信息(虚拟地址)填充待指令寄存器中。此时指令寄存器开始执行程序,通过页表将虚拟地址转化为物理地址,找到main函数入口。
 而正文所有代码是全部放在一起的,所以接下来就是将下一条指令读取到指令寄存器中,然后通过页表转化后得到的就是真实下一条指令内容。然后依次循环,代码“跑起来了”!!
在这里插入图片描述

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

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

相关文章

【进程篇】操作系统

再谈操作系统。 操作系统 概念 任何计算机系统都包含一个基本的程序集合&#xff0c;称为操作系统&#xff08;OS&#xff09;。 操作系统是一款进行软硬件管理的软件。 操作系统分为狭义和广义的操作系统。 笼统的理解&#xff0c;操作系统包括&#xff1a; 内核&#x…

day5,数据结构,单向,双向,循环链表

1】思维导图 2】完成单向循环链表的所有操作 【创建、判空、尾插、遍历、尾删、销毁】 创建&#xff1a; LooplinkPtr caerte() {LooplinkPtr h(LooplinkPtr)malloc(sizeof(Looplink));if(NULLh){printf("创建失败\n");return NULL;}h->len0;h->data0;h->…

threejs 建筑设计(室内设计)软件 技术调研之四 墙体添加真实门窗并保持原材质

运用threejs 开发 建筑设计&#xff08;室内设计&#xff09;软件 技术调研 四 墙体添加真实门窗并保持原材质 在线体验地址&#xff1a;http://47.96.130.245:8080/design/index.html 实现功能&#xff1a; 墙体材质变换后&#xff0c;添加真实门窗&#xff0c;墙体可保持原…

【JavaEE进阶】关于Maven

目录 &#x1f334;什么是Maven &#x1f332;为什么要学Maven &#x1f38d;创建一个Maven项目 &#x1f384;Maven核心功能 &#x1f6a9;项目构建 &#x1f6a9;依赖管理 &#x1f38b;Maven Help插件 &#x1f340;Maven 仓库 &#x1f6a9;本地仓库 &#x1f6a…

RabbitMQ 路由(Routing)通讯方式详解

在现代分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff09;是实现异步通信、解耦系统组件的重要工具。RabbitMQ 作为一个广泛使用的消息代理&#xff08;Message Broker&#xff09;&#xff0c;提供了多种消息传递模式&#xff0c;其中路由&#xff08;Ro…

uniapp自定义树型结构数据弹窗,给默认选中的节点,禁用所有子节点

兼容H5、安卓App、微信小程序 实现逻辑&#xff1a;给默认选中节点的所有子节点添加一个disabled属性&#xff0c;以此禁用子节点。 /components/sonTreeNode/sonTreeNode.vue 封装成组件 <template><view><view :class"[item,item.is_level1?pL1:item…

疾风大模型气象系统:精准到分钟,预见天气未来

精准到分钟,预见天气未来 在现代社会中,气象预报的精准度直接关系到人们的生活质量和生产效率。传统的天气预报虽然能为我们提供趋势性参考,但在短时突发天气变化的应对上仍有一定局限。而疾风大模型气象系统凭借其领先的技术和精细化的预测能力,为气象预报树立了新的标杆…

2024年合肥师范学院信息安全小组内部选拔赛(c211)WP

目录 前言MISC签到题_熟悉吗又来一道签到题文件包含 CRYPTO古典1古典2RSA webbaby_sql 前言 [HFNU 校级选拔] 已经结束&#xff0c;接下来一起了解下题目是怎么做的。 通过网盘分享的文件&#xff1a;ARCHPR_4.66.266.0_汉化绿色版.7z 链接: https://pan.baidu.com/s/1N_c0PJX…

15.初识接口1 C#

这是一个用于实验接口的代码 适合初认识接口的人 【CSDN开头介绍】&#xff08;文心一言AI生成&#xff09; 在C#编程世界中&#xff0c;接口&#xff08;Interface&#xff09;扮演着至关重要的角色&#xff0c;它定义了一组方法&#xff0c;但不提供这些方法的实现。它要求所…

3.使用SD卡挂载petalinux根文件系统

前言 说明为什么使用SD卡挂载petalinux根文件系统如何使用SD卡挂载根文件系统 配置根文件写入类型制作SD分区格式化SD卡将工程目录下的rootfs.tar.gz解压到SD EXT4分区 为什么使用SD卡挂载petalinux根文件系统 Petalinux 默认的根文件系统类型是 INITRAMFS&#xff0c;不能…

【Vulkan入门】16-IndexBuffer

TOC 先叨叨 上篇介绍了如何使用VertexBuffer传入顶点信息。两个多星期了我们一直在玩三个点&#xff0c;本篇介绍如何渲染更多的点。 在渲染前考虑一个问题&#xff0c;渲染一个三角形需要三个点&#xff0c;渲染两个相接的三角形需要几个点&#xff1f; 答案是6个点&#xf…

计算机工作流程

分析下面的计算机工作流程&#xff1a; 1.取数a至ACC&#xff1a;PC程序寄存器自增1&#xff0c;变成0&#xff08;可以理解为PC初始从-1开始自增&#xff09;&#xff1b;接着PC把当前指令的地址给到MAR&#xff08;地址寄存器&#xff09;&#xff1b;MAR拿到当前地址后&…

Restaurants WebAPI(二)——DTO/CQRS

文章目录 项目地址一、DTO1.1 创建Restaurant的Dto1.2 修改之前未使用Dto的接口1.2.1 修改GetRestaurantByIdUseCase1.2.2 修改IGetRestaurantByIdUseCase接口1.2.3 再次请求接口1.3 显示Dish List1.3.1创建DishDto1.3.2 在RestaurantDto里添加DishDto1.3.3 使用Include添加Dis…

202412月最新植物大战僵尸杂交版【V3.0.1】更新内容与下载

以下是对UI优化和新内容添加的摘要&#xff1a; UI优化摘要&#xff1a; 主界面重做&#xff1a;对游戏的主界面进行全面的设计更新&#xff0c;提升用户体验。商店重做&#xff1a;对游戏内的商店界面进行重新设计&#xff0c;以改善玩家的购物体验。选卡界面增加图鉴功能&a…

MCU驱动使用

一、时钟的配置&#xff1a; AG32 通常使用 HSE 外部晶体&#xff08;范围&#xff1a;4M~16M&#xff09;。 AG32 中不需要手动设置 PLL 时钟&#xff08;时钟树由系统自动配置&#xff0c;无须用户关注&#xff09;。用户只需在配置文件中给出外部晶振频率和系统主频即可。 …

服务器防火墙设置某个端口号只允许固定 ip地址访问

服务器防火墙设置某个端口号只允许固定 ip地址访问是运维常见的功能&#xff0c;今天我们分享一下&#xff1a; 一、Linux环境 1、firewall 方式 1&#xff09;允许特定 IP 地址访问 23 端口 sudo firewall-cmd --zonepublic --add-rich-rulerule family"ipv4" s…

Hexo Next主题集成百度统计

个人博客地址&#xff1a;Hexo Next主题集成百度统计 | 一张假钞的真实世界。 首先&#xff0c;需要在百度统计控制台新增自己的站点。 点击“新增网站”按钮&#xff1a; 按照要求输入相关信息并保存&#xff0c;页面跳转至代码获取页面。从代码页面中拷贝网站的ID&#xff1…

8K+Red+Raw+ProRes422分享5个影视级视频素材网站

Hello&#xff0c;大家好&#xff0c;我是后期圈&#xff01; 在视频创作中&#xff0c;电影级的视频素材能够为作品增添专业质感&#xff0c;让画面更具冲击力。无论是广告、电影短片&#xff0c;还是品牌宣传&#xff0c;高质量的视频素材都是不可或缺的资源。然而&#xff…

JumpServer开源堡垒机搭建及使用

目录 一,产品介绍 二,功能介绍 三,系统架构 3.1 应用架构 3.2 组件说明 3.3 逻辑架构 3.3 逻辑架构 四,linux单机部署及方式选择 4.1 操作系统要求(JumpServer-v3系列版本) 4.1.1 数据库 4.1.3创建数据库参考 4.2 在线安装 4.2.1 环境访问 4.3 基于docker容…

华为云计算HCIE笔记01

第一章 华为云Stack解决方案 2018年云栖大会马云提出的数据科学时代&#xff08;Data technology&#xff09;&#xff0c;相较于传统信息时代&#xff0c;技术的变更主要集中在过去我们更加看重的是传输&#xff0c;也就是传统的网络建设&#xff0c;随着目前国家网络建设的完…