Linux动静态库

Linux动静态库

认识动静态库

我们先来看一段代码:

#include<stdio.h>

int main()
{
    printf("hello world\n");
    return 0;
}

相信大家都应该知道这段代码的运行结果其实就是hello world

在这里插入图片描述

在Linux下我们可以通过ldd指令来查看一个可执行程序所依赖的库文件
在这里插入图片描述

这里的libc.so.6就是当前可执行程序所依赖的库文件,我们通过ls命令查看后发现libc.so.6它其实就是一个软链接。

在这里插入图片描述

这个时候我就有一个问题:那么我们怎么知道这个库是一个动态库还是静态库呢?

上面说了libc.so.6它是一个软链接,而该软链接的源文件就是libc-2.17.so。因此我们可以通过file+文件名来查看一下libc-2.17.so的文件类型

在这里插入图片描述

可以看到libc-2.17.so它其实是一个动态库。

注意:

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

在Linux下,我们如何知道一个动静态库的名字呢?

比如libc.so,我们去掉前缀lib,去掉后缀.so,剩下的就是该动静态库的库名了。

在Linux下,gcc/g++编译器默认采用的是动态链接,若是想采用静态链接的话,我们需要在gcc/g++后面带上一个-static选项即可。

大家在使用gcc/g++命令带这个选项的时候可能会由于当前云服务器上面没有下载静态库而导致出错,因此我们需要先使用下面这两条命令分别下载gcc/g++静态库之后再去进行静态链接,这样的话就不会出错了。

yum install glibc-static
yum install glibc-static libstdc++-static

下面我们就来生成一下静态链接的可执行程序吧

在这里插入图片描述

我们可以看到静态链接生成的可执行程序文件比动态链接生成的可执行程序的文件大小要大很多,将近大了100倍。由于静态链接所生成的可执行程序它不依赖第三方库,因此当我们通过ldd指令去查看静态链接生成的可执行程序所依赖的库文件时就会出现下面这一幕:

在这里插入图片描述

当我们用file命令分别查看这两个可执行程序的文件类型时,我们可以看到它们分别是动态链接和静态链接的。

在这里插入图片描述

动态库

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

在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)

动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

在这里插入图片描述

优点:

  • 使用动态库生成的可执行程序体积比较小,节省资源。当多个可执行程序同时运行时,若是要使用同一个库,库文件会通过进程地址空间进行共享,内存中不会存在重复代码。

缺点:

  • 比较依赖于第三方库。
静态库

静态库是在程序编译链接的时候将库中的代码拷贝到可执行程序里面,生成的可执行程序就不再需要依赖第三方库,同时生成的可执行程序的体积比较大。

优点:

  • 使用静态库生成的可执行程序,它不需要依赖于第三方库,即使没有第三方库它也可以独立运行,可移植性高。

缺点:

  • 生成的可执行程序体积比较大,比较占资源。 当多个静态链接生成的可执行程序使用同一个库时,内存中会存在大量重复的代码。比如说:9个静态链接生成的可执行程序都要使用C标准库,那么当这些可执行程序同时运行的时候,内存中就会存在9份C标准库的代码。
静态库的打包与使用
静态库的打包

静态库打包的本质:把你的代码生成的二进制.o文件进行打包。

为了便于展示,我使用下面四个文件来为大家展示静态库的打包,其中两个源文件myadd.c和mysub.c,两个头文件myadd.h和mysub.h。

myadd.h:

#pragma once
#include<stdio.h>

int myadd(int a,int b);

myadd.c:

#include"myadd.h"

int myadd(int a,int b)
{
    return a+b;
}

mysub.h

#pragma once
#include<stdio.h>

int mysub(int a,int b);

mysub.c

#include"mysub.h"

int mysub(int a,int b)
{
    return a-b;
}

下面我们来将这四个文件进行打包然后生成一个静态库

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

在这里插入图片描述

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

在这里插入图片描述

注意:ar命令是gnu的归档工具,它常用于将目标文件打包为静态库,ar命令中的-r选项与-c选项分别代表的是repalce和creat。

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

我们还可以使用ar命令的-t选项和-v选项来查看静态库中的目录列表

  • -t:列出静态库重点文件
  • -v(verbose):详细信息

在这里插入图片描述

第三步:将生成的静态库文件与目标文件对应的头文件组织起来

当我们将我们自己的库给别人使用的时候,我们需要创建两个目录,一个用来存放所有的头文件,一个用来存放静态库文件。

因此,我们将myadd.h与mysub.h这两个头文件放到include这个目录下。将静态库文件放到lib这个目录下。然后将这两个目录都放到mylib这个目录下,此时我们若是向把我们自己的库给别人使用,我们只需要将mylib这个文件发给别人就可以了。

在这里插入图片描述

静态库的使用

静态库生成好了之后,我们就来尝试着使用以下这个静态库吧。

我们首先创建一个test.c文件,然后写一段简单的代码用一下加法函数

test.c:

#include"myadd.h"
int main()
{
    int x = 10;
    int y = 20;
    printf("add: %d\n",myadd(x,y));
    return 0;
}

Makefile:

test:test.c
	gcc -o $@ $^ -static
    
.PHONY:clean
clean:
	rm -f test

方法一:使用gcc选项

下面我们就来make一下然后生成可执行程序来执行一下吧。

在这里插入图片描述

咦,我们发现居然出错了,我们打包好的静态库和头文件不就在当前目录下嘛,为什么它这里却又说找不到呢?

这是因为编译器它并不知道你所包含的myadd.h头文件在哪里,所以我们需要指定头文件的搜索路径。

因此我们需要在makefile里面的gcc后面带一个选项就可以指定头文件的搜索路径了

  • -I:指定头文件的搜索路径

在这里插入图片描述

下面我们再来make一下

在这里插入图片描述

可以看到我们这里又报错了,我明明已经指定头文件的搜索路径了,这里为什么又报错了呢?

这是因为头文件myadd.h里面只有加法函数的声明,并没有函数的定义,因此我们还需要指定库文件的搜索路径。

因此我们只需要在makefile里面的gcc后面带一个选项就可以指定库文件的搜索路径了

  • -L:指定库文件搜索路径

在这里插入图片描述

我们接着再来make一下

在这里插入图片描述

我们发现又出错误了,咦不对呀,我明明该做的事情都已经做了,这里为什么还会报错呀?

这是因为你只是告诉了编译器库文件的搜索路径在哪里,但是你并没有告诉你要去链接哪一个库,假如说这个库文件里面有很多个库,那编译器它又怎么知道你想要去链接哪个库呢。

所以我们还需要指定一下我们需要链接库文件中的哪一个库。

因此我们需要在makefile里面的gcc后面再带一个选项去指明我们想要链接库文件中的哪一个库。

  • l:指明想要链接库文件中的哪一个库

在这里插入图片描述

注意:前面说过怎么看一个库文件的名字(去掉前缀去掉后缀),因为我们是要告诉编译器我们想要链接库文件中的哪个库,所以我们只需要在-l选项后面带上它的名字即可。

下面我们再来make一下看看结果吧

在这里插入图片描述

最终我们得到了我们想要的运行结果。

注: 所有的库和头文件只有在当前路径下能够被直接找到,如果当前路径下还包了目录,那么你就需要指明你的搜索路径才行。

大家看了上面的那种静态库使用方式可能会觉得拿这种方式太麻烦了,于是就会问:除了上面的方法之外还有其它的方法嘛?

答案是有的,下面就来告诉大家第二种方法。

方法二:将头文件和库文件放到系统路径下

如果我们不指定搜索路径的话,编译器它就找不到我们的头文件和库文件,除了上面指定搜索路径的法子我们还可以将我们的头文件和库文件放到系统路径下,这样的话编译器就能够找到了。

[root@izuf65cq8kmghsipojlfvpz 4_4]# cp ./mylib/include/*  /usr/include/
[root@izuf65cq8kmghsipojlfvpz 4_4]# cp ./mylib/lib/libmymath.a  /usr/lib64/

在这里插入图片描述

在这里插入图片描述

注意:尽管使用这种方式我们不需要指定头文件的搜索路径和库文件的搜索路径,但是我们还是要告诉编译器我们链接库文件的哪个库。

下面我们来看一下运行结果:

在这里插入图片描述

虽然说这种方法相较于第一种方法而言会简单一点,但是我不建议你使用这种方法,原因有两个:

  1. 将我们的头文件和库文件放到系统路径下,这样做有可能会对系统文件造成污染
  2. 使用第一种方法可以帮你熟悉gcc命令,同时使用第一种方法虽然麻烦但是不会对系统文件造成污染。
动态库的打包与使用
动态库的打包

我们上面学了静态库的打包和使用之后,接下来我们再来学习动态库的打包和使用就会容易很多了。因此动静态库的打包基本类似。下面我们继续使用这四个文件来给大家展示动态库的打包和使用

在这里插入图片描述

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

在这里插入图片描述

注意: 我们这里让所有源文件生成对应的.o目标文件时,需要带上 -fPIC 这个选项

  • -fPIC:产生位置无关码(position independent code)

第二步:将目标文件打包生成动态库

在这里插入图片描述

注意: 我们这里将目标文件打包生成动态库的时候,我们还需要带上-shared选项

然后我们来make一下生成对应的目标文件和动态库:

在这里插入图片描述

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

这里我们换一种方式来将它们两个组织起来,我们在Makefile里面编写一段代码,通过 发布 然后将他们给组织起来。

在这里插入图片描述

下面我们来make output一下,通过发布将头文件和动态库给组织起来:

在这里插入图片描述

此时我们的动态库和头文件就已经打包完毕了,如果后面别人想要用我们的这个库,我们只需要把mylib这个文件给别人就可以了。

动态库的使用

上面我们的动态库打包好之后,我们就来使用一下我们打包好的动态库吧,下面我们依然使用test.c文件来为大家演示动态库的使用

test.c:

#include"myadd.h"
#include"mysub.h"

int main()
{
    int a = 20;
    int b = 10;
    printf("add: %d\n",myadd(a,b));
    printf("sub: %d\n",mysub(a,b));
    
    return 0;
}

Makefile:

test:test.c
	gcc -o $@ $^ -I./mylib/include -L./mylib/lib  -lmymath    
    
.PHONY:clean
clean:
	rm -f test 

下面我们来make一下生成可执行程序然后来执行一下吧

在这里插入图片描述

可以看到我们这里执行可执行程序的时候出错了,这个时候我就比较好奇了:我明明已经指定头文件的搜索路径,指定库文件的搜索路径,以及我们要链接库文件中的哪一个,为什么我们这里还是会报错呢?

这是因为你只是告诉了编译器,你并没有告诉操作系统。不要忘了链接动态库的时候是在程序运行的时候链接的。

可以看到当我们用ldd命令查看test可执行程序的时候,发现找不到这个动态库

在这里插入图片描述

那么问题又来了:我们应该如何解决呢?

  • 方法一:拷贝.so文件到系统共享库路径下

与静态库使用的第二种方法类似,我们将动态库拷到系统路径下。

[root@izuf65cq8kmghsipojlfvpz Test]# cp /root/code/Test/mylib/lib/libmymath.so /usr/lib64

在这里插入图片描述

  • 方法二:更改LD_LIBRARY_PATH

LD_LIBRARY_PATH是程序运行时帮我们找动态库的路径的,因此我们将该动态库所在目录路径添加到LD_LIBRARY这个环境变量中就可以了

[root@izuf65cq8kmghsipojlfvpz Test]# export LD_LIBRARY_PATH=/root/code/Test/mylib/lib

在这里插入图片描述

可以看到我们现在再使用ldd命令的时候就可以找到该动态库了。

下面我们来执行一下这个可执行程序吧

在这里插入图片描述

以上就是动静态库的所有内容了,希望大家可以动手敲一敲,如果文章觉得对你有帮助的话可以给作者三连支持一波。

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值