linux静态库与动态库

1、动态库和静态库概念

Linux中的库分为动态库和静态库。

静态库(.a):库文件以.a为后缀,程序在编译链接时把库的代码链接到可执行文件中(将需要的库函数拷贝一份到代码中)。程序运行时不需要再跳转到静态库。

动态库(.so):库文件以.so为后缀,程序在运行时才去链接动态库的代码(运行时跳转到动态库中,在动态库中执行库函数)。多个程序共享库的代码。

链接的本质:我们调用库函数时是如何与标准库联系的。

库的名称:去掉前缀lib和后缀’.a/.so’剩下的就是库名称,例如:libc.so就是C库。

gcc/g++,在编译时默认使用动态链接,如果想要生存静态链接,我们要带上-static。

2、库

我们了解了动态库和静态库的相关概念,但是我们还是不理解库是个什么东西。
假设,我们做了一个小程序,只希望提供给用户小程序的功能,不希望暴露我们的源码。我们可以选择给用户提供我们的.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 once2 #include<stdio.h>3 extern int add(int, int);

文件sub.h

1 #pragma once2 #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文件的集合。

3、制作静态库

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

编写库:

3.1 创建Makefile:

1 libmymath.s:add.o sub.o2         ar -rc $@ $^3 add.o:add.c4         gcc -c add.c -o add.o5 sub.o:sub.c6         gcc -c sub.c -o sub.o7 .PHONY:output8 output:9         mkdir -p mylib/include10         mkdir -p mylib/lib11         cp -f *.a mylib/lib12         cp -f *.h mylib/include13 .PHONY:clean14 clean:15         rm -f *.o libmymath.a

3.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.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 }

在这里插入图片描述

为什么会找不到头文件?

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

gcc -o mymath main.c -I ./mylib/include

在这里插入图片描述

此时出现了新问题——找不到库函数的实现。

我们在形成可执行程序时,库文件要使用,必须知道库所在的路径,而系统中库默认路径为/lib64。因此,我们要告诉gcc,它要链接的库的路径在哪里(-L)。

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

gcc -o mymath main.c -I ./mylib/include -L ./mylib/lib -l mymath

在这里插入图片描述

结果正确!!!

总结

-I 指明头文件的路径
-L 指定库文件目录,可以指定多个文件目录。库目录没有在/lib、/usr/lib、/usr/local/lib中,则必须用-L来指定一个库目录
-l 指定具体的库文件。如果没有指定,则默认去/lib、/usr/lib、/usr/local/lib去找。默认寻找的是动态库,可以指定-static,寻找静态库。

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

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

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

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

4、制作动态库

首先我们将文件全部编译成.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

在这里插入图片描述

直接运行:

在这里插入图片描述

注意:我们自己定义的环境变量只是本次登录有效,如果想永久有效只能修改环境变量的配置。当然,我们还有其它办法:

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

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

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

5、动静态库的加载

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

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

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

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

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

相关文章

vue3报错

这是因为eslint对代码的要求严格导致的&#xff0c;可以在package.json里面删掉"eslint:recommended"&#xff0c;然后重启就可以正常运行了

电影院订票选座网站小程序开发(java开源)

搭建一个电影院订票选座网站小程序需要掌握Java语言和相关的Web开发技术&#xff0c;同时需要使用开源框架和库来实现。以下是一个基本的步骤指南&#xff1a; 确定技术栈 首先&#xff0c;需要确定使用的技术栈&#xff0c;以便更好的开展工作。 设计数据库 设计数据库需要…

畜牧虚拟仿真 | 鱼授精过程VR模拟演练系统

随着科技的发展&#xff0c;虚拟现实(VR)技术逐渐渗透到各个领域&#xff0c;为人们提供了更加真实、直观的体验。在动物养殖教育领域&#xff0c;鱼授精过程VR模拟演练系统正成为一种新的教学手段&#xff0c;它能够帮助人们更好地理解和掌握鱼授精的操作技巧&#xff0c;从而…

Maven进阶2 -- 私服(Nexus)、私服仓库分类、资源上传和下载

目录 私服是一台独立的服务器&#xff0c;用于解决团队内部的资源共享与资源同步问题。 1.Nexus Nexus是sonatype公司的一款maven私服产品。 下载地址 启动 nexus.exe /run nexus 访问 & 登录 2.私服仓库分类 3.资源上传和下载 本地仓库上传和访问资源需要进行配置。…

章节2:客户端的Cookie

章节2&#xff1a;客户端的Cookie 无状态的影响 现实&#xff1a;每个请求都是独立的 需求&#xff1a;保持会话 cookie内容 key/value 格式&#xff0c;例如&#xff1a; namewuya id99 islogin1 cookie怎么产生 Cookie格式 Set-Cookie&#xff1a;第一次访问&#…

java版工程项目管理系统源码+系统管理+系统设置+项目管理+合同管理+二次开发em

​ 鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部…

IP路由基础+OSPF 基础

IP路由 RIB与FIB RIB&#xff1a;Routing Information Base&#xff0c;路由信息库 &#xff0c;路由器的控制平面 FIB&#xff1a;Forwarding Information Base&#xff0c;转发信息库&#xff0c;路由器的数据平面 路由信息库主要是记录直连路由以及协议宣告的路由信息&am…

如何给a-table增加列宽拖动功能

对于table的列宽设置 相信用过的人都知道&#xff0c;想要设置得很完美&#xff0c;几乎是不现实的&#xff0c;因为总有数据或长或短&#xff0c;那我们应该如何优化它呢&#xff1f;那便是让用户自行拖动列宽&#xff0c;从而能看全table的数据&#xff0c;但是对于antd-vue …

恒运资本:股票印花税下降有什么影响?什么原因导致下降?

在进行股票教育过程中是需求收取必定的手续费的&#xff0c;比如说买卖佣钱、印花税、过户费等等。那么股票印花税下降有什么影响&#xff1f;什么原因导致下降&#xff1f;下面就由恒运资本为大家剖析&#xff1a; 股票印花税下降有什么影响&#xff1f; 1、对于企业&#xf…

vscode运行python报错:ModuleNotFoundError:No module named ‘xxx‘

在乌班图上使用pycharm的时候&#xff0c;pycharm总是莫名其妙卡死&#xff0c;又说是搜狗输入法的锅&#xff0c;又说别的原因&#xff0c;一气之下不用pycharm,转到vscode上&#xff0c;没想到出现了如下报错。 就是vscode在运行python的时候&#xff0c;自定义模块的调用无…

【C语言】经典题目(四)

HI&#xff0c;大家好~&#x1f61d;&#x1f61d;这是一篇C语言经典题目的博客。 更多C语言经典题目及刷题篇&#xff0c;可以参考&#xff1a; &#x1f338; 【C语言】经典题目(一) &#x1f338; 【C语言】经典题目(二) &#x1f338; 【C语言】经典题目(三) &#x1f338;…

vue3中的自定义指令用法

我们都知道vue2中自定义指令全局和局部是这样写的 局部&#xff1a; 全局&#xff1a; 可vue3写法发生改变&#xff0c;如下&#xff1a; 全局&#xff1a; 局部&#xff1a;

音视频 FFmpeg命令行搭建

文章目录 一、配置二、测试 一、配置 以FFmpeg4.2.1 win32为例 解压ffmpeg-4.2.1-win32-shared.zip 拷⻉可执⾏⽂件到C:\Windows拷⻉动态链接库到C:\Windows\SysWOW64 注&#xff1a;WoW64 (Windows On Windows64)是⼀个Windows操作系统的⼦系统&#xff0c;被设计⽤来处理许…

【网络】自定义协议 | 序列化和反序列化 | Jsoncpp

本文首发于 慕雪的寒舍 以tcpServer的计算器服务为例&#xff0c;实现用jsoncpp来进行序列化和反序列化 阅读本文之前&#xff0c;请先阅读 自定义协议 | 序列化和反序列化 | 以tcpServer为例 1.安装jsoncpp 我所用的系统是centos7.6&#xff0c;先用下面的命令查找相关的包 …

图的遍历之 深度优先搜索和广度优先搜索

深度优先搜索的图文介绍 1. 深度优先搜索介绍 图的深度优先搜索(Depth First Search)&#xff0c;和树的先序遍历比较类似。 它的思想&#xff1a;假设初始状态是图中所有顶点均未被访问&#xff0c;则从某个顶点v出发&#xff0c;首先访问该顶点&#xff0c;然后依次从它的各…

Element组件浅尝辄止2:Card卡片组件

根据官方说法&#xff1a; 将信息聚合在卡片容器中展示。 1.啥时候使用&#xff1f;When? 既然是信息聚合的容器&#xff0c;那场景就好说了 新建页面时可以用来当做页面容器页面的某一部分&#xff0c;可以用来当做子容器 2.怎样使用&#xff1f;How&#xff1f; //Card …

分布式应用:Zabbix代理服务器与SNMP监控

目录 一、理论 1.分布式监控 2.Zabbix代理服务器部署 3.配置 agent 使用 proxy 4.设置 Zabbix-SNMP 监控 二、实验 1.Zabbix代理服务器部署 2.配置 agent 使用 proxy 3.设置 Zabbix-SNMP 监控 三、总结 一、理论 1.分布式监控 &#xff08;1&#xff09;作用&#x…

Docker安装Mysql、Redis、nginx、nacos等环境

相关系列文章&#xff1a; 1、DockerHarbor私有仓库快速搭建 2、DockerJenkinsHarbor 1、服务器 Ip部署内容说明192.168.88.7Docker、Mysql、redis、nacosnode1192.168.88.8Docker、Mysql、redis、nacosnode2192.168.88.9Docker、redis、nacos、nginxnode3 2、安装PXC8.0 Mys…

PHP实现在线进制转换器,10进制,2、4、8、16、32进制转换

1.接口文档 2.laravel实现代码 /*** 进制转换计算器* return \Illuminate\Http\JsonResponse*/public function binaryConvertCal(){$ten $this->request(ten);$two $this->request(two);$four $this->request(four);$eight $this->request(eight);$sixteen …

echarts 横向柱状图

<template><div ref"chart" style"height: 100%"></div> </template><script> import * as echarts from "echarts"; var cate ["质量通病1", "质量通病2", "质量通病3", "质…