Linux之动态库和静态库

news2024/9/24 9:21:28

文章目录

  • 前言
  • 一、动态库和静态库概念
  • 二、库
  • 三、制作静态库
    • 1. 创建Makefile:
    • 2.打包库
    • 3.使用库
      • 总结
      • 注意
      • 库的安装
  • 四、制作动态库
      • 总结
  • 五、动静态库的加载
  • 总结


前言


一、动态库和静态库概念

在之前的文章中,介绍过动静态库的概念,因此这次我们只是简单回顾一下。

  1. Linux中的库分为动态库和静态库。
  2. 静态库(.a):库文件以.a为后缀,程序在编译链接时把库的代码链接到可执行文件中(将需要的库函数拷贝一份到代码中)。程序运行时不需要再跳转到静态库。
  3. 动态库(.so):库文件以.so为后缀,程序在运行时才去链接动态库的代码(运行时跳转到动态库中,在动态库中执行库函数)。多个程序共享库的代码。
  4. 链接的本质:我们调用库函数时是如何与标准库联系的。
  5. 库的名称:去掉前缀lib和后缀'.a/.so'剩下的就是库名称,例如:libc.so就是C库。
  6. gcc/g++,在编译时默认使用动态链接,如果想要生存静态链接,我们要带上-static

二、库

我们了解了动态库和静态库的相关概念,但是我们还是不理解库是个什么东西。
假设,我们做了一个小程序,只希望提供给用户小程序的功能,不希望暴露我们的源码。我们可以选择给用户提供我们的.o可重定位目标二进制文件(gcc -c 文件)与头文件。让用户使用我们提供的.o文件和.h文件进行链接即可。(在编译时,只需要把源文件编译成.o文件,再将其链接即可形成一个可执行程序,因此我们可以直接提供,o文件)。
文件add.c

  1 #include"add.h"
  2 int add(int x, int y)
  3 {
  4         printf("ADD: %d + %d = ?\n",x, y);
  5         return x + y;
  6 }

文件mul.c

  1 #include"sub.h"
  2 int sub(int x, int y)
  3 {
  4         printf("SUB : %d - %d = ?\n",x, y);
  5         return x - y;
  6 }

文件add.h

  1 #pragma once
  2 #include<stdio.h>
  3 extern int add(int, int);

文件sub.h

  1 #pragma once
  2 #include<stdio.h>
  3 extern int sub(int, int);

文件main.c

  1 #include"add.h"
  2 #include"sub.c"
  3 int main()
  4 {
  5         int a = add(1, 2);
  6         int ret = sub(10, a);
  7         return 0;
  8 }

运行:
在这里插入图片描述
我们给用户同时提供.o文件(方法的实现)以及.h文件(方法的声明),用户就可以链接形成可执行程序。
但是如果我们有很多.c文件,难道我们要将所有的.c文件全部编译成.o文件,然后一个一个提供给用户吗?未免太过麻烦。我们可以把编译得到的所有.o文件打包,直接给对方提供一个库文件即可。把多个.o文件打包成一个文件,这个文件就是
库的本质就是.o文件的集合

三、制作静态库

首先,如果写一个库是否需要写main函数?
答案是不需要,因为库是提供给别人使用的,用户自己写的main函数会与库函数起冲突。我们需要在编写库的角度和使用库的角度同时考虑来制作库:
编写库:

1. 创建Makefile:

  1 libmymath.s:add.o sub.o
  2         ar -rc $@ $^
  3 add.o:add.c
  4         gcc -c add.c -o add.o
  5 sub.o:sub.c
  6         gcc -c sub.c -o sub.o
  7 .PHONY:output
  8 output:
  9         mkdir -p mylib/include
 10         mkdir -p mylib/lib
 11         cp -f *.a mylib/lib
 12         cp -f *.h mylib/include
 13 .PHONY:clean
 14 clean:
 15         rm -f *.o libmymath.a

2.打包库

将文件编译为.o文件
or命令:把所有.o文件打包起来,or作用是归档
-rc(replace和create):例如

or -rc libmymath.a add.o sub.o

output:发布。交付库,将库文件.a以及配套的头文件都交给用户。
在这里插入图片描述

在这里插入图片描述
将mylib打包起来。
此时,用户如果需要我们的库,只需要将mylib.tgz拷贝过去:

cp mylib.tgz .../test

然后解压

tar xzf mylib.tgz

在这里插入图片描述
安装本质就是拷贝。

3.使用库

文件main.c

  1 #include"add.h"
  2 #include"sub.h"
  3 int main()
  4 {
  5         printf("1 + 2 = %d",add(1, 2));
  6         printf("10 - (1 + 2) = %d",sub(10,3));
  7         return 0;
  8 }

在这里插入图片描述

  1. 为什么会找不到头文件?
    编译器搜索头文件,默认是在当前目录下搜索,在系统默认指定路径下搜索。虽然此时的mylib在当前路径下,但是头文件太深了(文件不在本层),编译器找不到头文件,因此我们需要给gcc指定路径(-I)。指明在当前路径下mylib目录中查找。
gcc -o mymath main.c -I ./mylib/include

在这里插入图片描述

  1. 此时出现了新问题——找不到库函数的实现。
    我们在形成可执行程序时,库文件要使用,必须知道库所在的路径,而系统中库默认路径为/lib64。因此,我们要告诉gcc,它要链接的库的路径在哪里(-L)。

如果要链接第三方的库,必须去指明库的名称(注意:指明时要去掉前缀和后缀!!!),也就是说,一定要告知是哪一个路径下的哪一个库,即使该路径下只有一个库也要明确告知gcc是哪一个库(我们以前写代码的时候,从未指明库的名称,是因为gcc/g++默认帮我们填写了,因为它们可以识别C/C++自带的库。但是自己写的库或者第三方库必须要写明)。

在这里插入图片描述
结果正确!!!

总结

  1. -I 指明头文件的路径
  2. -L 指明库文件的搜索路径
  3. -l 指明要链接哪个库,要带上库的名称(去掉前缀和后缀)

注意

gcc默认是动态链接,对于一个特定的库,究竟是动态链接还是静态链接取决于提供的是动态库还是静态库。

库的安装

将库安装到系统头文件下。
gcc对头文件的默认路径为/usr.include,对于库文件的默认路径是/lib64

sudo cp 头文件(包含路径) /usr/include/
sudo cp 库文件(包含路径) /lib64/

但是,不太推荐将第三方库写入系统默认路径,因为第三方库未经过测试会污染系统内其它文件。

四、制作动态库

首先我们将文件全部编译成.o文件,这里与制作静态库不同的是,需带上-fPIC,形成位置无关码:

gcc -c -fPIC add.c

什么是位置无关码?有位置无关码的目标二进制文件有什么特殊?
静态库采用的是绝对编址;
动态库采用的是相对编址。即,指定函数的地址采用相对编址(库中的偏移地址 + 段起始地址)。
在这里插入图片描述

动态库打包:

gcc -shared -o libmymath.so add.o sub.o

在这里插入图片描述
在这里插入图片描述
使用动态库:
在这里插入图片描述
我们试着运行mymath:
在这里插入图片描述
为啥运行不了呢(为啥找不到库)?
我们的确已经告诉了gcc:我们的库文件的路径以及库名称,但是我们编译完成后,程序与gcc还有关系吗?(程序是由gcc运行的吗?)显然此时程序与gcc无关。接下来的程序运行是由OS来进行的。
动态库是程序运行时才进行链接的,而程序的运行是OS和shell来执行的,因此OS和shell也需要知道库文件的路径及名称。但是我们自己制作的库并不在系统的默认路径下,因此OS无法找到库,就无法正常执行程序。那么我们要如何让OS找到我们的库呢?
我们可以将库路径添加到环境变量LD_LIBRARY_PATH中。例如:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/Jinger/dir1/mylib/lib/libmymath.so

在这里插入图片描述
直接运行:
在这里插入图片描述
注意:我们自己定义的环境变量只是本次登录有效,如果想永久有效只能修改环境变量的配置。当然,我们还有其它办法:

  1. 配置文件(/etc/ld.so.conf.d/):动态库进行搜索时可以通过自己定义conf文件找到动态库。

  2. 建立软链接,直接找到对应的库。
    把对应的动态库建立在系统的目录下。

总结

  1. 拷贝.so文件到系统共享库(动态库)路径下,默认路径是/usr/lib
  2. 更改LD_LRBRARY_PATH
  3. ldconfig配置/etc/ld.so.conf.d/,ldconfig更新
  4. 创建软链接

五、动静态库的加载

静态库不需要加载,静态库是将代码直接拷贝到程序中,因此内存中的代码和数据可能会存在多分,造成空间浪费。把静态库代码拷贝到内存中的代码区:
在这里插入图片描述

动态库通过fPIC形成位置无关码,采用相对编址的方式,在程序链接时将对应库中的偏移量添加到程序中,库函数在程序运行时加载进来,经过页表,把库映射到虚拟地址空间后(共享区),库就具有了起始地址。通过起始地址和偏移地址,就可以找到要调用的库函数。
在这里插入图片描述

系统层面上会维护动态库的起始地址(虽然刚刚加载时不能确定起始地址,因为共享区是由OS分配的,但是加载完毕就不会改变了),直接建立页表与内存的映射,就可以直接跳转访问了。所以动态库加载一次就可以被多个进程共同使用。
动态库相对于静态库更节省内存,静态库由多个程序使用相同的库函数,加载到内存中就会导致内存中有多份重复的库函数代码,而动态库则是多个程序共用一份动态库,不会导致出现重复的库函数代码,就节省了内存空间。


总结

以上就是今天要讲的内容,本文介绍了Linux中的动静态库的相关概念。本文作者目前也是正在学习Linux相关的知识,如果文章中的内容有错误或者不严谨的部分,欢迎大家在评论区指出,也欢迎大家在评论区提问、交流。
最后,如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!

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

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

相关文章

微信小程序面试题汇总

HTML篇CSS篇JS篇Vue篇TypeScript篇React篇前端面试题汇总大全&#xff08;含答案超详细&#xff0c;HTML,JS,CSS汇总篇&#xff09;-- 持续更新前端面试题汇总大全二&#xff08;含答案超详细&#xff0c;Vue&#xff0c;TypeScript&#xff0c;React&#xff0c;Webpack 汇总篇…

虎牙在全球 DNS 秒级生效上的实践2

博主介绍&#xff1a;✌全网粉丝4W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战、定制、远程&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面…

pytest使用手册

1. pytest寻找测试项的具体规则 如果未指定命令行参数&#xff0c;则从pytest命令运行的当前目录开始收集。如果在命令行参数中指定了目录、文件名则按参数来寻找。寻找过程会按照目录层层递归&#xff0c;在这些目录中&#xff0c;搜索 test_*.py 或 *_test.py 文件。从这些文…

GPT-4变笨引爆舆论!文本代码质量都下降,OpenAI刚刚回应了降本减料质疑

梦晨 克雷西 发自 凹非寺 量子位 | 公众号 QbitAI 大模型天花板GPT-4&#xff0c;它是不是……变笨了&#xff1f; 先是少数用户提出质疑&#xff0c;随后大量网友表示自己也注意到了&#xff0c;还贴出不少证据。 有人反馈&#xff0c;把GPT-4的3小时25条对话额度一口气用完…

PDF怎么转换成WORD?分享这几个方法给大家!

PDF怎么转换成Word&#xff1f;在我们的工作过程中&#xff0c;经常会使用到PDF文件、Word文件等等。而在很多时候&#xff0c;需要根据工作需求&#xff0c;将各种文件进行格式转换&#xff0c;例如将PDF文件转换成Word格式&#xff0c;从而满足我们对文件进行编辑、更改等需求…

learn C++ NO.8——初识模板(函数模板、类模板)

文章目录 引言1.泛型编程1.1.什么是泛型编程&#xff1f; 2.函数模板2.1.什么是函数模板2.2.为什么需要函数模板2.3.函数模板格式2.4.函数模板实现原理2.5.函数模板的实例化 3.类模板3.1.类模板定义格式3.1.1.类模板语法3.1.2.模板类的定义 3.2.模板类的实例化 引言 现在是北京…

Hadoop之Yarn概述

Hadoop之Yarn概述 Yarn是什么Yarn基础架构Yarn工作机制回顾HDFS、YARN、MapReduce三者关系Yarn调度器和调度算法先进先出调度器&#xff08;FIFO&#xff09;容量调度器&#xff08;Capacity Scheduler&#xff09;公平调度器&#xff08;Fair Scheduler&#xff09; Yarn常用命…

SpringBoot整合SpringSession实现分布式登录详情

目录 Session 共享为什么服务器 A 登录后&#xff0c;请求发到服务器 B&#xff0c;不认识该用户&#xff1f;解决方案SpringBoot整合SpringSession实现分布式登录 Session 共享 比如两个域名&#xff1a; aaa.yupi.combbb.yupi.com如果要共享 cookie&#xff0c;可以种一个…

事件机制原理剖析及实际业务应用说明

什么是事件&#xff1f; 一个特定的场景发生了一个特定的情况就是一个事件。 事件在设计中的作用 为对象之间解耦。 举例 现有用户中心和消息中心。 目前&#xff0c;有一个用户注册的场景&#xff0c;此场景要求用户注册成功后要给用户发送多渠道欢迎通知&#xff08;微信、…

(11) XGBoost

文章目录 1 简要介绍2 梯度提升树2.1 提升集成算法&#xff1a;重要参数n_estimators2.2 有放回随机抽样&#xff1a;重要参数subsample2.3 迭代决策树&#xff1a;重要参数 η \eta η 3 XGBoost的智慧3.1 选择弱评估器&#xff1a;重要参数booster3.2 目标函数&#xff1a;重…

SpringCloud_微服务基础day2(Eureka注册中心:服务注册与发现

p6:Eureka简介与依赖导入 前面我们了解了如何对单体应用进行拆分&#xff0c;并且也学习了如何进行服务之间的相互调用&#xff0c;但是存在一个问题&#xff0c;就是虽然服务拆分完成&#xff0c;但是没有一个比较合理的管理机制&#xff0c;如果单纯只是这样编写&#xff0c…

HBase:(三)HBase API

HBase:(一)安装部署_只爱大锅饭的博客-CSDN博客hbase部署安装https://blog.csdn.net/qq_35370485/article/details/130988364?spm1001.2014.3001.5501 1.创建maven项目 2.添加依赖 <dependency><groupId>org.apache.hbase</groupId><artifactId>hba…

【鲁棒】对信息不完整的 DSGE 模型进行鲁棒预测(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

使用 TiUP 部署 TiDB 7.1.0 集群

使用 TiUP 部署 TiDB 7.1.0 集群 参考官方文档 1. 下载社区版安装包 TiDB 社区版 TiDB 7.1.0 为长期支持版本 (Long-Term Support Releases, LTS)。 TiDB-community-server 软件包 TiDB-community-toolkit 软件包 wget https://download.pingcap.org/tidb-community-ser…

【论文阅读】Attention-based Deep Multiple Instance Learning

misc{Ilse_Tomczak_Welling_2018, title{Attention-based Deep Multiple Instance Learning}, journal{International Conference on Machine Learning}, author{Ilse, Maximilian and Tomczak, JakubM. and Welling, Max}, year{2018}, month{Jul} }1、摘要与引言 本文…

CodeForces..走路的男孩.[简单].[时间间隔]

题目描述&#xff1a; 题目解读&#xff1a; 数学上的遛狗问题。 大意就是需要遛狗&#xff0c;一天至少两次&#xff0c;每次至少120分钟&#xff0c;题中所给的时间轴是从0-1440分钟&#xff0c;表示一整天。 然后它的主人正在不断接收信息&#xff08;在工作&#xff09;…

作用域 (局部作用域和全局作用域) 详细介绍

作用域 (局部作用域和全局作用域) 详细介绍 作用域是当前的执行上下文&#xff0c;值和表达式在其中“可见”或可被访问。 常见的作用域为&#xff1a; 全局作用域&#xff1a;脚本模式运行所有代码的默认作用域 函数作用域&#xff1a;由函数创建的作用域 局部作用域&#xff…

附录1-小程序常用标签

目录 1 view 2 scroll-view 3 swiper与swiper-item 4 text 5 rich-text 6 button 7 image 1 view 相当于html的div 2 scroll-view scroll-view是一个有滚动条的div scroll-y是允许纵向滚动&#xff0c;scroll-x是允许横向滚动 3 swiper与swiper-item swipe…

文盘Rust -- tokio绑定cpu实践 | 京东云技术团队

tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢&#xff1f;这次我们来聊聊这个话题。 首先我们先写一段简单的多任务程序。 use tokio::runtime; pub fn main() {let rt runtime::Builder::new_mu…

华为OD机试真题 Java 实现【通信误码】【2022Q4 100分】,附详细解题思路

一、题目描述 信号传播过程中会出现一些误码&#xff0c;不同的数字表示不同的误码ID&#xff0c;取值范围为1~65535&#xff0c;用一个数组记录误码出现的情况。 每个误码出现的次数代表误码频度&#xff0c;请找出记录中包含频度最高误码的最小子数组长度。 二、输入描述 …