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

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

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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 概率论中 PDF,PMF,CDF的含义[通俗易懂]

    概率论中 PDF,PMF,CDF的含义[通俗易懂]概率论中PDF,PMF,CDF的含义在概率论中,我们经常能碰到这样几个概念PDF,PMF,CDF,这里就简单介绍一下PDF:概率密度函数(probabilitydensityfunction),在数学中,连续型随机变量的概率密度函数(在不至于混淆时可以简称为密度函数)是一个描述这个随机变量的输出值,在某个确定的取值点附近的可能性的函数。概率密度函数都是针对连续性随机变量的,对于连续性随机变量,都是针对某一段区间的取值,在一个点的取值都是几乎为0的,所以我们研究连续性随机变量时,都是取变量在一段

  • C# CultureInfo列表详细说明

    C# CultureInfo列表详细说明””(空字符串)固定区域性 af 南非荷兰语 af-ZA 南非荷兰语(南非) sq 阿尔巴尼亚语 sq-AL 阿尔巴尼亚语(阿尔巴尼亚) ar 阿拉伯语 ar-DZ 阿拉伯语(阿尔及利亚) ar-BH 阿拉伯语(巴林) ar-EG 阿拉伯语(埃及) ar-IQ 阿拉伯语(伊拉克) 

  • python 数组反转

    python 数组反转python中有一个列表a=[1,2,3,4,5,6]如果想反转该数组怎么办呢?一行代码搞定-Python代码1a = a[::-1]

  • Vista初探_Vista Alegre

    Vista初探_Vista Alegre昨天安装了手中的WindowsVistaBusiness,经过几个小时的摸索,我对Vista这个大块头有了一些个人体会,拿出来给大家分享。一、安装及激活:从安装开始说,在我安装之前,我已经有WindowsXP和Ubuntu两套操作系统,启动由Linux的Grub引导,如何把Vista装进硬盘又不影响Ubuntu的启动是第一个需要解决的问题。(以下括号内为题外话,如果你觉…

    2022年10月11日
  • java jvm优化(一)

    java jvm优化(一)转自http://ifeve.com/jvm-optimize-1/java由堆来分配所需内存。java有3个代,年轻代、年老代、永久代垃圾回收:当堆的空间不足以存放新的对象时,这是需要分配内存,也就是垃圾回收启动。GC算法:引用计数器回收、跟踪回收下面转自http://www.importnew.com/13827.htmlGC种类:4种1.串行垃圾回收器2.并行…

  • 什么是重载什么是覆盖_java覆盖和重载的关系

    什么是重载什么是覆盖_java覆盖和重载的关系java中的方法重载发生在同一个类里面两个或者多个方法的方法名相同但是参数不同的情况。与此相对,方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。覆盖者可能不会限

发表回复

您的电子邮箱地址不会被公开。

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