• Linux(11)动静态库生成与安装



    对之前内容的复习:👉 [Linux](5)gcc编译、gdb调试、make/makefile项目自动化构建工具、进度条小程序_世真的博客-CSDN博客

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

    生成动静态库

    有这样四个文件:

    mymath.h 头文件什么一个累加函数

    // mymath.h
    #pragma once
    
    #include 
    #include 
    
    // 累加
    extern int addToVal(int from, int to);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    累加函数的实现

    // mymath.c
    #include "mymath.h"
    
    int addToVal(int from, int to)
    {
        assert(from <= to);
        int result = 0;
        for (int i = from; i <= to; ++i)
        {
            result += i;
        }
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    myprint.h 头文件声明一个打印函数

    // myprint.h
    #pragma once 
    
    #include 
    #include 
    
    extern void Print(const char* msg);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    打印函数的实现,打印字符串和当前时间

    #include "myprint.h"
    
    void Print(const char* msg)
    {
        printf("%s : %lld\n", msg, (long long)time(NULL));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    makefile:

    mymath.o:mymath.c
    	gcc -c mymath.c -o mymath.o
    myprint.o:myprint.c
    	gcc -c myprint.c -o myprint.o
    .PHONY:clean
    clean:
    	rm -f *.o
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    将它们都处理成可重定位二进制文件(.o)

    使用 ar -rc 命令将 .o 文件打包成静态库

    ar -rc libmymath.a mymath.o myprint.o
    
    • 1

    生成动静态库的makfefile:

    .PHONY:all
    all:libmymath.so libmymath.a
    
    libmymath.so:mymath.o myprint.o
    	gcc -shared -o libmymath.so mymath.o myprint.o
    mymath.o:mymath.c
    	gcc -fPIC -c mymath.c -o mymath.o
    myprint.o:myprint.c
    	gcc -fPIC -c myprint.c -o myprint.o
    
    
    libmymath.a:mymath_s.o myprint_s.o
    	ar -rc libmymath.a mymath_s.o myprint_s.o
    mymath_s.o:mymath.c
    	gcc -c mymath.c -o mymath_s.o -std=c99
    myprint_s.o:myprint.c
    	gcc -c myprint.c -o myprint_s.o -std=c99
    
    .PHONY:lib
    lib:
    	mkdir -p lib-static/lib
    	mkdir -p lib-static/include
    	cp *.a lib-static/lib
    	cp *.h lib-static/include
    	mkdir -p lib-dyl/lib 
    	mkdir -p lib-dyl/include
    	cp *.so lib-dyl/lib 
    	cp *.h lib-dyl/include
    
    .PHONY:clean
    clean:
    	rm -rf *.o *.a *.so lib-static lib-dyl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    使用动静态库

    静态库

    1. 通过相对路径包含头文件
    #include "./lib-static/include/mymath.h"
    #include "./lib-static/include/myprint.h"
    
    int main()
    {
        int start = 0;
        int end = 100;
        int result = addToVal(start, end);
        printf("result: %d\n", result);
        Print("hello world");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 将头文件和我们编写的程序放在同一路径下,包含头文件就可以不用带路径了
    2. 将头文件放在系统头文件路径下/user/include,包含头文件也可以不用带路径了

    光是这样写还不行,它会报链接错误

    [CegghnnoR@VM-4-13-centos test]$ gcc mytest.c -o mytest
    /tmp/cc8M56hj.o: In function `main':
    mytest.c:(.text+0x21): undefined reference to `addToVal'
    mytest.c:(.text+0x42): undefined reference to `Print'
    collect2: error: ld returned 1 exit status
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这是因为找不到库文件,所以库文件也应该拷贝到系统路径下。

    将自己的头文件和库文件,拷贝到系统路径下的过程就叫做库的安装。

    不过这种方式不推荐,容易污染系统头文件和库。


    我们还有一种方式去只用自己的库

    gcc mytest.c -o mytest -I ./lib-static/include/ -L ./lib-static/lib/ -l mymath
    
    • 1

    使用 gcc -I 选项指定头文件所在目录,使用 -L 选项指定库文件所在目录,使用 -l 选项指定要包含的库名(去掉前缀lib和后缀.a)

    动态库

    和静态库一样去指定头文件目录、库文件目录、库名

    gcc mytest.c -o mytest -I ./lib-dyl/include/ -L ./lib-dyl/lib/ -l mymath
    
    • 1

    编译成功,但是运行会出问题:

    [CegghnnoR@VM-4-13-centos test]$ ./mytest
    ./mytest: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
    
    • 1
    • 2

    使用 ldd 查看程序依赖的库:

    [CegghnnoR@VM-4-13-centos test]$ ldd mytest
    	linux-vdso.so.1 =>  (0x00007ffdc9def000)
    	/$LIB/libonion.so => /lib64/libonion.so (0x00007f2c913c4000)
    	libmymath.so => not found
    	libc.so.6 => /lib64/libc.so.6 (0x00007f2c90edd000)
    	libdl.so.2 => /lib64/libdl.so.2 (0x00007f2c90cd9000)
    	/lib64/ld-linux-x86-64.so.2 (0x00007f2c912ab000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    运行时链接不到库,因为我们只告诉了 gcc 各种路径。而动态库是程序运行时去链接的,我们的进程找不到对应的动态库。

    解决方法

    1. 将动态库拷贝到系统路径下/lib64

    2. 导入环境变量 —— 程序运行时会在环境变量中查找自己需要的动态库路径

      LD_LIBRARY_PATH
      
      • 1

      将自己的库文件的绝对路径添加到这个环境变量中,注意用 : 分隔

    3. 系统配置文件

    4. 软链接


    程序运行需要把代码和数据加载到内存,那么动态库被加载到内存的哪个区了呢?

    动态库被加载到物理内存后,会被映射到虚拟进程地址空间的共享区。如果有其他进程也要使用这个动态库,系统不会重新加载这个动态库,而是直接将它映射到该进程的共享区。所以一个动态库在内存中只会加载一份,是可以被多个进程共享的,所以动态库又叫共享库。

  • 相关阅读:
    守护进程daemon
    LeetCode 623. 在二叉树中增加一行
    设计模式-原型模式-浅克隆和深克隆在Java中的使用示例
    图片叠加_图片压缩
    Vue3知识总结-2
    Vue 路由懒加载, VueRouter一步完成Vue的路由懒加载 一行代码搞定懒加载
    探寻JWT的本质:它是什么?它有什么作用?
    公共事业管理概论复习题及答案
    11.ElasticSearch系列之搜索相关性算分机制
    记一次MySQL5初始化被kill的问题排查
  • 原文地址:https://blog.csdn.net/CegghnnoR/article/details/127619795