0


linux下动静态库(初学者必备)

一.动静态库的基本概念

1.动态库:在Linux下动态库是以.so为后缀的文件。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。

2.静态库:在Linux下静态库是以.a为后缀的文件。程序在编译链接时会将库里面的代码拷贝一份放入我们的可执行程序里面。

3.静态链接:将库里的代码拷贝到可执行程序的过程。

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

5.库名称的识别:举个例子:libc.so,只需要去掉前缀lib和后缀.so

6.链接的本质:链接.lib文件和.o文件

二.动静态库的优缺点

下面分别使用动态链接和静态链接生成可执行程序(注意使用gcc进行静态链接时需要带-static)

1.动态链接:

首先编写Makefile:

  1 mytest:test.c
  2   gcc -o $@ $^
  3 .PHONY:clean
  4 clean:
  5   rm -f mytest                                                                                                                                  
~

对应test.c代码:

  1 #include<stdio.h>
  2 int main()
  3 {
  4   printf("hello ksy\n");                                                                                                                        
  5   return 0;
  6 }

2.静态链接:

对应Makefile:

mytest_static:test.c
  2   gcc -o $@ $^ -static                                                                                                                          
  3 .PHONY:clean
  4 clean:
  5   rm -f mytest

test.c代码和动态链接一样:

如果执行命令的时候报错了,可以执行下面两条命令:

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

下面我们可以观察一下这两种方式生成的可执行文件的大小:

我们可以发现才用静态链接的方式生成可执行程序文件的大小时采用动态链接的100倍左右。体积大的多

同样的我们可以使用file指令查看mytest,mytest_static的文件属性:

通过ldd查看可以执行程序依懒的库(注意动态链接生成的可执行程序才有依赖库,静态链接生成的可执行程序是没有依赖库的,静态链接会将库文件的代码拷贝一份到可执行文件中。

最后总结一下:

静态库的优点:

1)发布程序的时候,不需要提供对应的库了,因为库已经被打包到了可执行文件中去了。
2)库的加载速度比较快,因为库已经被打包到可执行文件中去了。
静态库的缺点:

1) 库被打包到应用程序(最后生成的可执行文件)中,如果库很多的话就会导致应用程序的体积很大。
2)库发生了改变,需要重新编译程序,如果源代码比较多,可能编译一遍一天就过去了。

1、动态库的优点

1)执行程序的体积小:程序在执行的时候采取加载动态库,并没有和可执行程序打包在一起
2)动态库更新了,不需要重新编译程序(不是绝对的,前提是函数的接口不变,内容便里没事)
2、动态库的缺点

程序发布的时候,需要把动态库提供给用户
动态库没有被打包到应用程序中,加载速度相对较慢

三.静态库的打包和使用

1.首先我们来回忆一下编译的过程:gcc的编译大致可以分为四个过程

  • gcc预处理器:把.c文件编译成预处理.i文件
  • gcc编译器:把预处理.i文件编译成.s的汇编文件
  • gcc汇编器:把.s汇编文件编译成.o二进制文件
  • gcc链接器:把.o二进制文件链接成一个可执行文件

四个阶段所对应的命令如下:这里以hello.c为例:

  • 预处理:gcc -E hello.c -o hello.i
  • 编译:gcc -S hello.i -o hello.s
  • 汇编:gcc -c hello.s -o hello.o
  • 链接:gcc hello.o -o hello

四个阶段所对应的功能:

预处理:(1)把.c文件中的头文件展开添加到.i预处理文件的开头;(2)然后把.c文件代码添加到.i的头文件内容之后;(3)把宏定义的量值替换为具体的值,去掉原代码中的注释。

编译:把c文件翻译成汇编文件,就是两种程序语法的转化。

汇编:把汇编文件编程二进制文件,此时的文件已经看不出具体的内容。

链接:将函数库中相应的代码组合到目标文件中。

2.静态库打包

静态库打包本质是将我们写的代码编译成.o形成二进制文件之后就不执行后面的过程,然后将形成的文件进行打包。下面说一下制作流程:

一、静态库的制作

1、命名规则

1)lib + 库的名字 + .a
2)例如:libmyMath.a

2、制作步骤:

1)生成对应的.o二进制文件 .c --> .o eg:gcc sum.c -c sum.o
2)将生成的.o文件打包,使用ar rcs + 静态库的名字(libMytest.a) + 生成的所有的.o
3)发布和使用静态库:
1.发布静态库
2.头文件
说明:

把.c文件,也就是源代码转化成.o的二进制文件之后,客户就不知道到你的核心技术具体是怎么实现的了。
ar是对.o的二进制文件进行打包,rc是打包参数,把所有的.o二进制文件打包成一个.a文件,即:静态库。因此:静态库是一个打包了二进制文件的集合。接口API是在头文件中体现出来的。

下面通过一个小例子演示如下打包静态库:为了演示这个过程,我创建了 mypow.c ,myadd.c ,mypow.h,myadd.h

下面展示这几个文件里面的内容:

myadd.h

  1 #ifndef __MYADD_H__
  2 #define __MYADD_H__ 
  3 int add(int x,int y);                                                                                                                           
  4 #endif 

myadd.c

  1 #include"myadd.h"  
  2 int myadd(int x,int y)  
  3 {  
  4   return x+y;                                                                                                                                   
  5 }  
~       

mypow.h

  1 #ifndef __MYPOW_H__
  2 #define __MYPOW_H__ 
  3   int mypow(int x,int y);                                                                                                                         
  4 #endif 
~                 

mypow.c

    1 #include"mypow.h"
W>  2 int mypow(int x,int y)
    3 {
W>  4     if(y==0)
    5     return 1;
    6     if(y==1)
    7     return x;
    8     
    9     return x*mypow(x,y-1);                                                                                                                    
   10 }

下面我们介绍一下生成静态库的想关指令:

ar 工具 对形成的.o文件进行打包(打包的时侯带上 -rc 选项)其中 r 和c分别是replace和creat。在这里我们将库名设置为 myMath.

下面编写Makefile:

  1 libmyMath.a:myadd.o mypow.o
  2   ar -rc $@ $^
  3 myadd.o:myadd.c
  4   gcc -c $<
  5 mypow.o:mypow.c
  6   gcc -c $<
  7 #清除
  8 .PHONY:clean
  9 clean:
 10   rm -rf output *.o *.a
 11 #发表
 12 .PHONY:output
 13 output:                                                                                                                                         
 14   mkdir -p mylib/include
 15   mkdir -p mylib/lib 
 16   cp *.h mylib/include
 17   cp *.a mylib/lib
~
~

说明:

  • include文件夹:存放头文件,提供给用户调用的接口API
  • lib文件夹:存放库文件,即:生成的静态库、动态库
  • src文件夹:存放源文件
  • test.c程序:是用户调用head.h头文件里面的接口,然后在调用静态库里面我们实现的算法(只不过已经不是源码,而是被编译成二进制文件)

下面我们分别执行make 和make out。make out 为发布。

现在我们以及打包成功了.现在我们就可以交给别人使用了。

下面我们将这个打包好的静态库放到一个目录下进行测试:

对应测试test.c内容:

    1 #include<stdio.h>
E>  2 #include"mypow.h"
    3 int main()
    4 {
E>  5   printf("%d\n",mypow(2,5));                                                                                                                  
    6 
    7 }
                 

对应makefile:

  1 mytest:test.c
  2   gcc -o $@ $^
  3 .PHONY:clean 
  4 clean:
  5   rm -f mytest         

我们发现怎么出错了。我们在使用gcc采用静态链接时,我们需要告诉编译器库文件所在路径,头文件所在路径,和你要链接那个库而这三个操作所对应的选项分别为:

参数说明:

  • -I参数:指定头文所在的文件夹名,文件夹名可以和参数贴着写在一起
  • -L参数:指定静态库的文件夹名
  • -l参数:指定静态库的名字,但名字要掐头去尾,eg:原静态库名字为libmyMath.a,在指定-l参数值的时候为:-l myMath

下面我们修改一下Makefile

  1 path =$(shell pwd)
  2 
  3 mytest:test.c
  4   gcc -o $@ $^ -I $(path)/mylib/include -L $(path)/mylib/lib -l myMath -static                                                                  
  5 .PHONY:clean
  6 clean:
  7   rm -f mytest 
~

在这里我们使用path来获取路径使用绝对路径。

下面我们编译程序并将其跑起来:

四动态库的打包和使用

一、动态库相关说明

1、命名规则:

1)lib + 名字 + .so
2)例如:libmyMath.so

2、制作步骤:

1)生成与位置无关的代码 (生成与位置无关的.o)
2)将.o打包成共享库(动态库)
3)发布和使用共享库:
注意:静态库生成的.o文件是和位置有关的用gcc生成和位置无关的.o文件,需要使用参数-fPIC(常用) 或 -fpic


二、动态库制作相关实例

在了解什么叫生成和位置无关的.o文件,我们就要联系之前学过的虚拟地址空间:

**linux上打开一个

运行的程序

进程

),操作系统就会为其分配一个(针对32位操作系统)

0-4G的地址空间

虚拟地址空间

),

虚拟地址空间

不是在

内存中。

静态库生成与

位置有关

二进制文件(.o文件)

虚拟地址空间是从

0开始

的,生成的二进制文件

(.o文件)

会被放到

代码段

.text代码区

。生成的

.o代码每次都被放到同一个位置

,是因为使用的是

绝对地址

。动态库生成与

位置无关

二进制文件(.o文件)

动态库 / 共享库 在程序打包的时候并不会把

.o文件

打包到

可执行文件

中,只是做了

一个记录

,当

程序运行之后

才去把动态库加载到程序中,也就是加载到上图中的

共享库

空间,但是每次加载到

共享库

空间的

位置可能不同

。还是和上面静态库制作同样的目录结构:**

同样的我们依然使用上面的那几个头文件就是复用下面我们来看一下Makfile:

  1 libmyMath.so:myadd.o mypow.o
  2   gcc -shared -o $@ $^
  3 myadd.o:myadd.c
  4   gcc -fPIC -c $<
  5 mypow.o:mypow.c
  6   gcc -fPIC -c $<                                                                                                                               
  7 #清除
  8 .PHONY:clean
  9 clean:
 10   rm -rf output *.o *.so 
 11 #发表
 12 .PHONY:output
 13 output:
 14   mkdir -p mylib/include
 15   mkdir -p mylib/lib 
 16   cp *.h mylib/include
 17   cp *.so mylib/lib

参数说明:

1.-PIC:生成和位置无关的.o文件
2.-shared:共享,就是把.o文件,打包成动态库 / 共享库
3.上面就已经完成动态库的制作,然后把下面的两个文件发布给用户即可调用

4.include/head.h: 头文件,定义接口API
5.lib/libmyMath.so:动态库,封装了编译之后的源代码二进制文件

同样的我们将mylib放到测试的文件下进行测试:

但是由于这里是动态链接,我们不仅要让编译器知道路径在哪还要让系统知道在那,所以我们需要导入环境变量LD_LIBRARY_PATH;

export LD_LIBRARY_PATH=/home/ksy/BK/Test/mylib/lib/。这样之后我们就可以使用了:

标签: linux

本文转载自: https://blog.csdn.net/qq_56999918/article/details/124180384
版权归原作者 一个山里的少年 所有, 如有侵权,请联系我们删除。

“linux下动静态库(初学者必备)”的评论:

还没有评论