Linux(七) 动静态库

目录

一、动静态库的概念

二、静态库的打包与使用

2.1 静态库的打包

2.2 静态库的使用

三、动态库的打包与使用

3.1 动态库的打包

3.2 动态库的使用

3.3 运行动态库的四种方法

四、总makefile


一、动静态库的概念

静态库: Linux下,以.a为后缀的文件。程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。本质是在编译时把静态库中的代码(不是一次性加载,而是分页加载)复制到了进程的的代码区中。
动态库: Linux下,以.so为后缀的文件。程序在运行的时候才去链接动态库的代码,在可执行程序装载或运行时,由操作系统的装载程序加载库,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。
静态链接: 将库中的相关代码复制进可执行程序中的过程。
动态链接: 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中。
链接的本质: 使.o文件可以找到要调用的函数的位置
库文件名称: 比如libc.so,去掉前缀lib和后缀.so,剩下的就是库名。libhello.a的库名就是hello。

头文件gcc的默认搜索路径是 /usr/include
库文件的默认搜索路径是 /usr/lib64

把文件拷贝到系统的默认路径下就叫做库的安装,拷贝之后就不用 -I 和 -L了


实例演示: 分别使用静态链接和动态链接编译生成两个可执行程序,比较两个程序的大小
使用gcc静态链接编译时,命令要带上**-static** 选项,如下:

gcc -o test test.c -static

 

可以看到的是,使用静态库静态链接成的可执行程序比动态链接生成的可执行程序要大很多。
我们还可以通过file命令查看文件的链接属性:
 还可以通过ldd 命令查看可执行程序的依赖库,动态链接生成的可执行程序才有依赖库,静态链接升序的可执行程序不依赖任何库文件,因为库文件的代码已经复制进可执行程序了。

因为这里是动态链接,不仅要让编译器动态库的路径,还要让操作系统知道,所以这里我们需要导入一个环境变量LD_LIBRARY_PATH,如下:

export LD_LIBRARY_PATH=/home/dgz/linux/lesson26/lib/uselib/output/lib

执行完之后

总结动静态库的优缺点
静态库

  • 优点: 程序运行的时候将不再需要静态库,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。
  • 缺点:一是浪费空间,因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个目标文件都在内存存在多个副本;另一方面就是更新比较困难,因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。

动态库

  • 优点: 动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间.动态链接的优点显而易见,就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;另一个优点是,更新也比较方便,更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标
  • 缺点: 程序运行的时候依赖动态库,  据估算,动态链接和静态链接相比,性能损失大约在5%以下。经过实践证明,这点性能损失用来换区程序在空间上的节省和程序构建和升级时的灵活性是值得的。

二、静态库的打包与使用

2.1 静态库的打包

静态库打包: 本质其实就是将代码编译成.o的二进制文件,然后进行打包。
为了更好地演示这个过程,我创建了mymath.cmymath.hmyprint.cmyprint.h四个文件,内容分别如下:
mymath.c
 

#include "mymath.h"
int Add_(int a,int b)
{return a+b;
}


mymath.h
 

#pragma once
#include <stdio.h>extern int Add_(int a,int b);


myprint.c
 

#include "myprint.h"
void printf_(const char* str)
{printf("hello %s [%d]\n",str,(int)time(NULL));
}


myprint.h

#pragma once 
#include <stdio.h>
#include <time.h>extern void printf_(const char* str);

如下: 

打包静态库的步骤

  1. 先将myadd.cmysub.c 变成生成对应的二进制文件
  2. 使用ar 归档工具对两个二进制文件进行打包,同时带上选项**-rc**(r和c分别代表replace和creat),这里的库名是hello。
     

    ar -rc libhello.a *.o

  3. 上面这两个步骤其实就把静态库打包好了,下面我们还有做一个工作就是发布静态库,简单地说,就是把头文件和静态库组织起来,头文件放在include 下,如下:

  4. 这样一个库文件就可以给别人使用了。
    上面的所有步骤我们可以写进Makefile里,利用make指令一键打包和make output发布,如下:

    libhello.a:myprint.o mymath.oar -rc libhello.a myprint.o mymath.o
    mymath.o:mymath.cgcc -c mymath.c -o mymath.o
    myprint.o:myprint.cgcc -c myprint.c -o myprint.o.PHONY:output
    output:mkdir -p output/lib mkdir -p output/includecp -rf *.h output/includecp -rf *.a output/lib
    .PHONY:clean
    clean:rm -rf *.o *.a output

2.2 静态库的使用

先把静态库放到一个测试目录下:

然后编写一段代码:
 

#include "myprint.h"
#include "mymath.h"int main()
{int ret = Add_(1,2); printf("%d\n",ret);printf_("dgz");return 0;
}

编写Makefile:
使用gcc编译时,采用静态链接编译,所以要带上选项**-static**,此外,因为我们使用了别人给的静态库,所以我们还有告诉编译器库文件所在路径,头文件所在路径以及库名,所以要用到以下三个选项:

  • -L: 指明库文件所在路径
  • -I: 指明头文件所在路径
  • -l: 指明库文件名称,这里库名就是hello(去掉前缀lib和后缀.a)

这里我们可以使用绝对路径,使用下面的shell命令获取当前所在路径:
 

path=$(shell pwd)

Makefile编写后如下:

path=$(shell pwd)mytest:test.c#-l 指定库目录名称 -L 库目录路径 -I 指定头文件路径gcc -o $@ $^ -I $(path)/output/include -L $(path)/output/lib -l hello -static 
.PHONY:clean
clean:rm -f mytest

 

使用file指令查看链接属性:

三、动态库的打包与使用

3.1 动态库的打包

我们同样还是使用上面的那四个文件进行演示。
步骤:

  1. 先将mymath.cmyprint.c 变成生成对应的二进制文件。注意这里生成的二进制文件要带上选项**-fPIC**,产生路径无关码,也就是这里使用相对地址,是动态确定的,不存在绝对地址
    gcc -c -fPIC mymath.c -o mymath_d.o
    gcc -c -fPIC myprint.c -o myprint_d.o
  2. 使用gcc带上选项**-shared** (生成共享的库格式)对二进制文件进行打包
    	gcc -shared myprint_d.o mymath_d.o -o libhello.so
  3. 最后一步就是对动态库进行发布,也就是将库文件和头文件进行组织打包
  4. 编写Makefile:
    libhello.so:myprint_d.o mymath_d.ogcc -shared myprint_d.o mymath_d.o -o libhello.so
    mymath_d.o:mymath.cgcc -c -fPIC mymath.c -o mymath_d.o
    myprint_d.o:myprint.cgcc -c -fPIC myprint.c -o myprint_d.o.PHONY:output
    output:mkdir -p output/lib mkdir -p output/includecp -rf *.h output/includecp -rf *.so output/lib 
    .PHONY:clean
    clean:rm -rf *.o *.so output
    

3.2 动态库的使用

先把动态库放到测试目录下:

然后编写Makefile,和静态库的使用类似:
 

path=$(shell pwd)mytest_d:test.c#-l 指定库目录名称 -L 库目录路径 -I 指定头文件路径gcc -o $@ $^ -I $(path)/output/include -L $(path)/output/lib -l hello 
.PHONY:clean
clean:rm -f mytest_d

 编译程序: 如果此时直接对程序进行编译,不会报错,但是执行的时候会报错,无法打开共享库里面的文件

 因为这里是动态链接,不仅要让编译器动态库的路径,还要让操作系统知道,所以这里我们需要导入一个环境变量LD_LIBRARY_PATH,如下(上面已经说过,这里重复一次)

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/dgz/linux/lesson26/lib/uselib/output/lib

 此时再执行程序就不会报错了:

使用file指令查看程序的链接属性:

使用ldd指令查看程序依赖的库: 

3.3 运行动态库的四种方法

  1. 将对应到.so和.h文件拷贝到/usr/lib64和/usr/include
  2. 系统在在搜索头文件时会先在系统默认路径下搜索(上面两个路径),如果没找到,但是环境变量LD_LIBRARY_PATH设置了,也会在该环境变量下搜索,这种方法是内存级的,退出就没有了。
  3. 修改配置文件,配置/etc/ld.so.conf.d/
    路径下原有的文件

    我们想要配置它只需要在该路径下创建一个.conf文件

    然后将我们要配置的路径写进该文件

    最后执行一下
    ldconfig
  4. 建立软连接
     建立成功,然后就可以找到了。

四、总makefile

.PHONY:all
all:libhello.so libhello.alibhello.so:myprint_d.o mymath_d.ogcc -shared myprint_d.o mymath_d.o -o libhello.so
mymath_d.o:mymath.cgcc -c -fPIC mymath.c -o mymath_d.o
myprint_d.o:myprint.cgcc -c -fPIC myprint.c -o myprint_d.olibhello.a:myprint.o mymath.oar -rc libhello.a myprint.o mymath.o
mymath.o:mymath.cgcc -c mymath.c -o mymath.o
myprint.o:myprint.cgcc -c myprint.c -o myprint.o
# 发布
.PHONY:output
output:mkdir -p output/lib mkdir -p output/includecp -rf *.h output/includecp -rf *.a output/libcp -rf *.so output/lib 
.PHONY:clean
clean:rm -rf *.o *.a *.so output

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

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

相关文章

Python专题:十五、JSON数据格式

Python的数据处理&#xff1a;JOSN 计算机的主要工作&#xff1a;处理数据 最容易处理的数据就是结构化数据 非结构化数据&#xff1a;视频&#xff0c;文件等 近些年的大数据、数据挖掘就是对互联网中的各种非结构化的数据的分析和处理 半结构化数据 明确的结构属性&…

陪诊服务运用预约小程序的效果是什么

在中高型城市里&#xff0c;陪诊师近些年也很有热度&#xff0c;已经衍生成为一个新的小众行业&#xff0c;不同医院/不同科目等其它情况针对不同群体往往很难完善&#xff0c;比如部分老年人腿脚不便、不认识字、外地语言难以沟通等&#xff0c;陪诊师的作用就尤为凸显. 对相…

[Bootloader][uboot]code总结

文章目录 1、U_BOOT_DRIVER2、DM框架dm_scan_platdatadm_extended_scan_fdt 1、U_BOOT_DRIVER 使用这个宏可以定义一个驱动实例&#xff0c;宏定义是 其中使用的struct driver结构体 使用的ll_entry_declare宏定义是 归结为 2、DM框架 1、 DM框架 DM模型抽象出了以下四个…

16.投影矩阵,最小二乘

文章目录 1. 投影矩阵1.1 投影矩阵P1.2 投影向量 1. 投影矩阵 1.1 投影矩阵P 根据上节知识&#xff0c;我们知道当我们在解 A X b AXb AXb的时候&#xff0c;发现当向量b不在矩阵A的列空间的时候&#xff0c;我们希望的是通过投影&#xff0c;将向量b投影到矩阵A的列空间中&…

ModuleNotFoundError: No module named ‘sklearn‘

ModuleNotFoundError: No module named sklearn 解决办法&#xff1a; pip install scikit-learn

7B2 PRO主题5.4.2免授权直接安装

B2 PRO 5.4.2 最新免授权版不再需要改hosts&#xff0c;直接在wordpress上传安装即可

Vue的学习 —— <网络请求库Axios>

目录 前言 正文 一、Axios基本概念 二、安装Axios 三、Axios使用方法 四、向服务器发送请求 前言 在之前的开发案例中&#xff0c;我们通常直接在组件中定义数据。但在实际的项目开发中&#xff0c;我们需要从服务器获取数据。当其他用户希望访问我们自己编写的网页时&a…

定档 11.2-3,COSCon'24 第九届中国开源年会暨开源社十周年嘉年华正式启动!

中国开源年会 COSCon 是业界最具影响力的开源盛会之一&#xff0c;由开源社在2015年首次发起&#xff0c;今年将举办第九届。 以其独特定位及日益增加的影响力&#xff0c;COSCon 吸引了越来越多的国内外企业、高校、开源组织/社区的大力支持。与一般企业、IT 媒体、行业协会举…

网络安全快速入门(十三)linux及vmware软件的网络配置

13.1 前言 在通过我们前面的了解&#xff0c;我们现在已经对Linux的基础知识有了大致的了解&#xff0c;今天我们来大概讲一下关于linux系统及vmware的网络配置问题&#xff0c;在这之前&#xff0c;我们需要对网络有一个大概的认识和了解&#xff0c;话不多说&#xff0c;我们…

HNU-算法设计与分析-作业5

第五次作业【回溯算法】 文章目录 第五次作业【回溯算法】<1> 算法分析题5-3 回溯法重写0-1背包<2> 算法分析题5-5 旅行商问题&#xff08;剪枝&#xff09;<3> 算法实现题5-2 最小长度电路板排列问题<4> 算法实现题5-7 n色方柱问题<5> 算法实现…

公共字段填充(AOP的使用)

Thread是线程池,ThreadLocal是线程变量,每个线程变量是封闭的,与其它线程变量分隔开来,在sky-common下的com.sky.context包下有一个Basecontext类 public class BaseContext {//每一个上下文创建了一个线程变量,用来存储long类型的id//创建三个方法,用来设置,取用,删除idpubli…

绝地求生:PGS3参赛队伍跳点一览,17压力有点大,4AM与PeRo大概率不roll点

在PCL春季赛结束后&#xff0c;PGS3的参赛队伍名单以及分组就正式确定了&#xff0c;最后确定名额的DDT和NH被安排在了A组和B组&#xff0c;感觉这次PGS3的分组比较均衡&#xff0c;没有“死亡之组”一说。这段时间已经有网友汇总了PGS3队伍在各个地图的跳点&#xff0c;并且把…

「AIGC算法」近邻算法原理详解

本文主要介绍近邻算法原理及实践demo。 一、原理 K近邻算法&#xff08;K-Nearest Neighbors&#xff0c;简称KNN&#xff09;是一种基于距离的分类算法&#xff0c;其核心思想是距离越近的样本点&#xff0c;其类别越有可能相似。以下是KNN算法的原理详解&#xff1a; 1. 算…

Vmvare—windows中打不开摄像头

1、检查本地摄像头是否能正常打开 设备管理器—查看—显示隐藏设备—选中照相机—启动 USB2.0 HD UVC—打开相机查看 2、检查虚拟机的设置 虚拟机—虚拟机—可移动设备—USB2.0 HD UVC—勾选在状态栏中显示 虚拟机—打开windows主机—右小角选中圆圈图标—勾选连接主机 此时…

Java | Leetcode Java题解之第91题解码方法

题目&#xff1a; 题解&#xff1a; class Solution {public int numDecodings(String s) {int n s.length();// a f[i-2], b f[i-1], cf[i]int a 0, b 1, c 0;for (int i 1; i < n; i) {c 0;if (s.charAt(i - 1) ! 0) {c b;}if (i > 1 && s.charAt(i …

小红书笔记怎么发浏览量高?

小红书笔记发布是有技巧和策略的&#xff0c;为什么有的小红书笔记浏览量那么高&#xff0c;是因为下足了功夫&#xff0c;小红书笔记怎么发浏览量高&#xff1f;今天伯乐网络传媒就来给大家揭秘一下。 一、选题与定位 1. 热门话题选择 要想提高小红书笔记的浏览量&#xff0…

虚拟化技术 使用vSphere Web Client管理ESXi主机

一、实验内容 通过vSphere Web Client将ESXi主机连接到iSCSI共享存储通过vSphere Web Client&#xff0c;使用共享存储创建虚拟机并安装windows 2008 R2操作系统通过vSphere Web Client&#xff0c;为虚拟机创建快照 二、、实验主要仪器设备及材料 安装有64位Windows操作系统…

Redis如何避免数据丢失?——AOF

目录 AOF日志 1. 持久化——命令写入到AOF文件 写到用户缓冲区 AOF的触发入口函数——propagate 具体的实现逻辑——feedAppendOnlyFile 从用户缓冲区写入到AOF文件(磁盘&#xff09; 函数write、fsync、fdatasync Redis的线程池 AOF文件的同步策略 触发的入口函数——…

JavaScript函数声明

JS函数声明 JS中的方法,多称为函数,函数的声明语法和JAVA中有较大区别 语法1&#xff1a;function 函数名 (参数列表){函数体} 语法2&#xff1a;var 函数名 function (参数列表){函数体} 函数说明 函数没有权限控制符不用声明函数的返回值类型,需要返回在函数体中直接return即…

Python | Leetcode Python题解之第92题反转链表II

题目&#xff1a; 题解&#xff1a; class Solution:def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:# 设置 dummyNode 是这一类问题的一般做法dummy_node ListNode(-1)dummy_node.next headpre dummy_nodefor _ in range(left - 1):pre…