[Linux]动静态库

[Linux]动静态库

文章目录

  • [Linux]动静态库
    • 见一见库
    • 存在库的原因
    • 编写库
      • 模拟编写静态库
      • 模拟使用静态库
      • 模拟编写动态库
      • 模拟使用静态库
    • 库的加载原理
      • 静态库的加载原理
      • 动态库的加载原理
    • 库在可执行程序中的编址策略
      • 静态库在可执行程序中的编址策略
      • 动态库在可执行程序中的编址策略

见一见库

在Linux系统中,C语言标准库(C Standard Library)和其他常用库都被称为C库(C library),通常存放在/usr/lib/usr/lib64目录下:

image-20230907132423757

在Linux系统中,/usr/include目录下存放了许多头文件:

image-20230907132557509

  • Linux系统预装了C/C++的头文件和库文件,头文件提供方法的说明,库文件提供方法的实现,头文件和库文件有对应关系,需要组合使用。
  • 在程序编译形成可执行程序的过程中,头文件在预处理时被引入,库文件在链接时被链接 。
  • 在visual studio下安装开发环境时,不仅安装了编译器软件,还安装语言对应的头文件和库文件。
  • 编译器会自动根据用户的输入,查找头文件中的相关内容,实现语法提示的功能。
  • 编译器会自动根据用户的输入,不断地进行编译,实现语法报错的功能。

存在库的原因

编程语言将常用的功能添加到库中,以便于用户可以直接使用这些功能,提高开发的效率。比如printf,用户不需要在每次想打印数据时,编写一个用于打印数据的函数。

编写库

模拟编写静态库

进行库的编写前要说明一下:

  • 库分为动态库和静态库
  • Linux下静态库的命名规则:lib库名.a
  • Linux下动态库的命名规则:lib库名.so
  • 云服务下一般不会内置静态库
  1. 头文件和源文件的编写

编写头文件myadd.h和其对应的源文件myadd.c和头文件mysub.h和其对应的源文件mysub.c,各文件中的具体代码如下:

//myadd.h
#pragma once int my_add(int x, int y);//myadd.c
#include "myadd.h" int my_add(int x, int y)//一个简单的加法函数
{return x + y;
}
//mysub.h
#pragma once int my_sub(int x, int y);//mysub.c
#include "mysub.h" int my_sub(int x, int y)//一个简单的减法函数
{return x - y; 
}
  1. 将源文件编译成目标文件

使用gcc -c 源文件名将源文件编译成目标文件:

image-20230907162247874

  1. 打包成静态库

使用ar -rc lib库名.a 目标文件 将目标文件打包成静态库:

image-20230907162355901

  1. 模拟库文件目录结构

创建include目录将头文件移动到该目录中,创建lib目录将静态库移动到该目录中:

image-20230907162516768

  1. 将库打包成压缩包

使用tar -czf 目标压缩包名 源文件将库打包成压缩包:

image-20230907162735301

模拟使用静态库

  1. 将前文操作中打包好的静态库压缩包复制到某一目录下并解压来模拟库的下载过程

image-20230907164341754

  1. 在当前目录下创建源文件main.c并编写调用静态库的代码,具体代码实现如下:
#include <stdio.h>
#include "myadd.h"
#include "mysub.h"int main()
{int x = 20;int y = 10;printf("%d + %d = %d\n", x, y, my_add(x, y));printf("%d - %d = %d\n", x, y, my_sub(x, y));return 0;
}
  1. 将源文件main.c编译形成可执行程序:

image-20230907165017051

由于第三方头文件不在编译的目录下,需要-I 路径选项指明头文件路径,由于第三方库编译器不会自己查找和使用,需要-L 路径指明库文件路径名,需要-l 库名指明库名。

总结一下Linux系统下第三方库的使用:

  • 需要指定头文件路径和库文件的路径和名称
  • 如果没有将头文件和库文件安装到编译器搜素的默认路径下,用户必须指明对应选项:
    • 头文件路径 (-I 路径
    • 库文件路径(-L 路径
    • 库名(-l 库名
  • 安装头文件和库文件的本质是将文件拷贝至系统默认路径下。
  • 头文件和库文件安装后,编译时需要指明库名选项

模拟编写动态库

在模拟编写动态库时,沿用了前文中打包静态库使用的头文件myadd.h和其对应的源文件myadd.c和头文件mysub.h和其对应的源文件mysub.c。

  1. 将源文件进行编译

打包动态库时需要使用gcc -fPIC -c 源文件名将源文件编译成目标文件:

image-20230907173438259

  1. 将目标文件打包成动态库

使用gcc -shared -o lib库名.so 目标文件将目标文件打包成动态库:

image-20230907173847412

  1. 模拟库文件目录结构

创建include目录将头文件移动到该目录中,创建lib目录将静态库移动到该目录中:

image-20230907174146799

  1. 将库打包成压缩包

使用tar -czf 目标压缩包名 源文件将库打包成压缩包:

image-20230907174229960

模拟使用静态库

  1. 将前文操作中打包好的动态库压缩包复制到某一目录下并解压来模拟库的下载过程

image-20230907184457085

  1. 在当前目录下创建源文件main.c并编写调用静态库的代码,具体代码实现如下:
#include <stdio.h>
#include "myadd.h"
#include "mysub.h"int main()
{int x = 20;int y = 10;printf("%d + %d = %d\n", x, y, my_add(x, y));printf("%d - %d = %d\n", x, y, my_sub(x, y));return 0;
}
  1. 将源文件main.c编译形成可执行程序:

指定动态库头文件的路径、库文件的路径和库名后,编译器能够成功编译,由于是动态库,程序运行时需要OS根据程序内的动态库地址链接到动态库才能成功运行,但是OS无法找到该动态库,就造成了下图的情况:

image-20230907184623458

  1. 采用导入环境变量的方式使得程序运行(临时方案)

使用export LD_LIBRARY_PATH=LD_LIBRARAY_PATH:动态库所在目录路径将动态库路径导入环境变量,OS在运行程序时会从环境变量中的路径找到动态库并成功运行:

image-20230907185214813

解决第三方动态库OS查找不到的方法:

  1. 导入环境变量:使用export LD_LIBRARY_PATH=LD_LIBRARAY_PATH:动态库所在目录路径将动态库路径导入环境变量,环境变量会在重新打开shell时重新加载,因此是临时方案
  2. 在系统路径下建立动态库的软链接:使用sudo ln -s 动态库路径 /lib64/lib库名.so将动态库的软链接添加到系统路径下
  3. 修改配置文件:在/etc/ld.so.conf.d/路径下创建后缀为.conf文件,将静态库的路径写入该文件,然后使用sudo ldconfig使配置文件生效。

库的加载原理

静态库的加载原理

动态库的加载过程就是在形成可执行程序的链接过程中直接将静态库中的实现拷贝至可执行程序中。因此静态库十分占用资源(磁盘、内存、网络资源)。

动态库的加载原理

首先,使用动态库生成可执行程序时,在链接过程中,可执行程序中只会将代表库中方法的外部符号替换成对应地址,由于形成可执行程序中没有具体的实现,因此要想运行起来,操作系统做了一系列的工作,在程序被加载到内存中形成进程后,操作系统会为其维护进程控制块和进程地址空间和页表等:

image-20230908133542761

在进程运行到动态库中的方法后,操作系统会在页表中寻找映射,发现映射到内存中的只是一个对应地址而不是具体方法实现,因此操作系统寻找这个动态库,按照一定策略将动态库加载到内存中,然后操作系统会将加载到内存中的动态库映射给进程地址空间中在栈区和堆区之间的共享区:

image-20230908133642887

而后,每次该进程执行该库中方法时,只需要跳转到进程地址空间中的共享区,就可以完成程序的执行:

image-20230908133705099

另外,当该库被加载到内存中后,后续运行的进程需要执行该库方法时,不需要再在内存中加载库,而是直接创建共享区映射,然后使用库中方法。

库在可执行程序中的编址策略

静态库在可执行程序中的编址策略

形成可执行程序时,可执行程序中会存在逻辑地址,如果采用的是静态库,可执行程序中静态库的方法也会被编址,获得一个逻辑地址,在程序变成进程运行时,只需要根据逻辑地址进行跳转即可。

动态库在可执行程序中的编址策略

形成可执行程序时,可执行程序中会存在逻辑地址,如果采用的是动态库,可执行程序中动态库的方法也会被编址,但该地址是库中方法在库中从起始地址开始的偏移量,在制作动态库的获取目标文件的操作时,使用gcc添加-fPIC就是获取这个被称为与地址无关码的地址偏移量,在实际进程运行时,进程只需要等待库中方法被加载到内存中并被映射到共享区,然后利用共享区映射加上偏移地址完成运行。

说明一下:

  • gcc/g++编译器编译形成可执行程序时,默认使用动态库。
  • gcc/g++编译器编译形成可执行程序时,使用-static选项,将使用静态库。
  • gcc/g++编译器编译形成可执行程序时,部分动态库不存在,会采用动态库混合使用的方式。

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

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

相关文章

网络安全架构:建立安全架构方法的指导框架

01 关键发现 ■ 架构框架使用集体见解来创建最佳实践&#xff0c;指导用户考虑组织风险和业务环境。这些方法的改编和定制&#xff0c;将帮助组织从中获得最佳价值。 ■ 方法论提供了一种系统工程方法&#xff0c;使用业务输入和期望&#xff0c;来创建可重复、可跟踪&#xf…

16 “count(*)“ 和 “count(1)“ 和 “count(field1)“ 的差异

前言 经常会有面试题看到这样的问题 “ select count(*) ”, “ select count(field1) ”, “ select count(1) ” 的效率差异啥的 然后 我们这里 就来探索一下 这个问题 我们这里从比较复杂的 select count(field1) 开始看, 因为 较为复杂的处理过程 会留一下一些关键的调试…

第23章 信号量实验(iTOP-RK3568开发板驱动开发指南 )

在上面两个章节对自旋锁和自旋锁死锁进行了学习&#xff0c;自旋锁会让请求的任务原地“自旋”&#xff0c;在等待的过程中会循环检测自旋锁的状态&#xff0c;进而占用系统资源&#xff0c;而本章节要讲解的信号量也是解决竞争的一种常用方法&#xff0c;与自旋锁不同的是&…

无需麻烦验证,文字验证码一键通过

前言 文字验证码&#xff0c;简单易用&#xff0c;安全可靠&#xff01;不需要麻烦的图形识别。这种验证方式不仅方便快捷&#xff0c;而且能有效防止恶意攻击和机器人访问。无需担心复杂操作&#xff0c;只需几秒钟就能完成验证过程。保护您的个人信息和数据安全&#xff0c;…

【HTML专栏4】常用标签(标题、段落、换行、文本格式化、注释及特殊字符)

本文属于HTML/CSS专栏文章&#xff0c;适合WEB前端开发入门学习&#xff0c;详细介绍HTML/CSS如果使用&#xff0c;如果对你有所帮助请一键三连支持&#xff0c;对博主系列文章感兴趣点击下方专栏了解详细。 博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;HTML/CS…

在VScode中如何将界面语言设置为中文

VSCode安装后的默认界面是只有英文的&#xff0c;如果想用中文界面&#xff0c;那么就需要安装对应的插件&#xff0c;vscode插件可以从扩展中心去搜索并安装。 安装vscode后打开vscode&#xff0c;点击左侧的扩展按钮。 在搜索框中输入chinese&#xff0c;弹出chinese&#x…

2023高教社杯数学建模国赛C题思路解析+代码+论文

如下为C君的2023高教社杯全国大学生数学建模竞赛C题思路分析代码论文 C题蔬菜类商品的自动定价与补货决策 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差, 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&…

nmp ERR! code ERR SOCKET TIMEOUT nmp ERR!network npmSocket timeout(已解决)

当安装vue-cli时&#xff0c;出现超时错误 npm ERR! code ECONNRESET npm ERR! network This is a problem related to network connectivity npm ERR! code ECONNRESET npm ERR! network aborted npm ERR! network This is a problem related to network connectivity. npm E…

用go实现一个循环队列

目录 队列数组队列的“假溢出”现象循环队列三种判断队列空和满的方法无下标&#xff08;链式&#xff09;有下标&#xff08;顺序&#xff09;长度标记 go用顺序表实现一个循环队列队列的链式存储结构 队列 队列&#xff08;queue&#xff09;是只允许在一端进行插入操作&…

Python—下载清华大学鹏城实验室遥感数据

当想下载清华大学鹏城实验室10m土地利用数据的时候&#xff0c;发现他们的下载方式很奇怪&#xff0c;只能一页页的点名称全选 &#xff0c;然后批量下载&#xff0c;再一个个的加入浏览器下载&#xff0c;当一次下载过多就回卡顿和下载失败&#xff0c;所以就有了想用python进…

C++项目实战——基于多设计模式下的同步异步日志系统-①-项目介绍

文章目录 专栏导读项目介绍开发环境核心技术环境搭建日志系统介绍1.为什么需要日志系统2.日志系统技术实现2.1同步写日志2.2异步写日志 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导师&a…

Java23种设计模式之【单例模式】

目录 一.单例模式的起源&#xff0c;和应用场景 1.单例模式的前世今生&#xff01; 2.什么是单例模式&#xff1f; 2.1使用单例模式的注意事项 2.2如何理解单例模式&#xff1f; 2.3单例模式的优势以及不足&#xff01; 2.4使用场景 二.实现 1.实现思路 1.1创建一个 S…

基于SSM的网络游戏公司官方平台

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

python实现zscore归一化和minmax标准化

zscore归一化&#xff1a; minmax from sklearn import preprocessing from sklearn.preprocessing import StandardScaler import numpy as np# 数据 x np.array([[1.,-1.,2.],[2.,0.,0.],[0.,1.,-1.]]) print(----------------minmaxscaler标准化-------------) # 调用minma…

初始化一个 vite + vue 项目

创建项目 首先使用以下命令创建一个vite项目 npm create vite然后根据提示命令 cd 到刚创建的项目目录下&#xff0c;使用npm install安装所需要的依赖包&#xff0c;再使用npm run dev即可启动项目 配置 vite.config.js 添加process.env配置&#xff0c;如果下面 vue-route…

mysql课堂笔记 mac

目录 启动mac上的mysql 进入mysql mac windows 创建数据库 创建表 修改字段数据类型 修改字段名 增加字段 删除字段 启动mac上的mysql sudo /usr/local/mysql/support-files/mysql.server start 直接输入你的开机密码即可。 编辑 进入mysql mac sudo /usr/local…

Java认识异常(超级详细)

目录 异常的概念和体系结构 异常的概念 异常的体系结构 异常的分类 1.编译时异常 2.运行时异常 异常的处理 防御式编程 LBYL EAFP 异常的抛出 异常的捕获 异常声明throws try-catch捕获并处理 finally 异常的处理流程 异常的概念和体系结构 异常的概念 在Java中…

一篇文章教会你如何降低代码的冗余度——探索指针数组,数组指针,函数指针,函数指针数组,回调函数的奥妙

前言&#xff1a;人们总说指针是c语言的灵魂&#xff0c;是因为指针的使用技巧是“千姿百态”的&#xff0c;程序员可以通过指针来直接访问内存&#xff0c;这就赋予了它功能的多样性以及更多意想不到的编程技巧与方式&#xff0c;在本篇文章中&#xff0c;笔者就给大家带来指针…

嵌入式Linux驱动开发(LCD屏幕专题)(二)

一、结合APP分析LCD驱动程序 1、open app: open("/dev/fb0", ...) 主设备号: 29, 次设备号: 0 -------------------------------------------------------------- kernel:fb_open // fbmem.cstruct fb_info *info;info get_fb_info(fbidx);if (info->fbop…

VB:顺序查找

VB&#xff1a;顺序查找 Private Sub Command1_Click()Dim i%, m%Dim x(1 To 10) As SingleFor i 1 To 10x(i) Val(InputBox("请输入"))Next im seqSearch(x, 10)If (m 1) ThenPrint "已找到"ElsePrint "未找到"End If End Sub Function se…