精华内容
下载资源
问答
  • 2022-03-21 21:37:15

    动态库和静态库


    为什么我们要使用别人(一般是顶尖的工程师写的)的代码?

    为了开发效率和鲁棒性(健壮性)

    如何使用别人的功能?

    1、库 2、开源代码 3、基本的网络功能调用(各自网络接口,语音识别)

    库一般分为动态库和静态库,动态库一般的命名为libc.so,静态库一般的命名为libc.a,去掉前缀lib,去掉.之后的内容,剩下的就是库的名字,这里就是c库,生成可执行程序的方式有两种:动态链接和静态链接

    #include<stdio.h>
    int main()
    {
        printf("hello world!\n");
        return 0;
    }
    

    ldd 可执行程序名字:查看可执行程序的动态链接关系

    image-20220228135628014

    在linux当中,默认情况下形成的可执行程序是动态链接的,可以看到这里是.so为后缀的

    下面我们举个例子来解释一下动态链接和静态链接:

    比如你在宿舍写作业,写到某个题突然不会了,学校旁边有个网吧,于是你就去网吧去查找资料解决问题,解决完了然后回到宿舍继续完成作用,还有一种方法就是你告诉你爸让给你买台电脑,你在宿舍有台电脑,于是你就不需要再去网吧去使用电脑去查阅资料,直接在自己买的电脑上面查找资料即可,前面说的那种方法类似于动态链接,后面的那种方法叫做静态链接

    当调用库频率增加时或者在上面的例子中说去网吧查阅资料频繁时,就变得很麻烦,就和上面的例子一样我们自己买个电脑,这样就省事了不少。相对应的把库中的我的可执行程序中所需要使用的二进制代码,拷贝进我的可执行程序当中,这就叫做静态链接。

    如果想使用静态链接,那就在gcc后面+static选项:一般,为了更好的支持开发,第三方库或者语言库,必须提供两个库,一个叫做静态库,一个叫动态库,方便程序员根据需要进行bin的生成

    image-20220228142033129

    动态链接的特点:体积小,节省资源(磁盘,内存),但是一旦库丢失,bin不可执行

    静态链接的特点:体积大,浪费资源(磁盘,内存),不依赖库,库丢失不影响可执行程序

    静态库与动态库的概念

    • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
    • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
    • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
    • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
    • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

    理解动静态库

    如何打包动静态库与如何使用动静态库

    不想将源代码暴露给别人,就需要打包动静态库,下面我们来看如何打包动静态库:

    我们首先写好自己想要打包的程序代码:

    add.c

    #include"add.h"
    int myadd(int x,int y)
    {
        return x+y;
    }
    

    add.h

    #pragma once
    #include<stdio.h>
    int myadd(int x,int y);
    

    sub.c

    #include"sub.h"
    int mysub(int x,int y)
    {
        return x-y;
    }
    

    sub.h

    #pragma once
    #include<stdio.h>
    int mysub(int x,int y);
    

    我们想让别人能使用我的库,前提是别人需要首先知道你的库能给我提供什么方法,通过头文件体现,然后将源文件编译生成目标文件:

    gcc -c add.c
    

    生成.o文件,这个.o文件可以被别人链接

    gcc -c sub.c
    

    然后使用ar -rc命令生成静态库:

    ar -rc libmymath.a add.o sub.o
    

    我们只需要将头文件和这个刚生成的库文件给别人就好了,别人就可以用该库,形成自己的可执行程序

    我们将头文件和这个刚生成的库文件都放进lib目录下,自己写个程序使用它:

    #include<stdio.h>
    #include"Add.h"
    #include"sub.h"
    int main()
    {
        printf("%d\n",myadd(10,20));
        printf("%d\n",mysub(10,20));
        
        return 0;
    }
    

    这里会报错,找不到:image-20220228144018395

    因为去当前路径或者默认路径(/usr/include)下找头文件,所以需要告诉gcc在指定路径下去找一下头文件:

    -I选项:

    gcc test.c -o mytest -I ./lib
    

    我们发现还会有问题:

    image-20220228144100170

    我们需要告诉gcc除了默认路径(/lib64/libc)以及当前路径之外,在指定的路径下也找一下库文件:

    -L(大写)选项:

    gcc test.c -o mytest -I ./lib -L ./lib -lmymath
    

    -l(小写)库名称:具体你要链接哪一个库

    image-20220228144231837

    可以看到成功执行了

    为什么C语言在编译的时候,从来没有明显的使用过-I -L -l等选项呢?

    1. 库文件和头文件在默认路径下gcc能找到
    2. gcc编译C代码,默认就应该链接libc

    如果我们也不想使用这些选项呢?

    头文件库文件分别拷贝到默认路径下,这个过程叫做库的安装,对于第三方库例如我们自己写的,一般也要带上-lname

    如何制作打包动态库

    生成动态库:

    Makefile的编写

    libmymath.so add.o sub.o
        gcc -shared -o $@ $^
    add.o:add.c
        gcc -fPIC -c $^ 
    sub.o:sub.c
        gcc -fPIC -c $^
    

    gcc选项 -fPIC:产生与位置无关码

    在进程地址空间中有共享区,一般动态库的代码是映射在共享区的,我们将库代码加载到内存,通过页表映射到进程地址空间的共享区,当其他程序想要相同的库代码时,可以通过页表映射到内存中相同库代码的部分,这样就大大减少了空间的使用情况,库有可能加载到物理内存的任意位置,也可能被映射到进程地址空间中共享区的任意区域,所以必须保证库当中的代码怎么执行都不会错,为了保证这个库当中的代码地址不随着加载到内存的位置发生变化而变化,需要告诉gcc,我们进行fPIC选项产生与位置无关码进行编译

    我们通过静态库的那套方式进行编译链接:

    gcc test.c -o mytest -I ./dy_lib -L ./dy_lib -lmymath
    

    我们运行可执行程序,发现无法正常运行:

    image-20220228153922235

    此时和编译器gcc还有关系吗?毫无关系,是运行时出现的问题,属于运行问题,也要能够让系统帮我们找到运行时需要使用的动态库。

    为什么之前动态链接的其他程序可以直接运行呢?因为库可以找到

    环境变量LD_LIBRARY_PATH,需要将库的路径导入到环境变量LD_LIBRARY_PATH中:

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/whb/101/lesson18/dy_lib/test/lib/
    

    image-20220228154358535

    可以看到此时就可以正常执行了

    image-20220228154453152

    更多相关内容
  • Linux动态库和静态库

    万次阅读 多人点赞 2021-10-28 10:56:35
    文章目录动静态库的基本原理认识动静态库静态库各自的特征静态库的打包与使用打包使用动态库的打包与使用打包使用 动静态库的基本原理 动静态库的本质是可执行程序的“半成品”。 我们都知道,一堆源文件...

    动静态库的基本原理

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

    我们都知道,一堆源文件和头文件最终变成一个可执行程序需要经历以下四个步骤:

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

    例如,用test1.c、test2.c、test3.c、test4.c以及main1.c形成可执行文件,我们需要先得到各个文件的目标文件test1.o、test2.o、test3.o、test4.o以及main1.o,然后再将这写目标文件链接起来,最终形成一个可执行程序。
    在这里插入图片描述
    如果我们在另一个项目当中也需要用到test1.c、test2.c、test3.c、test4.c和项目的main2.c或者main3.c分别形成可执行程序,那么可执行程序生成的步骤也是一样的。
    在这里插入图片描述
    而实际上,对于可能频繁用到的源文件,比如这里的test1.c、test2.c、test3.c、test4.c,我们可以将它们的目标文件test1.o、test2.o、test3.o、test4.o进行打包,之后需要用到这四个目标文件时就可以之间链接这个包当中的目标文件了,而这个包实际上就可以称之为一个库。
    在这里插入图片描述
    实际上,所有库本质都是一堆目标文件(xxx.o)的集合,库的文件当中并不包含主函数而只是包含了大量的方法以供调用,所以说动静态库本质是可执行程序的“半成品”。

    认识动静态库

    在Linux下创建文件编写以下代码,并生成可执行程序。

    #include <stdio.h>
    
    int main()
    {
    	printf("hello world\n"); //库函数
    	return 0;
    }
    

    这是最简单的代码,运行结果大家也都知道,就是hello world。
    在这里插入图片描述

    下面我们就通过这份简单的代码来认识一下动静态库

    在这份代码当中我们可以通过调用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的文件类型。
    在这里插入图片描述
    此时我们可以看到,libc-2.17.so实际上就是一个共享的目标文件库,准确来说,这还是一个动态库。

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

    这里可执行程序所依赖的libc.so.6实际上就是C动态库,当我们去掉一个动静态库的前缀lib,再去掉后缀.so或者.a及其后面的版本号,剩下的就是这个库的名字。

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

    [cl@VM-0-15-centos testlib]$ gcc -o mytest-s mytest.c -static
    

    在这里插入图片描述
    此时生成的可执行程序就是静态链接的了,可以明显发现静态链接生成的可执行程序的文件大小,比动态链接生成的可执行程序的文件大小要大得多。

    静态链接生成的可执行程序并不依赖其他库文件,此时当我们使用ldd 文件名命令查看该可执行程序所依赖的库文件时就会看到以下信息。
    在这里插入图片描述
    此外,当我们分别查看动静态链接生成的可执行程序的文件类型时,也可以看到它们分别是动态链接和静态链接的。
    在这里插入图片描述

    动静态库各自的特征

    静态库

    静态库是程序在编译链接的时候把库的代码复制到可执行文件当中的,生成的可执行程序在运行的时候将不再需要静态库,因此使用静态库生成的可执行程序的大小一般比较大。

    优点:

    • 使用静态库生成可执行程序后,该可执行程序就可以独自运行,不再需要库了。

    缺点:

    • 使用静态库生成可执行程序会占用大量空间,特别是当有多个静态程序同时加载而这些静态程序使用的都是相同的库,这时在内存当中就会存在大量的重复代码。

    动态库

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

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

    • 节省磁盘空间,且多个用到相同动态库的程序同时运行时,库文件会通过进程地址空间进行共享,内存当中不会存在重复代码。

    缺点:

    • 必须依赖动态库,否则无法运行。

    静态库的打包与使用

    为了更容易理解,下面演示动静态库的打包与使用时,都以下面的四个文件为例,其中两个源文件add.csub.c,两个头文件add.hsub.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):建立静态库文件。
    [cl@VM-0-15-centos static]$ ar -rc libcal.a add.o sub.o
    

    在这里插入图片描述
    此外,我们可以用ar命令的-t选项和-v选项查看静态库当中的文件。

    • -t:列出静态库中的文件。
    • -v(verbose):显示详细的信息。
    [cl@VM-0-15-centos static]$ ar -tv libcal.a
    

    在这里插入图片描述

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

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

    因此,在这里我们可以将add.hsub.h这两个头文件放到一个名为include的目录下,将生成的静态库文件libcal.a放到一个名为lib的目录下,然后将这两个目录都放到mathlib下,此时就可以将mathlib给别人使用了。
    在这里插入图片描述

    使用Makefile

    当然,我们可以将上述所要执行的命令全部写到Makefile当中,后续当我们要生成静态库以及组织头文件和库文件时就可以一步到位了,不至于每次重新生成的时候都要敲这么多命令,这也体现了Makefile的强大。
    使用Makefile。。。
    编写Makefile后,只需一个make就能生成所有源文件对应的目标文件进而生成静态库。
    在这里插入图片描述
    一个make output就能将头文件和静态库组织起来。
    在这里插入图片描述

    使用

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

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

    现在该目录下就只有main.c和我们刚才打包好的静态库。
    在这里插入图片描述

    方法一:使用选项

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

    • -I:指定头文件搜索路径。
    • -L:指定库文件搜索路径。
    • -l:指明需要链接库文件路径下的哪一个库。
    [cl@VM-0-15-centos project]$ gcc main.c -I./mathlib/include -L./mathlib/lib -lcal
    

    在这里插入图片描述
    此时就可以成功使用我们自己打包的库文件并生成可执行程序。
    在这里插入图片描述
    说明一下:

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

    方法二:把头文件和库文件拷贝到系统路径下

    既然编译器找不到我们的头文件和库文件,那么我们直接将头文件和库文件拷贝到系统路径下不就行了。

    [cl@VM-0-15-centos project]$ sudo cp mathlib/include/* /usr/include/
    
    [cl@VM-0-15-centos project]$ sudo cp mathlib/lib/libcal.a /lib64/
    

    在这里插入图片描述
    需要注意的是,虽然已经将头文件和库文件拷贝到系统路径下,但当我们使用gcc编译main.c生成可执行程序时,还是需要指明需要链接库文件路径下的哪一个库。
    在这里插入图片描述
    此时才能成功使用我们自己打包的库文件并生成可执行程序。
    在这里插入图片描述

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

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

    扩展:
    实际上我们拷贝头文件和库文件到系统路径下的过程,就是安装库的过程。但并不推荐将自己写的头文件和库文件拷贝到系统路径下,这样做会对系统文件造成污染。

    动态库的打包与使用

    打包

    动态库的打包相对于静态库来说有一点点差别,但大致相同,我们还是利用这四个文件进行打包演示:
    在这里插入图片描述

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

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

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

    在这里插入图片描述
    说明一下:

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

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

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

    [cl@VM-0-15-centos dynamic]$ gcc -shared -o libcal.so add.o sub.o
    

    在这里插入图片描述

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

    与生成静态库时一样,为了方便别人使用,在这里我们可以将add.hsub.h这两个头文件放到一个名为include的目录下,将生成的动态库文件libcal.so放到一个名为lib的目录下,然后将这两个目录都放到mlib下,此时就可以将mlib给别人使用了。
    在这里插入图片描述

    使用Makefile

    当然,生成动态库也可以将上述所要执行的命令全部写到Makefile当中,后续当我们要生成动态库以及组织头文件和库文件时就可以一步到位了。
    在这里插入图片描述
    编写Makefile后,只需一个make就能生成所有源文件对应的目标文件进而生成动态库。
    在这里插入图片描述
    一个make output就能将头文件和动态库组织起来。
    在这里插入图片描述

    使用

    我们还是用刚才使用过的main.c来演示动态库的使用。

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

    现在该目录下就只有main.c和我们刚才打包好的动态库。
    在这里插入图片描述
    说明一下,使用该动态库的方法与刚才我们使用静态库的方法一样,我们既可以使用 -I-L-l这三个选项来生成可执行程序,也可以先将头文件和库文件拷贝到系统目录下,然后仅使用-l选项指明需要链接的库名字来生成可执行程序,下面我们仅以第一种方法为例进行演示。

    此时使用gcc编译main.c生成可执行程序时,需要用-I选项指定头文件搜索路径,用-L选项指定库文件搜索路径,最后用-l选项指明需要链接库文件路径下的哪一个库。

    [cl@VM-0-15-centos project]$ gcc main.c -I./mlib/include -L./mlib/lib -lcal
    

    在这里插入图片描述
    与静态库的使用不同的是,此时我们生成的可执行程序并不能直接运行。
    在这里插入图片描述
    需要注意的是,我们使用-I-L-l这三个选项都是在编译期间告诉编译器我们使用的头文件和库文件在哪里以及是谁,但是当生成的可执行程序生成后就与编译器没有关系了,此后该可执行程序运行起来后,操作系统找不到该可执行程序所依赖的动态库,我们可以使用ldd命令进行查看。
    在这里插入图片描述
    可以看到,此时可执行程序所依赖的动态库是没有被找到的。

    解决该问题的方法有以下三个:

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

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

    [cl@VM-0-15-centos project]$ sudo cp mlib/lib/libcal.so /lib64
    

    在这里插入图片描述
    可执行程序也就能够顺利运行了。
    在这里插入图片描述

    方法二:更改LD_LIBRARY_PATH

    LD_LIBRARY_PATH是程序运行动态查找库时所要搜索的路径,我们只需将动态库所在的目录路径添加到LD_LIBRARY_PATH环境变量当中即可。

    [cl@VM-0-15-centos project]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/cl/BasicIO/testlib/project/mlib/lib
    

    在这里插入图片描述
    此时我们再用ldd命令查看该可执行程序就会发现,系统现在就可以找到该可执行程序所依赖的动态库了。
    在这里插入图片描述
    现在我们也就能正常运行该可执行程序了。
    在这里插入图片描述

    方法三:配置/etc/ld.so.conf.d/

    我们可以通过配置/etc/ld.so.conf.d/的方式解决该问题,/etc/ld.so.conf.d/路径下存放的全部都是以.conf为后缀的配置文件,而这些配置文件当中存放的都是路径,系统会自动在/etc/ld.so.conf.d/路径下找所有配置文件里面的路径,之后就会在每个路径下查找你所需要的库。我们若是将自己库文件的路径也放到该路径下,那么当可执行程序运行时,系统就能够找到我们的库文件了。

    首先将库文件所在目录的路径存入一个以.conf为后缀的文件当中。
    在这里插入图片描述
    然后将该.conf文件拷贝到/etc/ld.so.conf.d/目录下。
    在这里插入图片描述
    但此时我们用ldd命令查看可执行程序时,发现系统还是没有找到该可执行程序所依赖的动态库。
    在这里插入图片描述
    这时我们需要使用ldconfig命令将配置文件更新一下,更新之后系统就可以找到该可执行程序所依赖的动态库了。

    [cl@VM-0-15-centos project]$ sudo ldconfig
    

    在这里插入图片描述
    而此时我们也就可以正常运行该可执行程序了。
    在这里插入图片描述

    展开全文
  • (1)使用静态库生成的可执行文件大于动态库生成的可执行文件(程序占用的内存较多) (2)使用静态库生成的可执行文件难以升级,使用动态库生成的可执行文件易于升级 (3)使用静态库生成的可执行文件运行速度快,...
  • 2)在 Linux 静态库的制作过程中发现有别于 Windows 下静态库的制作方法;3)在 Linux 应用程序链接第三方库或者其他静态库的时候发现链接顺序的烦人问题。本文这三个问题针对 Linux 下标准库链接如何巧妙构建 ...
  • linux静态库和动态库

    2015-04-03 22:53:28
    linux静态库和动态库的区别
  • 静态库 文章目录动静态库静态库的概念动态链接的特点静态链接的特点查看文件的动静态链接属性动态链接的机理生成&&使用静态库生成&&使用动态库使用外部库 动静态库的概念 静态库(.a):程序在...

    动静态库

    动静态库的概念

    • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
    • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
    • 在可执行文件开始运行以前,外部函数的机器码由操作系统**从磁盘上的该动态库中复制到内存中,**这个过程称为动态链接(dynamic linking)
    • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
    • 系统默认库命名规则:lib+name.so.version。去掉lib和第一个.后的就是库的名字

    动态链接的特点

    ldd 文件名
    

    image-20220112091749998

    /lib64/libc.so.6是软链接

    image-20220112092056943

    当顺序执行到printf中程序中没有该函数,跳转到库中执行完再返回后续代码执行。动态库并没有把代码拷贝进来,因此程序体积比较小。

    image-20220112092333001

    objdump -d test:

    image-20220301162558586

    • 动态链接的特点

      • 体积小

        • image-20220112092818698
      • 依赖库

        • 因此删库之后连系统中以C为依赖的命令都用不了了。
      • 运行的时候加载的,可以只有一份。而进程在内存中,没跑到对应函数的时候是不会将处于磁盘上的动态库中的对应代码加载到内存中的

    静态链接的特点

    gcc -o $@ $^ -static
    

    image-20220112092923450

    静态库是将对应的printf函数部分拷贝到自己的程序中。

    image-20220112093615253

    objdump -d test-s

    image-20220301162850628

    • 静态链接的特点
      • 将对应代码拷贝进bin,体积比较大
      • bin可移植性强
      • 链接的时候纳入进来,可能比较占资源(硬盘和内存)
        • 比如每个程序都用了printf,那每份程序里面的printf其实就是冗余的

    查看文件的动静态链接属性

    file 文件名
    

    image-20220112093338810

    动态链接的机理

    通过代码区找到映射区的区域,通过页表找到内存中动态库的位置。

    image-20220112101315589

    当前是一个进程,如果是100个动态链接的进程,代码是只读的,其他进程通过各自页表映射进程地址空间的共享区,因此库我文件只要一份就行。

    image-20220117105540492

    而静态链接是把拷贝进来的代码放在代码区了,所以代码区变得很大。

    生成&&使用静态库

    我们要提供两个东西,头文件和库文件。

    一批头文件:有什么方法可以使用,接口参数是什么意思

    一个、多个库文件:具体的实现,供我们动静态链接。

    链接的本质:链接.o文件,lib文件。

    而动静态库就是把所有.o文件打包,命令成.a。

    #ifndef MY_LIB_H
    #define MY_LIB_H
    #include<stdio.h>
    void myprintf();
    
    #endif
    
    
    #include"mylib.h"
    
    void myprintf()
    {
      printf("hello world!\n");
    }
    

    image-20220112102829916

    gcc -c xxx.c
    

    image-20220112102956112

    单纯不带包给别人用只要把.o和.h给别人即可。不过这样使用注意需要makefile的时候加上其.o为依赖项目,不然找不到对应的符号。

    test:test.c mylib.o
    	gcc -o $@ $^ 
    .PHONY:clean
    clean:
    	rm -rf *.o test test-static
    
    #include"mylib.h"
    
    int main()
    {
      myprintf();
    }
    

    image-20220112103527778

    但是数量多起来就不方便了,需要打包。

    1. 自己定义库——一批头文件和源文件,只把源文件处理成.o,使用ar -rc(replace and create)打包成一个静态库
    2. 给别人使用的时候提供头文件和静态库。但是gcc的时候面临问题,比如头文件和静态库不在当前目录下(如果在当前目录下-I就不需要了),需要-I指出头文件目录,需要-L指明库的搜索路径,-l指出库搜索路径中的要用的库名(去掉lib和.后的,可以带空格也可以不带),-static指出静态链接。
    • 第一步:将.o文件打包成静态库
    ar -rc libxxx.a xxx.o xxx.o
    

    ar命令是gnu的归档工具,常用于将目标文件打包为静态库

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

    image-20220112125312073

    • 第二步:使用静态库

    image-20220112125409006

    path = $(shell pwd) #可以不要这个path使用相对路径
    
    test:test.c
    	gcc -o $@ $^ -I $(path)/mylib/include -L $(path)/mylib/lib -l mymath -static
    	
    .PHONY:clean
    clean:
    	rm -f test
    
    • -I选项指定头文件搜索路径
    • -L选项指定库文件搜索路径
    • -l选项指明需要链接库文件路径下的哪一个库

    但是这样挺麻烦的,怎么样不用这些选项呢?

    有一种粗暴的方式是把自己的头文件和库扔到stdio.hlibc.a的系统默认的搜索路径下。但是这样做会污染其他文件环境。所以还是乖乖地放当前路径下文件夹中把选项带上。

    (所谓框架就是这样,下载来之后就是一堆头文件和库)

    sudo cp mylib/include/* /usr/include/
    sudo cp mylib/lib/libmymath.a /lib64/
    

    需要注意的是,虽然已经将头文件和库文件拷贝到系统路径下,但当我们使用gcc编译main.c生成可执行程序时,还是需要-l指明需要链接库文件路径下的哪一个库。

    ./test -lmymath
    

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

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

    生成&&使用动态库

    extern"C"的实例见c++基础的笔记。

    windows中的动态库为.dll,静态库是.lib。

    • 第一步:让所有源文件生成对应的目标文件
    gcc -fPIC -c add.c
    gcc -fPIC -c sub.c
    

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

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

    说明:

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

    -shared 代表共享库

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

    将add.h和sub.h放到include下,将动态库文件放到lib下,两个文件都放到mylib下。此时就可以给别人使用了。

    • 封装成makefile

    生成动态库的makefile

    libmymath.so:myadd.o mysub.o
    	gcc -shared -o $@ $^
    myadd.o:myadd.c
    	gcc -fPIC -c $<
    mysub.o:mysub.c
    	gcc -fPIC -c $<
    
    .PHONY:output
    output:		#打包发给别人的
    	mkdir -p mylib/include
    	mkdir -p mylib/lib
    	cp *.h mylib/include
    	cp *.so mylib/lib
    	
    .PHONY:clean
    clean:
    	rm -rf output *.o libmymath.so
    

    生成.out的makefile

    path = $(shell pwd) #可以不要这个path使用相对路径
    
    test:test.c
    	gcc -o $@ $^ -I $(path)/mylib/include -L $(path)/mylib/lib -l mymath 
    	
    .PHONY:clean
    clean:
    	rm -f test
    

    光是这样生成并不能使用动态库,因为程序要使用的时候操作系统不知道动态库在哪。上述的行为是让gcc知道的-l动态库。

    因此让操作系统知道使用的动态库方法有三:

    1. 我们需要将自己的动态库文件拷贝到系统共享库路径下/usr/lib

      sudo cp mlib/lib/libmymath.so /lib64
      
    2. 更改LD_LIBRARY_PATH

      类似PATH帮我们找可执行程序,这个环境变量帮我们找动态库

    ldd test
    

    image-20220301192820325

    export LD_LIBRARY_PATH=/home/ycb/demo1/mylibso/mylib/lib
    
    ldd test
    

    image-20220301192946244

    此时结果正常运行

    image-20220301193007357

    1. 配置/etc/ld.so.conf.d/+ldconfig更新

    我们可以通过配置/etc/ld.so.conf.d/的方式解决该问题,/etc/ld.so.conf.d/路径下存放的全部都是以.conf为后缀的配置文件,而这些配置文件当中存放的都是路径,系统会自动在/etc/ld.so.conf.d/路径下找所有配置文件里面的路径,之后就会在每个路径下查找你所需要的库。我们若是将自己库文件的路径也放到该路径下,那么当可执行程序运行时,系统就能够找到我们的库文件了。

    image-20220301194049702

    使用外部库

    系统中其实有很多库,它们通常由一组互相关联的用来完成某项常见工作的函数构成。

    gcc -Wall test.c -o test -lm
    
    #include <math.h>
    #include <stdio.h>
    int main(void)
    {
     	double x = pow(4.0, 5.0);
     	printf("%lf\n", x);
     	return 0;
    }
    

    对于这份代码,加不加math.h都能跑出来。当然加了C语言的第三方库的libm.so也能跑出来。

    ls /usr/lib64/libm.so
    

    image-20220112133831480

    但是在老版本中可能会找不到对应的库。(比如上次做yacc的编译原理lab的时候没有-l m导致pow一直没反应。

    展开全文
  • linux 动态库静态库

    2018-04-05 15:21:40
    Ubuntu linux动态库静态库的创建与使用附工程代码....
  • 在windows环境下,我们通常在IDE如VS的工程中开发C++项目,对于生成使用静态库(*.lib)与动态库(*.dll)可能都已经比较熟悉,但是,在linux环境下,则是另一套模式,对应的静态库(*.a)与动态库(*.so)的生成...
  •  linux下的库有两种:静态库和共享库(动态库)。  二者的不同点在于代码被载入的时刻不同。  静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。  共享库的代码是在可执行程序运行时才载入内存...
  •  动态库的制作:  方法一:    gcc -c -fPIC add.c sub.c div.c mul.c //-c表示生成.o目标文件,-f后加一些编译选项,PIC表示与位置无关  gcc -shared -o libmymath.so add.o sub.o mul.o div....
  • 2.动态库和静态库 3.静态库生成和使用 3.1静态库的生成 4.动态库生成和使用 4.1动态库的生成 4.2 动态库的使用 4.2.1 动态链接 4.2.2 动态加载 5.工具使用 5.1 nm工具 5.2ldd命令 1.GCC编译流程 gcc可以...

    目录

    1.GCC编译流程

    2.动态库和静态库

    3.静态库生成和使用

    3.1静态库的生成

     3.2 静态库的使用

    4.动态库生成和使用

    4.1动态库的生成

     4.2 动态库的使用

    4.2.1 动态链接

    4.2.2 动态加载

    5.工具使用

    5.1 nm工具

    5.2 ldd命令


    1.GCC编译流程

    gcc可以让程序员通过它能够更好地控制整个编译过程。

    1.预处理阶段(预处理器cpp)

    生成i的文件

    2.编译阶段(编译器egcs)

    将预处理后的文件转换成汇编语言, 生成文件.s

    3.汇编阶段(汇编器as)

    有汇编变为目标代码(机器代码)生成 .o 的文件

    4.链接阶段(链接器ld)

    连接目标代码, 生成可执行程序.exe

    如图所示:

    gcc的常用项如图:

    选项名

    作用

    -c

    通知gcc取消连接步骤,即编译源码并在最后生成目标文件

    -Dmacro

    定义指定的宏,使它能够通过源码中的#ifdef进行检验

    -E

    不经过编译预处理程序的输出而输送至标准输出

    0

    获得有关调试程序的详细信息,它不能与-o选项联合使用

    -I

    在包含文件搜索路径的起点处添加指定目录

    -l

    提示连接程序在创建最终可执行文件时包含指定的库

    -shared

    生成共享目标文件。通常用在建立共享库时。

    -static

    禁止使用共享连接。

    -O、-O2、-O3

    将优化状态打开,该选项不能与-g选项联合使用

    -O0: 不进行优化处理

    -O1或者-O: 优化生成代码。

    -O2: 进一步优化

    -O3:比 -O2 更进一步优化,包括 inline 函数

    -S

    要求编译程序生成来自源代码的汇编程序输出

    -v

    启动所有警报

    -h

    预处理文件(标头文件)

    -Wall

    在发生警报时取消编译操作,即将警报看作是错误

    -w

    禁止所有的报警

    参考:

    1.Linux基础——gcc编译、静态库与动态库(共享库)_daidaihema的博客-CSDN博客_gcc 动态库编译

    2.GCC 参数详解 | 菜鸟教程

    2.动态库和静态库

    区别如下:

    静态库

    动态库

    特点

    在链接阶段,将汇编生成的.o文件和引用库一起生成可执行文件,这个方式称之为动态连接。

    静态库:libxxxx.a

    特点:

    1.静态库对函数库的链接是放在编译时。

    2.程序在运行时与函数库再无瓜葛

    3.浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

    动态库:libxxx.so

    1.动态库把对一些库函数的链接载入推迟到程序运行的时期。

    2.可以实现进程之间的资源共享

    3.方便程序升级。

    4.可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)

    优点

    程序在运行时与函数库没有关系

    1.动态库把对一些库函数的链接载入推迟到程序运行的时期。

    2.可以实现进程之间的资源共享

    3.方便程序升级。

    4.可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)

    缺点

    1.空间浪费

    2.静态库对程序的更新、部署和发布页会带来麻烦

    运行时候需要连接库,不存在将报错

    备注:inux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。

    參考:https://www.cnblogs.com/codingmengmeng/p/6046481.html

    3.静态库生成和使用

    3.1静态库的生成

    Linux中的静态库与动态库 - 独孤剑—宇枫 - 博客园

    步骤1.书写示例代码:如下add.cpp和mult.cpp头文件和源文件

    Add.cpp如下:

    #include "add.h"
    
    int add(int a,int b)
    {
        return  a+b;
    }
    
    Mult,cpp如下:
    
    #include "mult.h"
    int mult(int a,int  b)
    {
        return  a*b;
    }

    步骤:编译把两个源码文件生成目标文件

    gcc   -c  .\add.cpp .\mult.cpp  -I./

    步骤,生成静态库:3.使用ar -rsv libxxx.a  *.o制作静态库

    ar -rsv    libdemo.a .\add.o .\mult.o

     3.2 静态库的使用

    步骤1:书写可执行程序.main.cpp如下

    #include <mult.h>
    #include <add.h>
    
    int main()
    
    {
        int a=2,b=3;
        cout<<mult(2,3)<<endl;
        cout<<add(2,3)<<endl;
        return 0;
    }

    步骤2:链接静态库:g++ -o main  .\main.cpp   -L./ -ldemo

    步骤3:验证:如下

    4.动态库生成和使用

    4.1动态库的生成

    步骤1和步骤2,和静态库一样,生成.o文件:

    步骤3:生成动态库

    g++  -shared  -fPIC  -o   libdemo.so  add.cpp   mult.cpp 

     4.2 动态库的使用

    4.2.1 动态链接

    这个是经常遇见的,步骤1和静态库的使用步骤1相同

    步骤2:生成可执行文件

    g++  -o main   .\mult.cpp   -L.  -ldemo

    4.2.2 动态加载

    涉及到函数:

    函数原型

    说明

    const char *dlerror(void)

    当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

    void *dlopen(const char *filename, int flag)

    用于打开指定名字(filename)的动态链接库,并返回操作句柄。调用失败时,将返回NULL值,否则返回的是操作句柄。

    void *dlsym(void *handle, char *symbol)

    根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。

    int dlclose (void *handle)

    用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。2.2在程序中使用动态链接库函数。

    解析:

    函数1:dlsym(void *handle, char *symbol)

    filename:如果名字不以“/”开头,则非绝对路径名,将按下列先后顺序查找该文件。

    (1)用户环境变量中的LD_LIBRARY_PATH的值;

    (2)动态链接缓冲文件/etc/ld.so.cache

    (3)目录/lib,/usr/lib

    flag表示在什么时候解决未定义的符号(调用)。取值有两个:

    1) RTLD_LAZY : 表明在动态链接库的函数代码执行时解决。

    2) RTLD_NOW :表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。

    函数2:dlsym(void *handle, char *symbol)

    dlsym()的用法一般如下:

    void(add)(int x,int y); 说明一下要调用的动态函数add

    add=dlsym("xxx.so","add"); 打开xxx.so共享库,取add函数地址

    add(89,369); 两个参数89和369调用add函数

    步骤1:程序使用demo.cpp:

    #include "add.h"
    #include <stdio.h>
    #include <dlfcn.h>
    #include <cstdlib>
    extern "C"
    {
        typedef  int (*fn)(int a,int b);
    }
    
    
    int main(int argc, char const *argv[])
    {
    	void *handle = dlopen("libdemo.so",RTLD_LAZY);
    	const char *err = dlerror();
    	if(err !=NULL){
    		perror("could not open shared object!");
    	}
    	if (NULL != handle) {
    		printf("open sussess\n");
    
    	}
    
    	fn  add_func = (fn)dlsym(handle,"add");
    	cout<<add_func (23,34)<<endl;
    	
    	dlclose(handle);
    	return 0;
    }

    步骤2:编译

    g++  -g   -o  demo   demo.cpp   -L./   -ldemo  -ldl

    步骤3:验证

    ./demo

    如下:

     

    5.工具使用

    5.1 nm工具

    nm可以查看库中有那些函数:列出.o .a .so中的符号信息,包括诸如符号的值,符号类型及符号名称等。所谓符号,通常指定义出的函数,全局变量等等。

    https://www.cnblogs.com/itech/archive/2012/09/16/2687423.html

    5.2 ldd命令

    可以查看可执行文件或者库连接那些文件,及其连接是否正确

    参考:

    Linux中的静态库与动态库 - 独孤剑—宇枫 - 博客园

    静态库和动态库的区别 - 阿玛尼迪迪 - 博客园

    展开全文
  • 我们在写c语言的时候,经常会去包含一个.h的头文件,这个头文件就是文件,比如<stdio.h>,<stdlib.h>这些都是标准的头文件,一般放在/usr/include,也就是说,从这个目录中我们可以找到这个头文件,然后...
  • linux动态库.so文件.a文件是否是独立可用的? 比如我有个dynamic.sostatic.o文件,这两个文件是否可能依赖于别的soa文件,甚至有没有可能依赖于.cpp/.c文件,或者只可能依赖其中的一种?即soa文件是否一定...
  • 在这里将以举例的形式详述...静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。
  • 主要介绍了linux生成(加载)动态库静态库示例方法,大家参考使用
  • 库的存在极大的提高了C/C++程序的复用性,但是库对于初学者来说有些难以驾驭,本文从Linux的角度浅谈Linux下的静态库动态库和动态加载库。  Linux库类型  Linux下可以创建两种类型的库:  1、静态库(.a): 在...
  • Linux系统下使用gcc 5.3编译器编译的boost库1.68版本的动态库和静态库,多线程参数编译,经测试可用
  • 1、linux静态库和动态库区别: 库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库和动态库两种。 静态库:这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个...
  • 1. 静态库和动态库的制作过程区别 1.1 静态库的制作过程 详见博客:Linux中静态库的制作与使用 1.2 动态库的制作过程 详见博客:Linux动态库(共享库)的制作与使用 2. 静态库和动态库在程序编译时使用它们的共同...
  • linux平台的内核5.4.0版本的编译好的ffmpegopencv,其中ffmpeg支持mp4、MOV、AVI文件编解码、读取写入
  • 主要介绍了 Linux静态库动态库实例详解的相关资料,希望通过本文能帮助到大家,让大家理解掌握这部分内容,需要的朋友可以参考下
  • Linux下,库分为静态库和共享库。   库的生成 静态库:库名:Libxxx.a,lib是库的前缀,xxx是库名,.a为静态库的后缀。 第一步:将需要生成静态库的.c文件转换为编译后的.o文件 命令:gcc -S mian.o mian.c 第二步...
  • 本文主要讲解什么是库、库的种类以及静态库和动态库的分析。
  • 本文件创建于2020-08-26,主要是最新版的opencv4.4交叉编译好的动态库和静态库,可以直接在开发板上使用,交叉编译工具版本为arm-linux-gnueabihf-g++ 7.4版
  • 下面小编就为大家带来一篇浅谈Linux C语言动态库静态库。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 下面小编就为大家带来一篇linux 程序、动态库静态库内部添加版本号编译时间详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • linux下库分为两种:静态库动态库 (1)静态库静态库在程序编译的时候会被链接到目标代码里面(即:编译程序时,静态库中的函数被拷贝到当前的程序中),所以我们的程序就不再需要该静态库了。因此编译出来的...
  • Linux 如何使用gcc生成静态库和动态库,使用GCC编译生成静态库和动态库的方法

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 128,857
精华内容 51,542
关键字:

linux动态库和静态库