linux 动态库 静态库_静态库里面包含动态库

linux 动态库 静态库_静态库里面包含动态库动态库与静态库文件系统补完文件的三个时间acm动态库与静态库动态链接与静态链接静态库文件系统补完文件的三个时间acm我们通过stat指令查看文件信息:[lyl@VM-4-3-centos2022-3-14]$statlog.txtFile:‘log.txt’Size:0 Blocks:0IOBlock:4096regularemptyfileDevice:fd01h/64769d Inode:790871

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

在这里插入图片描述

文件系统补完

文件的三个时间acm

我们通过stat指令查看文件信息:

[lyl@VM-4-3-centos 2022-3-14]$ stat log.txt 
  File: ‘log.txt’
  Size: 0         	Blocks: 0          IO Block: 4096   regular empty file
Device: fd01h/64769d	Inode: 790871      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1001/     lyl)   Gid: ( 1001/     lyl)
Access: 2022-03-13 22:18:13.571644848 +0800
Modify: 2022-03-13 22:18:13.571644848 +0800
Change: 2022-03-13 22:18:13.571644848 +0800
 Birth: -

可以看到在文件信息下有3个时间,这三个时间分别代表着:

  • Access:最近访问文件的时间
  • Modify:文件内容最近修改的时间
  • Change:文件属性最近修改的时间
    在这里插入图片描述
    那么这三个时间有什么作用呢?
    我们在使用自动化构建工具Makefile时,如果连续make会发现:
    在这里插入图片描述
    这便是由Modify与Change两个时间决定的,若Modify时间不早于Change,则gcc指令可以执行,否则会显示此时mytest is up 同date.
    在这里插入图片描述

动态库与静态库

我们在实际开发中,经常要使用别人已经实现好的功能,这是为了开发效率和鲁棒性(健壮性);因为那些功能都是顶尖的工程师已经写好的,并且已经践行多年的代码。
那么如何使用他人开发的功能呢?可以使用:1.库,包括静态库与动态库。2.开源代码。3.基本的网络功能调用,比如各种网络接口、语音识别等等。
这其中,我们将详细介绍静态库和动态库。

动态链接与静态链接

一般情况下,为了更好的支持开发,第三方库或者是语言库,都必须提供静态库和动态库,这是方便程序员根据需要进行bin(二进制文件)的生成。
动态库是动态链接生成的,而静态库是静态链接生成的。
一般来说,我们直接gcc编译默认是动态链接的而如果加上-static选项,那么生成的可执行文件将为静态生成的
在使用-static选项时可能出现yum -y install glibc-static的报错,这其实是静态链接时没有找到libc.a。使用yum -y install glibc-static指令安装即可解决问题。

在这里插入图片描述
可以很明显的看到动态链接的文件大小明显要比静态链接的文件大小要小多了,这是为什么呢?
其实,动态链接是当执行到要调用的接口时,编译器会自动去搜寻所链接的库,而静态链接则是暴力的将所要用的库中可执行程序使用的二进制代码全部拷贝到我们生成的可执行文件中,这也就是为什么静态链接生成的文件这么大的原因了。
在这里插入图片描述

静态库与动态库

一般的命名方式为lib+库的名字+.a比如C语言提供的标准静态库名字就是libc.a
静态库是指程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
而动态库则是指程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表(头文件),而不是外部函数所在目标文件(.o)的整个机器码
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking),也就是说,动态链接是在需要调用接口时才会去将所用接口的二进制代码拷贝到内存中。
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
  • 这里需要提一下的是,我们之前所提过的进程地址空间中有一个共享区,而一般动态库的代码就映射在共享区,所有进程都共享着动态库的代码。
    在这里插入图片描述

动静态库的对比

动态库被加载在内存中,可以供多个使用库的程序共享映射到自己的虚拟地址空间使用,因此可以减少页面交换以及降低内存中代码冗余,并且因为与源程序模块分离,因此开发模式比较好。
而加载动态库的程序运行速度相对较慢,因为动态库运行时加载,映射到虚拟地址空间后需要重新根据映射起始地址计算函数/变量地址。
静态库则与之相反,其运行速度相对较快,但消耗资源较多。

生成静态库

我们为什么会制作库呢?一般是想让别人能够使用我们实现的功能,但又不暴露自己的源代码才会打包库。那么接下来我们来学习如何打包静态库。

打包静态库

由于生成静态库需要先生成目标文件(.o)再进行打包,故先编写相应的源文件再将其编译成目标文件:

[lyl@VM-4-3-centos 2022-3-14]$ gcc -c add.c -o add.o #生成目标文件
[lyl@VM-4-3-centos 2022-3-14]$ gcc -c sub.c -o sub.o #生成目标文件

此时的add.o和sub.o文件是已经编译好但还没有链接的两个文件,此时再用 ar命令将其打包成静态库:

[lyl@VM-4-3-centos 2022-3-14]$ ar -rc libmycal.a add.o sub.o #打包成静态库

这里的ar是gnu归档工具,rc表示(replace and create)。

[lyl@VM-4-3-centos 2022-3-14]$ ar -tv libmycal.a #查看静态库的目录列表
rw-rw-r-- 1001/1001   1240 Mar 14 11:11 2022 add.o
rw-rw-r-- 1001/1001   1240 Mar 14 11:11 2022 sub.o
  • t:列出静态库中的文件
  • v:verbose 详细信息

使用静态库

[lyl@VM-4-3-centos 2022-3-14]$ cp ./*.h ./lib #将头文件复制到lib目录下(提供函数入口地址,使用接口的前提) [lyl@VM-4-3-centos 2022-3-14]$ cp ./libmycal.a ./lib # 将静态库复制到lib目录下 

既然已经打包好了静态库,让我们包一下头文件来调用我们实现的接口:

#include <stdio.h>
#include "add.h"
#include "sub.h"

int main()
{ 
   
  int a = 10;
  int b = 20;
  printf("a+b:%d\n", Add(a, b));
  printf("a-b:%d\n", Sub(a, b));
  return 0;
}

在这里插入图片描述

发现代码编译不过去,报错说函数Add及Sub未定义,我们明明已经实现了接口并打包成静态库了,这是为什么呢?
其实gcc编译时去链接库和头文件是去默认路径以及当前路径寻找,而我们将静态库打包到lib目录下,gcc编译时就找不到我们的库了,所以我们需要加一些选项来告知gcc去寻找指定路径的库及头文件。
gcc寻找的默认路径:

/usr/include

在这里插入图片描述
因此,正确链接的指令为:

gcc -o main main.c -I ./lib -L ./lib  -lmycal -static

其中:

  • -I(i的大写) + 指定路径:是指告知gcc除了默认路径,还要去寻找一下指定的路径的头文件
  • -L + 指定路径:指定库所在的路径。
  • -l(L的小写)+库名字:表示要具体链接的是哪一个库,因为指定目录下可能不止一个库,所以要指明库的名字。
    由此,我们就静态链接生成了一个可执行文件main,运行main程序结果如下:
    在这里插入图片描述
    此时我们删除静态库libmycal.a,再运行main程序,发现程序照样可以运行。

生成动态库

学习完生成和使用静态库后,下面我们来生成一下动态库。

打包动态库

在这里,我们将生成动态库的依赖关系及方法写进自动化构建工具中:
在这里插入图片描述

需要注意的是:

  • 由于库在内存中是可加载的,它可能在内存中的任意位置,也可能被映射到进程地址空间的每个区域,所以为了保证库当中的代码执行不会出错,也就是要保证库中的代码是与位置无关的,因此生成.o文件时需要带上-fPIC选项表示生成与位置无关码。
  • 这里由于在依赖关系中已经点明了要生成的目标文件,故不带上$@也可以
  • 打包动态库不需要像静态库一样使用ar指令,直接用gcc即可,但是需要带上-shared选项表示生成共享库格式,这也体现了动态库代码映射在共享区的特点
    在这里插入图片描述

此时make就制作好了动态库:
在这里插入图片描述

使用动态库

和静态库使用一样带上三个选项打包动态库:
在这里插入图片描述
这里我们在运行程序时可能会报错:

error while loading shared libraries
cannot open shared object file: No such file or directory

这是因为,此时的可执行程序已经与编译器无关了,这属于运行问题,运行时系统也需要找到我们所使用的库,但在默认路径下没有我们的库。
这里解决方法有多种,但我倾向于推荐下面这一种:
修改环境变量LD_LIBRARY_PATH,将动态库所在路径添加到该环境变量中,这样程序在运行时系统就能够找到动态库,从而运行成功。

[lyl@VM-4-3-centos dynamic]$ echo $LD_LIBRARY_PATH
:/home/lyl/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
[lyl@VM-4-3-centos dynamic]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/lyl/2022-3-14/dynamic/lib
[lyl@VM-4-3-centos dynamic]$ echo $LD_LIBRARY_PATH
:/home/lyl/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/lyl/2022-3-14/dynamic/lib

当然,让系统找到我们的动态库还可以拷贝.so文件到系统共享库路径下, 一般指/usr/lib。但是这可能会污染系统原生的库,一般不推荐这样做。
其次,还有一种方法,在我们的系统下有/etc/ld.so.conf.d/这个路径:

[lyl@VM-4-3-centos dynamic]$ ls /etc/ld.so.conf.d/
bind-export-x86_64.conf  dyninst-x86_64.conf  hcoll.conf  kernel-3.10.0-1160.11.1.el7.x86_64.conf  mariadb-x86_64.conf  mxm.conf  sharp.conf

我们可以把库所在的路径写进这个路径当中,可以在这个路径下制造自己的.conf,然后再将库路径写进这个conf中:

echo "/home/lyl/2022-3-14/dynamic" > /etc/ld.so.conf.d/lyl.conf

当然这个方法也不推荐,毕竟可能污染库的头文件和库。

好了,动态库和静态库的全部内容至此介绍完毕。
文章编写不易,还希望各位能给个赞~

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/188357.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • JTAG接口定义与其他简介

    JTAG接口定义与其他简介JTAG接口定义与其他简介JTAG(JointTestActionGroup)是一个接口,为了这个接口成立了一个小组叫JTAG小组,它成立于1985年,比推丸菌的年龄还大。在1990年IEEE觉得一切妥当,于是发布了IEEEStandard1149.1-1990,并命名为StandardTestAccessPortandBoundary-ScanArchitecture…

    2022年6月10日
    70
  • BigDecimal 比较大小

    BigDecimal 比较大小BigDecimala=newBigDecimal(101);BigDecimalb=newBigDecimal(111);//使用compareTo方法比较//注意:a、b均不能为null,否则会报空指针if(a.compareTo(b)==-1){System.out.println(“a小于b”);}if(a.compareTo(b)==0){System.out.println(“a等于b”);}if(a.compareT.

    2022年6月3日
    36
  • java获取服务器文件路径,干货满满!

    java获取服务器文件路径,干货满满!一、SpringCloud微服务概念定义提起微服务,不得不提SpringCloud全家桶系列,SpringCloud是一个服务治理平台,是若干个框架的集合,提供了全套的分布式系统解决方案。包含了:服务注册与发现、配置中心、服务网关、智能路由、负载均衡、断路器、监控跟踪、分布式消息队列等等。SpringCloud通过SpringBoot风格的封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、容易部署的分布式系统开发工具包。开发者可以快速的启动服务或构建应用、同时能够

    2022年7月11日
    43
  • 开源自动化运维平台Spug

    开源自动化运维平台Spug开源自动化运维平台SpugSpug演示环境特性安装Docker安装安装步骤1.安装docker2.拉取镜像3.启动容器4.初始化5.访问测试6.版本升级SpugSpug是面向中小型企业设计的轻量级无Agent的自动化运维平台,整合了主机管理、主机批量执行、主机在线终端、应用发布部署、在线任务计划、配置中心、监控、报警等一系列功能。官网地址:https://spug.cc使用文档:https://spug.cc/docs/about-spug/更新日志:https://spug.cc

    2022年5月17日
    56
  • Java面试宝典(超级详细)「建议收藏」

    一、Java基础1.JDK和JRE有什么区别?JDK:JavaDevelopmentKit的简称,java开发工具包,提供了java的开发环境和运行环境。JRE:JavaRuntimeEnvironment的简称,java运行环境,为java的运行提供了所需环境。具体来说JDK其实包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具。简单来说:如果你需要运行java程序,只需安装JRE就可

    2022年4月5日
    41
  • SpringBoot Test及注解详解

    SpringBoot Test及注解详解一、SpringBootTest介绍SpringTest与JUnit等其他测试框架结合起来,提供了便捷高效的测试手段。而SpringBootTest是在SpringTest之上的再次封装,增加了切片测试,增强了mock能力。整体上,SpringBootTest支持的测试种类,大致可以分为如下三类:单元测试:一般面向方法,编写一般业务代码时,测试成本较大。涉及到的注解有@Test。 切片测试:一般面向难于测试的边界功能,介于单元测试和功能测试之间。涉及到的注解有@RunWith

    2022年5月30日
    31

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号