Linux中创建和使用静态库&动态库

  • 库本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行

Linux下库的种类###

  • linux下的库有两种:静态库和共享库(动态库)。
  • 二者的不同点在于代码被载入的时刻不同。
  • 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。
  • 共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

库文件是如何产生的在linux下

  • 静态库的后缀是.a,它的产生分两步
  • Step1 由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表
  • Step2 ar rcsv 命令将很多.o转换成.a,成为静态库
  • 动态库的后缀是.so,它由gcc加特定参数编译产生。

库文件是如何命名的,有没有什么规范

  • 在linux下,库文件一般放在/usr/lib和/lib下,
  • 静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称
  • 动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号,minor是副版本号

创建和使用静态库

下面通过一个简单的例子来说明:

1 首先创建unsgn_pow.c文件

	/*unsgn_pow.c:库程序*/
	unsigned long long  unsgn_pow(unsigned int x,unsigned int y)
	{
    unsigned long long   res = 1;
    if (y == 0)
    {
    res=1;
    }
    else if (y == 1)
    {
    res = x;
    }
    else
    {
     res = x * unsgn_pow(x,y-1);
    }
    return res;
	}

2 然后创建pow_test.c文件,它会调用unsgn_pow()函数

	/*pow_test.c*/

	#include <stdio.h>
	#include <stdlib.h>
	
	int main(int argc,char *argv[])
	{
	        unsigned int x , y;
	        unsigned long long   res;
	        if ((argc<3) || (sscanf(argv[1],"%u",&x) != 1) || (sscanf(argv[2],"%u",&y)) != 1)
	        {
	         printf("Usage: pow base expnent
");
	         exit(1);
	        }
	        res = unsgn_pow(x,y);
	        printf("%u ^ %u = %llu
",x,y,res);
	        exit(0);
	}

3 创建静态库

	hh@hh-virtual-machine:~$ gcc -c unsgn_pow.c
	hh@hh-virtual-machine:~$ ar rcsv libpow.a unsgn_pow.o
	r - unsgn_pow.o
  • r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。
    默认的 情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
  • c:创建一个库。不管库是否存在,都将创建。
  • s:写入一个目标文件索引到库中,或者更新一个存在的目标文件索引。甚至对于没有任何变化的库也作该动作。对一个库做ar s等同于对该库做ranlib。
  • v:该选项用来显示执行操作选项的附加信息。

4 使用静态库(编译主程序,它会链接到刚生成的静态库libpow.a)

	hh@hh-virtual-machine:~$ gcc -o pow_test pow_test.c -L. -lpow
	hh@hh-virtual-machine:~$ ./pow_test 2 10
	2 ^ 10 = 1024

创建和使用动态库

1 首先生成目标文件,但是此时要加编译器选项-fpic和链接器选项-shared

	hh@hh-virtual-machine:~$ gcc -fPIC -Wall -c unsgn_pow.c

-wall 允许发出gcc提供的所有有用的报警信息

2 其次生成动态库

	hh@hh-virtual-machine:~$ gcc -shared -o libpow.so unsgn_pow.o

生成动态库libpow.so,libpow.so就是我们生成的目标动态库。我们以后使用动态库和main.c程序生成可执行程序

说明:

以上两部也可以合成一步搞定:

	hh@hh-virtual-machine:~$ gcc -fpic -shared -o libpow.so unsgn_pow.c

3 使用动态链接库

 在编译程序时,使用动态链接库和静态库是一致的,使用”-l库名”的方式,在生成可执行文件的时候会链接库文件。

	hh@hh-virtual-machine:~$ gcc -o pow_test pow_test.c -L. -lpow

4 运行

在运行时,程序链接的动态链接库需要在系统目录下才行,所以在运行可执行程序前,需要注册动态库的路径名。

可采用以下几种方式:

a. 在linux下最方便的解决方案是拷贝libPpow.so到绝对目录 /lib 下(但是,要是超级用户才可以,因此要使用sudo)。就可以生成可执行程序了

b.第二种方法是:将动态链接库的目录放到程序搜索路径中,可以将库的路径加到环境变量LD_LIBRARY_PATH中实现:

		export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH

执行此命令后也可以生成可执行程序。

                    hh@hh-virtual-machine:~$ cp libpow.so /lib
		hh@hh-virtual-machine:~$ ./pow_test 2 10
		2 ^ 10 = 1024
		hh@hh-virtual-machine:~$ 

收获

  • 主要是对动态链接和静态链接有了更深刻的认识。
  • 静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。换句话说,函数和过程的代码就在程序的exe文件中,该文件包含了运行时所需的全部代码。当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了宝贵的内存资源。
  • 而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间建立链接关系。当要执行所调用DLL中的函数时,根据链接产生的重定位信息,Windows才转去执行DLL中相应的函数代码。一般情况下,如果一个应用程序使用了动态链接库,Win32系统保证内存中只有DLL的一份复制品。
  • 静态库与动态库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终的EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。
  • 采用动态链接库的优点:(1)更加节省内存;(2)DLL文件与EXE文件独立,只要输出接口不便,更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性。
  • 动态链接库的两种链接方法:
    1 装载时动态链接,这种用法的前提是在编译之前已经明确知道要调用DLL中的哪几个函数,编译时在目标文件中只保留必要的链接信息,而不含DLL函数的代码;当程序执行时,利用链接信息加载DLL函数代码并在内存中将其链接入调用程序的执行空间中,其主要目的是便于代码共享。
    2 运行时动态链接,这种方式是指在编译之前并不知道将会调用哪些DLL函数,完全是在运行过程中根据需要决定调用哪个函数,并用LoadLibrary和GetProc-Address动态获得DLL函数的入口地址。