linux中C的静态库和动态库分析

    从开始学C语言写第一个"hello world"历程到现在,我依然困惑于到底这个程序完整的执行流程是什么样的。不过,现在我正在尝试一点一点的揭开它的面纱。现在,我尝试分析linux中C语言静态库和动态库生成和调用的方法,这可以算作实现最终愿望的一小步。
   
首先说明的是,本文参考于linux 静态库、共享库,这篇文章写的的确不错。笔者结合自己的学习过程,稍作修改。
一、什么是库
  
 本质上说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。由于windows和linux本质的不同,因此二者的二进制库是不兼容的。linux操作系统支持的函数库可分为静态库和动态库,动态库又称为共享库。linux系统有几个重要的目录存放着相应的函数库,如/lib、/usr/lib。

二、静态库和动态库

A.静态库

    这类库的名字一般是libxxx.a,利用静态库编译生成的可执行文件比较大,因为整个函数库的所有数据都被整合进了可执行文件中。
   
它的优点就是程序执行时不需要外部的函数库支持,它的装载速度要比动态库快。它的缺点因为它的优点而产生,静态库升级后,那么你的程序就必须重新编译。当多个应用程序都用到相同的库中,执行这些程序时,会把静态库重复调入内存,而造成内存的浪费。

B.动态库

     这类库的名字一般是libxxx.so,动态库又称共享库。相对于静态函数库,动态函数库在编译的时候没有被编译进可执行文件中,所以动态函数看所生成的可执行文件比较小。程序执行过程中,需要动态申请并调用相应的库才能运行,所以程序的运行环境中必须提供相应的库。
   
它的优点是多个应用程序可以使用一个共享的动态库,启动多个应用程序的时候,只需要动态库加载到内存一次即可,自然比较节省内存。它的缺点是启动加载时候速度比较慢。

三、函数库的创建

实验的所有文件及内容如下:

add.c
#include "heads.h"
int add(int a,int b)
{
        return a+b;
}

sub.c
#include "heads.h"
int sub(int a,int b)
{
        return a-b;
}

heads.h
#ifndef _HEADS_H_
#define _HEADS_H_
extern int add(int,int);
extern int sub(int,int);
#endif

test.c
#include <stdio.h>
#include "heads.h"
int main()
{
        int a=5,b=6;
        printf("a+b=%d ",add(a,b));
        printf("a-b=%d ",sub(a,b));
        return 0;
}

    注意:不管是静态库还是动态库,原材料都是目标文件(*.o),所以先将源文件做成目标文件。

A.静态库的创建

ar -cr libaddsub.a add.o sub.o

ar       静态函数库创建命令
    -c   不管库是否存在,都将创建
    -r    在库中插入模块

B.动态库的创建

gcc -shared -pic -o libaddsub.so add.o sub.o

-shared    生成共享库
-pic         产生位置无关代码

四、函数库的使用

A.静态库的使用

gcc test.c -static -L. -laddsub -o test1 

static    使用静态库连接 

-llibrary 用lib和.a把library包裹起来,并且在系统的标准搜索目录和L指定的目录中搜素该库文件

-Ldir       在l选项搜素目录中添加dir目录

     可以看到程序编译通过并可以执行

    当删除了静态库文件libaddsub.a,发现程序依然能够执行,也就是说使用静态库连接生成的程序执行时不依赖于静态库

B.动态库的使用

gcc test.c -L. -laddsub -o test2

    通过这个命令可以看见仅仅是去掉了static选项。这种情况,gcc优选选择连接动态库,当没有合适动态库的动态库连接时才连接静态库。下边的实验做之前,在/work目录下已经生成了动态库libaddsub.so。

    可以看到生成的test2明显比test1小很多,即证明了动态库连接可以使可执行文件比较小

    但是,直接运行却提示找不到库,这是因为动态库libaddsub.so所在目录/work并没有添加到执行时搜索动态库的目录中去。

五、动态库的运行

(1) 第一种解决的办法是使用环境变量LD_LIBRARY_PATH,这个环境变量就可以指定执行时搜索动态库的目录,这里把/work目录添加进去。

 

(2)把动态库libaddsub.so拷贝到/lib或者/usr/lib中去,这种办法将在第七部分实验

(3)修改配置文件

#cd /etc/ld.so.conf.d  进入配置文件夹

#vi mylib.conf   建立一个配置文件*.conf,这里以mylib.conf为例,并添加动态库所在目录,这里是/work

#ldconfig         使配置生效,实际上是更新ld.so.cache文件

   实验过程及结果如下:

再查看test2所有依赖的动态库

    可以看到,test2依赖的动态库有三个,包含libaddsub.o和两个系统标准的动态库。可执行文件test2的大小为12037Bytes。

    当删除动态库libaddsub.so后,再来运行test2,不能执行,说明动态库在运行过程中还需要动态库libaddsub.so。

六、查看库

nm 可以打印出动态库或者静态库中涉及到的所有符号

ldd 可以查看一个可执行程序依赖的共享库

七、连接和运行选项

A.将静态库libaddsub.a移动到系统标准库目录中测试编译

    编译可以通过,执行也可以通过,说明系统标准库目录中的静态库可以被连接。另外,注意这时的test1大小是423629Bytes

   将static去掉重现编译,发现test1大小变成了11817Bytes,莫非此时libaddsub.a是被动态连接上的?

    但是,将libaddsub.a删除之后,重新运行test1,仍然可以执行,可以得知libaddsub.a是被静态连接的。查看,test1的构造,可以看到它依赖于两个标准动态库。说明,没有static连接器选项时,默认找的是动态库,但是没有动态库时就连接静态库。

    test1中libaddsub.a是被动态连接,而另外两个标准库是被动态连接的。

B.用类似的方法分析动态库

   

    可以看到系统标准库目录中的动态库可以被连接,而且在执行时可以被搜索到。 

    从第五部分可以知道,使用环境变量LD_LIBRARY_PATH可以指定执行时搜索动态库的目录,那么它是否能指定连接器的搜索目录呢?

    从上边的实验,可以看出环境变量LD_LIBRARY_PATH不能指定连接器的搜索目录。

 

参考博客:linux 静态库、共享库

             静态链接与动态链接的区别

                Linux 共享库: LD_LIBRARY_PATH 与ld.so.conf

原文地址:https://www.cnblogs.com/amanlikethis/p/3405991.html