• 【Linux】Linux下静态库(.a)和动态库(.so)的制作和使用


    目录

     一、程序工作过程简单介绍

    1. 安装GCC

    2. 源程序工作流程

    3. 使用示例

    4. gcc常用的编译参数选项

    二、静态库的制作及使用

    1. 库是什么

    2. 静态库的制作及使用

    2.1. 制作静态库

    2.2. 使用静态库

     2.3 静态库的制作和使用总结

    三、 动态库的制作和使用

    1 制作动态库

    2. 使用动态库

    2.1 动态库的工作原理

    2.2 定位共享库

    3.  动态库的制作和使用总结


    本文讲解Linux下静态库和动态库的制作和使用,分为三个部分:程序工作的过程、静态库的制作和使用、动态库的制作和使用。
    在介绍静态库和动态库的制作及使用之前,需要先了解程序的工作流程。

     一、程序工作过程简单介绍

    1. 安装GCC

    Linux下使用GCC对.c或.cpp文件进行编译链接生成可执行的文件。
    简单理解,GCC就是一个编译器,把程序源文件编译成可执行文件。

    GCC支持C的不同标准,可以使用命令行参数 -std=c99 决定编译时使用哪一个标准,如: -std=c99表示编译器支持C99标准。
    安装GCC的命令(g++表示C++):

    sudo apt install gcc g++

    查看安装的GCC/G++版本可以使用如下命令:

    1. gcc -v  
    2. gcc --version
    3. g++ -v
    4. g++ --version

    2. 源程序工作流程

    源程序变成可执行程序的流程如下,高级语言经过编译生成汇编语言,汇编语言经过汇编变成计算机可以识别的机器语言,机器语言可以在计算机上直接运行。 

     GCC工作流程:

    源代码进行预处理(-E)变成预处理后源代码(.i),再经过编译器编译(-S)生成汇编代码(.s),汇编代码经过汇编器汇编(-c)成为目标代码(.o),目标代码与库代码、其它目标代码等一起经过链接器链接成可执行程序。

    3. 使用示例

    在Linux/lesson3/文件夹下新建test.c文件,文件内容如下:

    1. #include
    2. int main()
    3. {
    4. #ifdef DEBUG
    5. printf("This is DEBUG info\n");
    6. #endif
    7. // This is one-line comment
    8. printf("Hello Linux!\n");
    9. return 0;
    10. }

    1> 使用gcc命令【-E】进行预处理生成预处理文件(.i),并显示预处理后生成的文件。
                    -E是预处理命令,-o指定生成的文件;
                    ls 查看当前文件夹内的所有文件夹及文件;

    1. -virtual-machine:~/Linux/lesson3$ gcc test.c -E -o test.i
    2. -virtual-machine:~/Linux/lesson3$ ls
    3. test.c test.i

    2> 使用gcc命令【-S】进行编译(只编译,不汇编)生成汇编代码(.s),并显示编译后的文件。
                    -S 编译指定的源文件,不进行汇编,-o指定生成的文件;
                    ls  查看当前文件夹内所有文件夹及文件

    1. -virtual-machine:~/Linux/lesson3$ gcc test.i -S -o test.s
    2. -virtual-machine:~/Linux/lesson3$ ls
    3. test.c test.i test.s

    3> 使用gcc命令【-c】进行编译、汇编,不进行链接,并显示编译、汇编后的文件。
                    -c 编译、汇编指定的文件,但是不进行链接
                    ls 查看当前文件夹内所有文件夹及文件

    1. -virtual-machine:~/Linux/lesson3$ gcc test.s -c -o test.o
    2. -virtual-machine:~/Linux/lesson3$ ls
    3. test.c test.i test.o test.s

    4> 使用gcc命令【-o】链接指定的文件生成可执行文件,并运行可执行文件。
                    -o [file1] [file2]或者 [file2] -o [file1] 把【文件file2】编译成【可执行文件file1】
                    ls 查看当前文件夹内的所有文件夹及文件
                    ./a.out 运行可执行文件

    1. -virtual-machine:~/Linux/lesson3$ gcc test.o -o a.out
    2. -virtual-machine:~/Linux/lesson3$ ls
    3. a.out test.c test.i test.o test.s
    4. -virtual-machine:~/Linux/lesson3$ ./a.out
    5. Hello Linux!

    4. gcc常用的编译参数选项

    gcc编译选项说明示例
    -E

    预处理指定的源文件,不进行编译;

    如果不指定生成的文件,会把预处理后的内容显示在终端;如果指定了生成的文件(如示例),会把预处理后的内容保存到指定的文件内

    gcc test.c -E -o test.i
    -S

    编译指定的源文件,不进行汇编;

    不指定生成的文件会把编译后的文件保存到【源文件名.s】中

    gcc test.i -S

    gcc test.i -S -o test.s

    gcc test.c -S

    gcc test.c -S -otest.s

    -c

    编译、汇编指定的源文件,不进行链接

    不指定生成的文件会把编译后的文件保存到【源文件名.o】中

    gcc test.i -c

    gcc test.s -c -o test.o

    gcc test.c -c

    gcc test.c -c -o test.o

    -o [file1] [file2]

    [file2] -o [file1]

    把文件file2编译成可执行文件file1

    gcc test.c -o a.out

    gcc -o a.out test.c

    gcc test.o -o a.out

    gcc -o a.out test.o

    -I Directory指定include包含文件的搜索目录gcc test.c -I ./include/
    -g在编译时,生成调试信息,表示该程序可以被调试器调试
    -D

    程序编译时,指定一个宏,该宏内的代码会被执行;

    -D后加宏的名字,如示例中【DEBUG】就是代码中宏名

    gcc test.c -o a.out -DDEBUG
    -w不生成任何警告信息
    -Wall生成所有警告信息
    -On表示编译器的优化,n取值范围0~3,-O0表示没有优化,-O1默认,-O3优化级别最高
    -l程序编译时,指定使用的库
    -L指定编译的时候,搜索的库的路径

    -fPIC

    -fpic

    生成与位置无关的代码
    -shared生成共享目标文件,通常在建立共享库时使用
    -std指定c标准,默认是GNU C-std=c99,表示使用c99标准

    二、静态库的制作及使用

    1. 库是什么

    库文件是计算机上的一类文件,可以简单的把库文件看作是代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。

    库是特殊的一种程序,编写库的程序和编写一般的程序区别不大,只不过库不能单独运行。

    库文件有两种:静态库和动态库,动态库又称为共享库,二者区别:
            静态库在程序的链接阶段被复制到程序中;
            动态库/共享库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。

    库当然是有优点的,使用库可以使代码保密,并且方柏霓部署和分发。

    2. 静态库的制作及使用

    静态库在不同平台下文件类型不同。

    window下静态库为.lib文件,libxxx.lib;

    linux下静态库为.a文件,libxxx.a;

    lib是前缀,表示是库文件,是固定的;
    xxx是库名;
    .lib/.a是后缀名,是固定的;

    2.1. 制作静态库

    Linux下静态库的制作过程:

    1. gcc命令获得.o文件:gcc -c xx.c xx.c xx.c
    2. 使用ar工具(archive),将.o文件打包
               ar rcs libxxx.a xxx.o xxx.o
        r - 将文件插入到备存文件中
        c - 建立备存文件
        s - 索引

    示例,有如下文件夹,包含calc和library两个文件夹,calc文件夹内包含
            add.c——加法计算、div.c——除法计算、mult.c——乘法计算、sub.c——减法计算
            head.h——加、减、乘、除的函数声明
            main.c——对加、减、乘、除函数的调用

     各个文件的内容如下:

    head.h:

    1. #ifndef _HEAD_H
    2. #define _HEAD_H
    3. // 加法
    4. int add(int a, int b);
    5. // 减法
    6. int subtract(int a, int b);
    7. // 乘法
    8. int multiply(int a, int b);
    9. // 除法
    10. double divide(int a, int b);
    11. #endif

    add.c:

    1. #include
    2. #include "head.h"
    3. int add(int a, int b)
    4. {
    5. return a+b;
    6. }

    div.c:

    1. #include
    2. #include "head.h"
    3. double divide(int a, int b)
    4. {
    5. return (double)a/b;
    6. }

    mult.c:

    1. #include
    2. #include "head.h"
    3. int multiply(int a, int b)
    4. {
    5. return a*b;
    6. }

    sub.c:

    1. #include
    2. #include "head.h"
    3. int subtract(int a, int b)
    4. {
    5. return a-b;
    6. }

    main.c:

    1. #include
    2. #include "head.h"
    3. int main()
    4. {
    5. int a = 20;
    6. int b = 12;
    7. printf("a = %d, b = %d\n", a, b);
    8. printf("a + b = %d\n", add(a, b));
    9. printf("a - b = %d\n", subtract(a, b));
    10. printf("a * b = %d\n", multiply(a, b));
    11. printf("a / b = %f\n", divide(a, b));
    12. return 0;
    13. }

    制作静态库的过程:

    1>  进入calc文件夹

    1. -virtual-machine:~/Linux/lesson4$ cd calc
    2. -virtual-machine:~/Linux/lesson4/calc$ ls
    3. add.c div.c head.h main.c mult.c sub.c

    2> 编译add.c、div.c、mult.c、sub.c文件,生成为.o文件

    1. -virtual-machine:~/Linux/lesson4/calc$ gcc -c add.c div.c mult.c sub.c
    2. -virtual-machine:~/Linux/lesson4/calc$ ls
    3. add.c add.o div.c div.o head.h main.c mult.c mult.o sub.c sub.o

    3> 把生成的.o文件打包成静态库文件,假定库名为cacl,可以看到已经生成了libcacl.a库文件

    1. -virtual-machine:~/Linux/lesson4/calc$ ar rcs libcacl.a add.o div.o mult.o sub.o
    2. -virtual-machine:~/Linux/lesson4/calc$ ls
    3. add.c div.c head.h main.c mult.o sub.o
    4. add.o div.o libcacl.a mult.c sub.c

    2.2. 使用静态库

    使用静态库时需要用
            -l命令指定使用的库名;
            -L命令指定库所在的路径。

    如上在calc文件夹内,main.c文件生成可执行文件使用到库libcacl.a

    1. -virtual-machine:~/Linux/lesson4/calc$ gcc main.c -o app -lcacl -L ./
    2. -virtual-machine:~/Linux/lesson4/calc$ ls
    3. add.c app div.o libcacl.a mult.c sub.c
    4. add.o div.c head.h main.c mult.o sub.o
    5. -virtual-machine:~/Linux/lesson4/calc$ ./app
    6. a = 20, b = 12
    7. a + b = 32
    8. a - b = 8
    9. a * b = 240
    10. a / b = 1.666667

    如果生成的库在library/lib文件夹内,如下。

     在library文件夹中编译main.c生成可执行文件,那么还需要指明包含文件的路径

    1. -virtual-machine:~/Linux/lesson4/library$ gcc main.c -o app -I ./include/ -lcacl -L ./lib/
    2. -virtual-machine:~/Linux/lesson4/library$ ls
    3. app include lib main.c src
    4. -virtual-machine:~/Linux/lesson4/library$ ./app
    5. a = 20, b = 12
    6. a + b = 32
    7. a - b = 8
    8. a * b = 240
    9. a / b = 1.666667

     2.3 静态库的制作和使用总结

    制作静态库:
            首先使用【gcc -c xxx.c xxx.c】编译为.o文件;然后使用【ar rcs libxxx.a *.o】命令把需要的.o文件打包为静态库文件。

    使用静态库:
            使用【gcc main.c -o app -I [包含的头文件路径] -L [库文件路径] -l[库名]】命令编译生成可执行文件即可。

    三、 动态库的制作和使用

    动态库在不同平台下的文件类型不同

    windows平台下是 libxxx.dll;
    linux平台下是libxxx.so;

    lib是前缀,表明是库文件,固定的;
    xxx是库名;
    .dll/.so是后缀,固定的

    1 制作动态库

    linux下制作动态库的过程:

    1. gcc得到.o文件,得到和位置无关的代码:gcc -c -fpic/fPIC xx.c xx.c
    2. gcc得到动态库,使用-shared参数:gcc -shared xx.o xx.o -o libxx.so

    针对上面的library文件夹,按照如下命令生成动态库

    1> 生成.o文件

    /library/src$ gcc -c -fpic *.c -I ../include/

    2> 生成动态库

    /library/src$ gcc -shared *.o -o libcalc.so

    2. 使用动态库

    2.1 动态库的工作原理

    首先了解下库的工作原理。

    静态库是GCC进行链接时,会把静态库中代码打包到可执行程序中;
    动态库是GCC进行链接时,动态库的代码不会被打包到可执行程序中;

    程序启动后,动态库会被动态加载到内存中,通过ldd 命令可以检查动态库的依赖关系,如下main.c调用动态库生成的可执行文件,使用ldd命令查看依赖关系。

    如下用到的libcalc.so是因为执行程序没有找到该动态库的地址,所以显示【not found】。

    2.2 定位共享库

    动态库使用时需要先定位到它所在的位置。当系统加载可执行代码时,能够知道其所以来的库的名字,但是还需要知道绝对路劲。就需要系统的动态载入器获取绝对路径。对于elf格式的可执行程序,是由ld-linux.so完成的,它现后搜索elf文件的DT_RPATH段——>设置环境变量LD_LIBRARY_PATH——>/etc/ld.so.cache文件列表_》/lib/usr/lib目录找到库文件后将其载入内存。

    具体定位共享库的方法有如下:

    • 第一种,配置环境变量,一次性的,只有一个终端可用。

    在当前终端下输入如下命令,冒号后面是共享库所在的路径。

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/usr/Linux/lesson4/library/lib

    方便的获取动态库的路径,先定位到动态库所在路径下,然后使用【pwd】命令可以获取当前目录的完整路径。

    然后可以输入以下命令查看环境配置是否成功:

    echo $LD_LIBRARY_PATH

    显示如下表示配置环境变量LD_LIBRARY_PATH成功:

     这种配置只能用在当前终端,属于一次性的。

    • 第二种,用户级别的配置,只对当前用户有效。对.bashrc文件配置

    首先【cd】命令进入home目录,【ll】命令查看home目录下的文件及文件夹

    然后【vim .bashrc】命令对.bashrc文件编辑,最后一行加入如下内容,保存退出。

     最后使用【. .bashrc】或【source .bahsrc】命令更新配置。 

     使用【echo $LD_LIBRARY_PTAH】命令查看是否添加成功:

    •  第三种,系统级别的环境变量配置,需要root权限。

    首先,系统级别的环境配置需编辑【/etc/profile】文件

    $ sudo vim /etc/profile

    在文件最后添加:
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/usr/Linux/lesson4/library/lib

    然后,使用命令更新编辑后的文件

    $ source /etc/profile

    查看是否添加成功

    $ echo $LD_LIBRARY_PATH

    显示添加的路径表示添加成功。

    • 第四种,修改【/etc/ld.so.cache】文件

    【/etc/ld.so.cache】文件是二进制文件,不能修改,那么通过修改/etc/ld.so.conf文件,把共享库的路径添加,最后更新配置,具体命令:

    检查是否添加成功,转到可执行程序目录,使用【ldd xxx】命令查看依赖库能否找到。

    • 第五种,把共享库文件放到/lib和/use/li目录下,不建议使用该种方法。

    3.  动态库的制作和使用总结

    制作动态库

    首先使用【gcc -c -fpic xx.c xx.c】或【gcc -c -fPIC xx.c xx.c】命令编译生成.o文件,然后使用【gcc -shared xx.o xx,o -o libxx.so】命令生成共享库。

    使用动态库

    需要先定位到共享库,可以配置环境变量:【LD_LIBRARY_PATH】或者修改【/etc/ld.so.conf】文件,使共享库能够被定位到。

    以上参考:课程列表_牛客网 (nowcoder.com)icon-default.png?t=M666https://www.nowcoder.com/study/live/504/1/8

  • 相关阅读:
    Godot引擎小白入门指南
    华为机试真题 C++ 实现【分班问题】
    QEvent(事件)
    外语配音软件“布谷鸟配音“和ffmepg转换软件的使用以及SYD_Calculator提取文件到C语言
    某医疗机构:建立S-SDLC安全开发流程,保障医疗前沿科技应用高质量发展
    Dijkstra算法略解
    Shader的属性和语义
    Linux二进制方式安装mysql8
    JSD-2204-处理登录成功的管理的权限列表-前后端分离-Day13
    2023年汉字小达人市级比赛什么时候进行?如何准备市级比赛?
  • 原文地址:https://blog.csdn.net/sinat_41752325/article/details/126278114