• Linux动静态库


    在这里插入图片描述

    本章代码gitee仓库:动静态库

    1. 静态库

    Linux开发工具gcc/g++篇,此篇文章讲过动静态库的基本概念,不了解的可以先看一下这篇文章。

    现在我们先来制作一个简单的静态库

    mymath.h

    #pragma once
    
    #include
    
    extern int meyerrno;
    int add(int x,int y);
    int sub(int x,int y);
    int mul(int x,int y);
    int div(int x,int y);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    mymath.c

    #include"mymath.h"
    
    int myerrno = 0;
    int add(int x,int y)
    {
      return x+y;
    }
    int sub(int x,int y)
    {
      return x-y;
    }
    int mul(int x,int y)
    {
      return x*y;
    }
    int div(int x,int y)
    {
      if(y == 0)
      {
        myerrno = -1;
        return -1;
      }
      return x/y;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    我们要是想把我们这个方法提供给不然用,2种方法

    1. 直接将源文件给别人

    2. 将源代码打包成库 -> 库+.h文件

      这里头文件是是必须要给的,这个头文件就相当于我们库的一份说明书

    别人要使用我们的方法,其就是将我们的这个.c文件全部编译成.o,然后链接起来形成可执行,现在我们不给源文件,我们自己先编译成.o,然后全部打包给人家,人家就只需要编译自己的.c文件,然后结合我们这个打包文件即可

    指令ar -rc lib.o test1.o test2.o将编译好的.o文件打包起来

    这时候就已经将库打包好了,要将其发布,我们只需再包装一下即可

    将这个打包好的库,使用一下

    image-20231115193958352

    我们这里包含完整路径,告诉这个头文件在哪儿,但一般我们都是直接包含头文件的名称,不会带上路径

    #include"mymath.h"
    int main()
    {
        printf("1+1=%d\n",add(1,1));
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    但是这样的话,编译就不通过了,找不到这个头文件

    在gcc中,它会在当前目录和系统里面搜索,这个当前目录指的是和源代码在同一级目录下

    image-20231115194238561

    我们可以采用gcc main.c -I ./lib/include,告诉gcc去这个路径里面找我们的头文件

    这里的路径不需要再跟上我们的mymath.h,因为我们代码中已经包含了

    image-20231115194540196

    再继续编译,这里没有报找不到头文件的错误,而是报的的是链接错误

    这里我们可以测试一下,让他生成目标文件,不进行链接,这里是没有问题的

    image-20231115194741297

    这是因为找不到我们打包好的静态库,这个原因也是gcc只会去默认的库路径或者是当前目录找库。

    所以我们还要带上gcc main.-I ./lib/include/ -L ./lib/mymathlib/,告诉gcc去这个路径找,执行发现,还是错误

    image-20231115195408595

    报错还是一样,这里是因为我们并没告诉gcc,要链接哪个库,我们后面还要跟上gcc main.c -I ./lib/include/ -L ./lib/mymathlib/ -l mymath

    小写-l后面简易紧跟库名称,库的真实名称是去掉前缀,去掉后缀

    image-20231115200549943

    这些选项带着看着有点冗余,我们之前写的代码是纯C的代码,gcc能在系统中找到这些动静态库。

    而我们现在用的库,属于第三方库,所以要带上这些选项,如果想这样做,我们可以将这个库的头文件拷贝到系统的include,将库文件拷贝到系统的lib64目录中,然后指定-l编译,如果不想这样,我们可以建立软链接

    Tips:

    我们这里写的库中,对除零错误进行了判定

    printf("11/0 = %d errno = %d\n",div(11,0),myerrno);
    
    • 1

    这段代码的myerrno并没有改变,这是因为传参是从右向左传递的,传递myerrno的时候,div函数还未调用,所以myerrno没有改变

    当我们链接完毕之后,可采用指令ldd a.out查看所依赖的动态库,这里却并没有mymath.c

    image-20231115204125246

    gcc默认动态链接,我们这里只提供了静态库,所以gcc只能对这个库进行静态链接。

    这里动静态库都链接了,这也说明,如果需要gcc可以同时链接多个动静态库

    接下来我们将我们的库文件拷贝到系统路径

    image-20231115205743400

    然后不带上IL这些选项,编译发现,并没有出现找不到头文件的错误,出现的是链接错误

    image-20231115205940209

    还是需要链接gcc main.c -lmymath

    image-20231115210033429

    我们上面的对库进行拷贝,本质上就是对这个三方库进行安装,所谓的下载,就是在系统路径下去掉这个库。

    这里第三方库,必须要指明库名称!

    当然,这里并不是很建议将自己写的库安装到系统路径,我们也可以采用软连接的方式

    sudo ln -s /home/Pyh/linux/study/lib_11_15/test/lib/include /usr/include/myinc
    sudo ln -s /home/Pyh/linux/study/lib_11_15/test/lib/mymathlib/libmymath.a /lib64/libmymath.a
    
    • 1
    • 2

    image-20231116091609995

    image-20231116091822359

    这两种方式都可以,但是对于第三方库,我们还是需要安装他们都说明书来进行安装

    2. 动态库

    不管是形成动态库还是静态库,第一步都是先将源文件生成目标.o文件

    image-20231116093631971

    采用指令:

    gcc -fPIC -c myprint.c
    gcc -fPIC -c mylog.c
    
    • 1
    • 2

    然后将其打包,ar指令是专门打包静态库的。形成动态库直接采用的是gcc

    gcc -shared -o libmymethod.so *.o
    
    • 1

    生成可执行程序就是将所有的.o文件链接,可是我们的这两个.o里面并没有main函数,都是提供的方法,所以要跟上shared,表示生成共享库(动态库)

    image-20231116101050170

    这里形成的动态库是有可执行权限的,而静态库是没有的

    静态库是提供源代码的,在形成可执行的时候,它的作用就是将需要的内容拷贝过去,并不会加载到内存

    对于动态库来说,它要和可执行程序产生关联,当可执行程序运行时,要访问动态库的内容,要跳转到动态库,那么动态库就必须加载到内存当中,所以要有可执行权限

    我们可以使用Makefile一次性生成我们的动静态库

    dynamic-lib=libmymethod.so
    static-lib=libmymath.a
    
    .PYONY:all
    all:$(dynamic-lib) $(static-lib)
    
    $(static-lib):mymath.o
    	ar rc $@ $^
    mymath.o:mymath.c
    	gcc -c $^
    
    $(dynamic-lib):mylog.o myprint.o
    	gcc -shared -o $@ $^
    mylog.o:mylog.c
    	gcc -fPIC -c $^
    myprint.o:myprint.c
    	gcc -fPIC -c $^
    
    .PHONY:clean
    clean:
    	rm -rf *.o *.a *.so mylib
    
    .PHONY:output
    output:
    	mkdir -p mylib/include
    	mkdir -p mylib/lib
    	cp *.h mylib/include
    	cp *.a mylib/lib
    	cp *so mylib/lib
    
    • 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

    这个单独使用静态库还是和上面一样,就不做演示了。我们来使用动态库看看,方法也是一样

    image-20231116111000244

    生成可执行程序没有问题,但是运行的时候报错了。

    我们先看一下依赖的动态库ldd a.out

    image-20231116113227772

    发现这里是not found,我们在file a.out看一下,这个程序是使用共享库的

    image-20231116113420002

    那么问题就是出现在了这个not found

    我们这里是动态库,我们只是告诉了编译器,这个库的路径在哪里。可是动态库是要加载到内存的,系统的加载器并不知道它在哪儿。

    这也可以想静态库一样,直接安装到系统路径下,这里就不演示了。

    • 方法1:拷贝到默认库路径

    • 方法2:建立软链接

      sudo ln -s /home/Pyh/linux/study/lib_11_15/test/mylib/lib/ /lib64/libmymethod.so
      
      • 1

      image-20231116164533102

    • 方法3:将自己库所在的路径,添加到系统环境变量LD_LIBRARY_PATH

      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/Pyh/linux/study/lib_11_15/test/mylib/lib
      
      • 1

      image-20231116192936219

      如果重新登录,那么这个配置的环境变量就失效了,要是想长久有效,可以将其添加到系统启动时候的脚本里面~/.bash_profile

      image-20231116213355331

    • 方法4:在目录/etc/ld.so.conf.d里面,加上后缀位.conf的路径文件,然后ldconfing即可(需要root权限)

      image-20231116235237846

      这里不用添加库名字,因为我们这个程序在编译的时候,已经知道了库的名字,所以系统知道链接哪个库

    其实在实际情况下,我们用的库都是别人成熟的库,都采用直接安装到系统当中

    推荐库ncurses,这个是基于终端的,可提供一些图形化界面的库

    3. 动态库的加载

    1. 动态库在程序运行的时候,会被加载到内存当中,而静态库没有;
    2. 创建的动态库被所以的可执行文件动态链接,都要使用,所以动态库也叫共享库。

    所以动态库在系统加载之后,会被所有的进程共享。

    那这个动态库是怎么加载的呢?

    动态库也是文件,文件都是存储在磁盘上。如果我们要执行这个程序需要访问这个库的代码,那么这个库就会加载到内存当中。

    而进程访问内存是页表映射的方式,那么这个加载到内存的库,就会映射到页表,然后页表再映射到进程地址空间的共享区里面,这时候代码区要访问这个库,就跳转共享区,拿到要的方法,执行完毕之后再返回代码区。所以在建立映射之后,我们执行的任何代码,都是在进程地址空间中执行的。

    一个共享库可以给多个进程使用,而一个进程也可以使用多个共享库,这就说明在系统当中,一定会有多个动态库,操作系统要将这些库管理起来,也是六个字先描述,再组织。所以这个共享库有没有加载到内存当中,操作系统是十分清楚的。

    这里还有个问题,动态库中,可能会存在全局变量,例如errno这样的,如果我们的进程将其修改了,会不会影响其他的进程呢?

    这肯定是不会的,如果这个进程要修改这个全局变量,就需要对这个变量进行写入,写入的过程会发生写时拷贝。这也是进程为什么具有独立性的一个原因!

  • 相关阅读:
    Tools_Download
    英语写作中“探索”、“研究”explore、investigate、study的用法
    蓝桥杯打卡Day5
    spark 任务优化
    2024年8月7日(mysql主从 )
    Transactional的7种Propagation 事务配置 开启 关闭 spring springboot mybatis
    MySQL数据库基本操作
    【wsl】window自带linux配置
    基于Echarts实现可视化数据大屏董事会指标体系层级结构系统
    今日好料推荐(ARM嵌入式)
  • 原文地址:https://blog.csdn.net/Dirty_artist/article/details/134453695