【Linux篇】第十一篇——动静态库(动静态库的介绍+动静态库的打包与使用)_ar打包静态库-程序员宅基地

技术标签: # Linux  运维  动静态库  linux  服务器  

动静态库的基本原理

认识动静态库

动静态库的概念

静态库的打包与使用

静态库的打包

 静态库的使用

动态库的打包与使用

动态库的打包

动态库的使用


动静态库的基本原理

动静态库的本质就是可执行程序的"半成品"。

需要完成一个可执行程序需要经历以下四个步骤:

  1. 预处理:完成头文件的展开,去掉注释,宏替换,条件编译等,最终形成***.i文件
  2. 编译:完成语法分析,词法分析,语义分析,符号汇总等,检查无误后将代码翻译成汇编指令,最终形成***.s文件
  3. 汇编:将汇编指令转换成二进制指令,最终形成***.o文件
  4. 链接:将生成的各个***.o文件进行链接,最终形成可执行程序

例如,用test1.c,test2.c以及main1.c形成可执行文件,需要先得到各个文件的目标文test1.o,test2.o以及main1.o,然后将这些目标文件链接起来,最终形成一个可执行程序。

 如果在另一个程序main2.c中也需要用到test1.c和test2.c,那么可执行程序生成和上面的步骤一样。

 实际上,对于可能频繁用到的源文件,比如这里的test1.c,test2.c,可以将他们的目标文件test1.o,test2.o进行打包,之后需要用到这两个目标文件就可以直接链接这个包当中的目标文件即可,上面的打包就可以称为一个库。库的本质就是一堆***.O集合,库的文件当中并不包含主函数而只是包含了大量写好的方法以供调用,因此,我们说动静态库是可执行程序的"半成品".

认识动静态库

下面以我们最初学习的最简单的代码,初步认识动静态库

#include <stdio.h>

int main()
{
	printf("hello world\n"); //库函数
	return 0;
}

运行结果如下所示: 

 在这份代码中我们通过调用printf输出的hello world,主要原因是gcc编译器在生产可执行程序时,将C标准库链接进来.

在Linux下,可以通过ldd 文件名来查看一个可执行程序所以来的库文件

第一次打开可能可不懂,其实就是libc.so.6就是该可执行程序依赖的库文件,通过ls命令去发现libc.so.6实际上就只是一个软链接

 实际上该软链接的源文件libc-2.17.solibc.so.6在同一个目录下,为了进一步了解,可以通过file 文件名命令来查看文件类型

 可以看到,实际上libc-2.17.so就是一个共享的目标文件库,准确来说,这还是一个动态库。说一下:

  • 在Linux中,以.so为后缀的是动态库,以.a为后缀的是静态库。
  • 在Windows中,以.dll为后缀的是动态库,以.lib为后缀的是静态库

这里的libc.so.6实际上就是C动态库,库的名字就是,去掉前缀lib,再去掉后缀.so或.a及其后面的版本。

而g++编译器一般都是默认是动态链接的,若想进行静态链接,可以携带一个-static

 此时生产的可执行程序就是静态链接了,可以明显观察到静态链接生成的可执行程序文件的大小,要比动态的要大的多。主要原因下面详细介绍。

动静态库的概念

静态库

Linux下,以.a为后缀的文件。程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候不再需要静态库。本质是在编译时把静态库中的代码复制到进程的代码中。因此使用静态库生成的可执行程序要比一般的程序大。

  • 优点:程序运行的时候不再需要静态库
  • 缺点:生成的可执行程序较大。如果多个使用静态链接生成的程序同时运行会占用大量的内存空间

动态库

Linux下,以.so为后缀的文件。程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。一个动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。

在可执行文件开始运行前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程叫做动态链接。动态库在多个程序间共享,节省了磁盘空间,操作系统采用虚拟内存机制允许物理内存中的一份动态库被调用到该库的所有进程共用,节省了内存和磁盘空间。

  • 优点:节省磁盘空间,且多个用到相同动态库的程序同时运行时,库文件会通过进程地址空间进行共享,内存当中不会存在重复代码
  • 缺点:必须依赖动态库,否则无法运行

静态库的打包与使用

静态库的打包

本质就是将代码编译成.o的二进制文件,然后进行打包。

为了更好的演示这个过程,创建add.c,add.h,sub.c和sub.h四个文件,内容如下所示:

add.h

#pragma once

extern int my_add(int x, int y);

 add.c

#include "add.h"

int my_add(int x, int y)
{
	return x + y;
}

sub.h 

#pragma once

extern int my_sub(int x, int y);

sub.c 

#include "sub.h"

int my_sub(int x, int y)
{
	return x - y;
}

如下所示:

第一步:将所有源文件生成对应的目标文件

第二步:使用ar命令将所有目标文件打包为静态库

 ar命令是gnu的归档工具,常用于将目标文件打包为静态库,下面我们使用ar命令的-r选项和-c选项进行打包。

  • -r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件
  • -c(create):建立静态库文件。

 此外,我们可以用ar命令的-t选项和-v选项查看静态库当中的文件。

  •  -t:列出静态库中的文件
  • -v:显示详细的信息

 第三步:将头文件和生成的静态库组织起来

 当我们把自己的库给别人用的时候,实际上需要给别人两个文件夹,一个文件夹下面放的是一堆头文件的集合,另一个文件夹下面放的是所有库文件。

因此,在这里我们可以将add.hsub.h这两个头文件放到一个名为Include的目录下,将生成的静态库文件libmymath.a放到一个名为lib的目录下,然后将这两个目录放到myliba下,此时就可以将myliba给别人使用了。

 Makefile 

当然,也可以将上述所要执行的命令全部写道Makefile中,后续当我们要生成静态库以及组织头文件和库文件时就可以一步到位了,不至于每次重新生成的时候都要敲这么多命令。

编写Makefile后,只需一个make就能生成所有源文件对应的目标文件进而生成静态库。

 make output就能将头文件和静态库组织起来。

 静态库的使用

创建源文件main.c,编写下面这边简单的程序尝试使用我们打包好的静态库。

#include <stdio.h>
#include <add.h>
#include<sub.h>
int main()
{
	int x = 20;
	int y = 10;
	int z = my_add(x, y);
    int q=my_sub(x,y);
	printf("%d + %d = %d\n", x, y, z);
    printf("%d+%d=%d\n",x,y,q);
	return 0;
}

经过调整后目录下只有main.c和我们刚刚打包好的静态库。 

 

 使用选项

 此时使用gcc编译main.c生成可执行程序时需要携带三个选项:

  • -I:指定头文件搜索路径
  • -L:  指定库文件搜索路径
  • -l:   指明需要链接库文件路径下的哪一个库

具体操作如下:

 注意:

  1. 因为编译器不知道你所包含的头文件add.h在哪里,所以需要指定头文件的搜索路径。
  2. 因为头文件add.h当中只有my_add函数声明,并没有该函数的定义,所以还需要指定所要链接库文件的搜索路径。
  3. 实际中,在库文件的Lib目录下可能会有大量的库文件,因此我们需要指明需要链接库文件路径下的哪一个库,库文件名需要去掉前缀lib,再去掉后缀.so或者.a以及后面的版本号,剩下的就是这个库的真正名字。

为什么之前使用gcc编译的时候没有指明过库的名字?

 因为我们使用的gcc编译的是C语言,而gcc就是用来编译C程序的,所以gcc编译的时候默认就找的是C库,但此时我们要链接的是哪一个库编译器是不知道的,因此我们需要使用选项,指明需要链接库文件路径下的哪一个库。

动态库的打包与使用

动态库的打包

动态库的打包相对于静态库来说有一点点差别,但大致相同,还是以上面的四个文件为例子:

 第一步:让所有源文件生成对应的目标文件

此时用源文件生成目标文件时需要携带-fPIC选项: 

说明:

  • -fPIC:产生位置无关码
  • -fPIC作用域编译阶段,告诉编译器产生于位置无关的代码,此时产生的代码中没有绝对地址,全部都使用相对地址,从而代码可以被加载器加载到内存的任意位置都可以正确的执行。这正是共享库所要求的,共享库被加载时, 在内存的位置不是固定的。
  • 如果不加-fPIC选项,则加载.so文件的代码段时,代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成了每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的拷贝,并且每个拷贝都不一样,取决于这个.so文件代码段和数据段内存映射的位置。
  • 不加-fPIC编译出来的.so是要在加载时根据加载到的位置再次重定位的,因为它里面的代码BBS位置无关代码。如果该.so文件被多个应用程序共同使用,那么它们必须每个程序维护一份.so的代码副本(因为.so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)。
  • 我们总是用-fPIC来生成.so,但从来不用-fPIC来生成.a,但是.so一样可以不用-fPIC选项进行编译,只是这样的.so必须要在加载到用户程序的地址空间时重定向所有表目。

 第二步:使用-shared选项将所有目标文件打包成动态库

 与生成静态库不同的是,生成动态库时我们不必使用ar命令,只需使用gcc的-shared选项即可。

第三步: 将头文件和生成的动态库组织起来

 与生成静态库时一样,为了方便别人使用,在这里可以将add.h和sub.h这两个头文件放到一个名为include的目录下,将生成的的动态库文件libcal.so放在一个名为lib的目录下,然后将这两个目录都放在mlib下。

 使用Makefile

当然,生成动态库也可以将上述所要执行的命令全部写到Makefile当中,后续当我们要生成动态库以及组织头文件和库文件就可以一步到位了

 编写Makefile后,只需一个make就能生成所有源文件对应的目标文件进而生成动态库。

 一个make output就能将头文件和动态库组织起来

动态库的使用

使用main.c来演示动态库的使用和我们刚打包好的动态库

 使用该动态库的方法与刚才我们使用静态库的方法一样,我们既可以用-I,-L,-l这三个选项来生成可执行程序,也可以先将头文件和库文件拷贝到系统目录下,然后仅使用-l选项指明需要链接的库名字来生成可执行程序。下面仅以第一种方法为例进行演示。

与静态库不同的是,这里无法直接运行。

 需要注意,这里使用-I,-L,-l这三个选项都是在编译期间告诉编译器我们使用的头文件和库文件在哪里以及是谁,但是当生成的可执行程序生成后就与编译器没有关系了,此后该可执行程序运行起来后,操作系统找不到该可执行程序所依赖的动态库。

既然找不到我们的库文件,那么我们直接将库文件拷贝到系统共享的库路径下,这样一来系统就能找到对应的库文件了。

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_58367586/article/details/127035200

智能推荐

spark2原理分析-RDD的shuffle简介_rdd shuffle-程序员宅基地

文章浏览阅读868次。概述本文介绍RDD的Shuffle原理,并分析shuffle过程的实现。RDD Shuffle简介spark的某些操作会触发被称为shuffle的事件。shuffle是Spark重新分配数据的机制,它可以对数据进行分组,该操作可以跨不同分区。该操作通常会在不同的执行器(executor)和主机之间复制数据,这使shuffle成为复杂且非常消耗资源的操作。Shuffle背景为了理解shuf..._rdd shuffle

python3 os.system 异步执行_Python执行系统命令的方法 os.system(),os.popen(),commands-程序员宅基地

文章浏览阅读3.8k次。最近在做那个测试框架的时候发现 Python 的另一个获得系统执行命令的返回值和输出的类。1.最开始的时候用 Python 学会了 os.system() 。这个方法是拥塞的。os.system('ping www.baidu.com')2.通过 os.popen() 返回的是 file read 的对象,对其进行读取 read() 的操作可以看到执行的输出。这个方法是后台执行,不影响后续脚本运行..._os.system异步执行

CM+CDH安装搭建全过程(总结版)_cloudera manager server gc cpu usage is at 10% or -程序员宅基地

文章浏览阅读2.9k次。目录第一次搭建CM、CDH第二次搭建CM、CDH搭建环境:搭建过程:报错过程:总结复盘:第三次搭建CM、CDH搭建环境:搭建过程:报错过程:总结复盘:第四次搭建CM、CDH搭建环境:搭建过程:报错过程:总结复盘:第一次搭建CM、CD..._cloudera manager server gc cpu usage is at 10% or more of total process time

内核开发调试printk_printk 头文件-程序员宅基地

文章浏览阅读706次。进行内核开发调试在进行驱动开发的过程中往往要打印一些信息来查看是否正确类似于printf,以下将介绍在内核开发常用的调试方法。.(第一次写文章,内容可能不咋样勿喷呀)内容一、printk介绍二、如何查看并修改消息级别在应用程序采用printf打印调试、内核驱动采用printk打印调试。printk函数打印数据到console缓冲区,打印的格式方类似printf。printk函数说明头文件:<linux/kernel.h>int printk(KERN_XXX const_printk 头文件

Kafka原理、部署与实践——深入理解Kafka的工作原理和使用场景,全面介绍Kafka在实际生产环境中的部署_kafka如何负载使用一台对外的机器-程序员宅基地

文章浏览阅读2.5k次。随着互联网的发展,网站的流量呈爆炸性增长,传统的基于关系型数据库的数据处理无法快速响应。而NoSQL技术如HBase、MongoDB等被广泛应用于分布式数据存储与处理,却没有提供像关系型数据库一样的ACID特性、JOIN操作及完整性约束。因此,很多公司或组织开始转向Apache Spark、Flink、Beam等新一代大数据处理框架来处理海量数据。然而,由于新一代大数据处理框架依赖于HDFS等文件系统,导致集群规模扩容困难、成本高昂。另一方面,云计算平台的出现让用户可以快速部署、扩展大数据处理集群。_kafka如何负载使用一台对外的机器

麒麟KYLINOS桌面操作系统2303上安装tigervnc_麒麟系统电脑安装vncserver-程序员宅基地

文章浏览阅读1.4k次。hello,大家好啊,今天给大家带来在麒麟桌面操作系统2303上安装tigervnc的文章,本篇文章给大家讲述如何安装并且远程连接使用,后面会给大家更新如何将tigervnc做成桌面图标点击即可开启及关闭,欢迎大家浏览分享转发。_麒麟系统电脑安装vncserver

随便推点

设备驱动模型:总线-设备-驱动_总线设备驱动模型-程序员宅基地

文章浏览阅读1.3k次,点赞5次,收藏12次。总线是连接处理器和设备之间的桥梁代表着同类设备需要共同遵循的工作时序。总线驱动:负责实现总线行为,管理两个链表。name:指定总线的名称,当新注册一种总线类型时,会在 /sys/bus 目录创建一个新的目录,目录名就是该参数的值;bus_groups、dev_groups、drv_groups:分别表示 总线、设备、驱动的属性。通常会在对应的 /sys 目录下在以文件的形式存在,对于驱动而言,在目录 /sys/bus//driver/ 存放了驱动的默认属性;_总线设备驱动模型

TensorFlow精进之路(十五):深度神经网络简介_tensorflow 精进之路-程序员宅基地

文章浏览阅读265次。1、概述本来想用卷积神经网络来预测点东西,但是效果嘛......,还是继续学习图像类的应用吧~前面学习的神经网络都是一些基础的结构,这些网络在各自的领域中都有一定效果,但是解决复杂问题肯定不够的,这就需要用到深度神经网络。深度神经网络是将前面所学的网络组合起来,利用各自网络的优势,使整体效果达到最优。这一节就简单的记下一些常用的深度神经网络模型,因为tensorflow等框架都将这些网络实现..._tensorflow 精进之路

第九十四篇 Spark+HDFS centos7环境搭建_spark写入hdfs需要用户名密码吗-程序员宅基地

文章浏览阅读2.6k次。一、安装包下载:Spark 官网下载: https://spark.apache.org/downloads.htmlHadoop 官网下载: https://hadoop.apache.org/releases.html目前使用Spark 版本为: spark-2.4.3 Hadoop版本为: hadoop-2.10.1二、配置自登陆检测是否可以自登陆,不需要密码则配置正常:ssh localhost在搭建Hadoop环境时,出现localhost.localdomain: Permis_spark写入hdfs需要用户名密码吗

Node.js_node可以使用什么命令 ,它会自动找到该文件下的start指令,执行入口文件。-程序员宅基地

文章浏览阅读280次。nodejs。_node可以使用什么命令 ,它会自动找到该文件下的start指令,执行入口文件。

linux图片相似度检测软件下载,移动端图像相似度算法选型-程序员宅基地

文章浏览阅读293次。概述电商场景中,卖家为获取流量,常常出现重复铺货现象,当用户发布上传图像或视频时,在客户端进行图像特征提取和指纹生成,再将其上传至云端指纹库对比后,找出相似图片,杜绝重复铺货造成的计算及存储资源浪费。该方法基于图像相似度计算,可广泛应用于安全、版权保护、电商等领域。摘要端上的图像相似度计算与传统图像相似度计算相比,对计算复杂度及检索效率有更高的要求。本文通过设计实验,对比三类图像相似度计算方法:感..._linux 图片相似度对比

java isprime函数_判断质数(isPrime)的方法——Java代码实现-程序员宅基地

文章浏览阅读3.8k次。判断质数(isPrime)的方法——Java代码实现/** 质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数* 100以内质数表2 3 5 7 11 13 17 19 23 29 31 37 41 43 4753 59 61 67 71 73 79 83 89 97质数具有许多独特的性质:(1)质数p的约数只有两个:1和p。(2)初等数学基本定理:..._java isprime

推荐文章

热门文章

相关标签