Linux环境下静态库和动态库(共享库)的创建及使用

Linux环境下静态库和动态库(共享库)的创建及使用

编写程序时,常常将重复代码写进函数,通过多次调用函数来减少工作量,类似的,当一个工作需要多次调用某些函数和变量时,可以将这些函数、变量封装成库,供其他程序调用,提高开发效率。C语言中有两种库类型,分别为静态库和动态库(共享库)。

静态库

静态库是在目标文件链接成可执行文件过程中,直接将代码载入可执行文件,后期程序的运行不再需要静态库。在Linux系统中,静态库的后缀为.a,静态库的生成分为两步,第一步将源文件编译生成目标文件,第二步通过目标文件生成静态库。具体做法如下:
首次创建测试文件:
test.h

#ifndef __TEST_H__
#define __TEST_H__

#include <stdio.h>


void PrintName();


#endif //__TEST_H__

test.c

#include "test.h"

void PrintName()
{
    printf("------Soul------\n");
}

main.c

#include "test.h"


int main()
{
    PrintName();
    return 0;
}

1、将 test.c 文件经编译器、汇编器生成.o目标文件,其中-c选项为只编译生成目标文件。

gcc -c test.c

2、静态库文件命名格式为lib + 静态库名 + .a,使用ar命令生成静态库,ar命令是Linux系统中用于建立或修改备存文件,或是从备存文件中抽取文件的命令,其中r用于将文件插入备存文件中,这里表示将.o的目标文件加入到静态库中;c用于建立备存文件,即创建静态库;s表示若备存文件中包含了对象模式,可利用此参数建立备存文件的符号表,即为了产生索引。

ar rcs libmytest.a test.o

3、此时静态库便制作好了,当需要使用静态库时,只需在程序中包含静态库中函数的原型声明,并在生成目标文件时指明静态库名(除lib.a外的部分)即可,其中-o选项用于生成指定的输出文件,一般在生成可执行文件时使用;-L指定额外的库函数搜索路径;-l用于连接时搜索指定的函数库。

gcc -o test main.c -L./ -lmytest

4、静态库链接完成后,可以通过先删除静态库再运行可执行文件的方式来测试函数是否已经链接到目标文件中。

rm libmytest.a
./test

注意,静态库使用过程中是有代价的,当多个程序使用同一个库函数时,会在内存中拷贝多份库函数,造成了内存的浪费,而使用动态库可以很好的解决该问题。

动态库(共享库)

动态库在链接生成可执行文件过程中没有拷贝程序段,而是对用到的函数进行了标记,当可执行程序运行过程中需要用到该函数时,再加载动态链接库文件至内存,因此程序运行过程中还需要依赖动态库的存在,其后缀为.so。具体做法如下:
仍然以创建静态库时的test.htest.cmain.c为例进行说明。

1、将 test.c 文件经编译器、汇编器生成.o目标文件。

gcc -c test.c

2、动态库文件命名格式为lib + 动态库名 + .so,使用gcc命令生成动态库,其中-shared用于生成共享目标文件;-fPIC(position independent code)作用于编译阶段,告诉编译器产生与位置无关代码,产生的代码中没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置都可以正确的执行,这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。

gcc -shared -fPIC -o libmytest.so test.o

3、调用动态库编译目标文件。

gcc -o test main.c -L./ -lmytest

4、生成可执行文件后,测试是否成功

./test

此时直接执行文件会报一个No such file or directory的错误:

ubuntu@ubuntu:~/Desktop/test$ ./test
./test: error while loading shared libraries: libmytest.so: cannot open shared object file: No such file or directory

这是因为程序在运行时,首先会去/usr/lib/lib目录中查找需要的动态库文件(Linux系统中的库文件一般放在/usr/lib/lib目录中,其中/lib中存放的是系统启动和运行需要的库,/usr/lib中存放的是应用程序启动或者运行所需要加载的库),如果找不到目标动态库,则会报出以上错误。
这里将创建的动态库拷贝一份放置到/usr/lib中试一下,可以顺利执行文件

root@ubuntu:/home/ubuntu/Desktop/test# cp libmytest.so /usr/lib
root@ubuntu:/home/ubuntu/Desktop/test# su ubuntu
ubuntu@ubuntu:~/Desktop/test$ ls
libmytest.so  main.c  test  test.c  test.h  test.o
ubuntu@ubuntu:~/Desktop/test$ ./test 
------Soul------

至此已经完成了静态库和动态库的创建和使用,在项目中可以采用建立库文件的方式来减少代码开发工作量,查了下静态库和动态库的区别,二者的主要区别为:静态库和动态库是两种共享程序代码的方式,静态库在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系,换句话说就是,当链接静态库生成可执行程序时,静态库中所包含的将被使用的函数会被拷贝到可执行文件中,可执行文件的代码量由于被编译器补充完整而增多,可执行文件一旦生成将不再需要静态库;动态库在链接阶段没有被复制到可执行程序中(仅生成了函数的索引表),而是程序在运行时由系统动态加载到内存中供程序调用。使用动态库的优点是系统只需载入一次动态库,不同的程序可以得到内存中相同的动态库的副本,因此节省了很多内存。

在项目开发过程中,是选择静态库还是动态库呢?这个可以根据实际的编译时间和运行时间需求,动态库的编译要快于静态库,相对的,链接静态库的程序运行时间则要快于动态库,再综合考虑到使用动态库易于升级、链接动态库生成的可执行文件占用内存小、静态库易于部署等特点,合理的制定方案。当静态库和动态库同时存在且同名时,可执行程序会优先调用动态库。

静态库和动态库在使用过程中的路径搜索问题

如上面提到的,动态库建立完成后还不能直接使用,需要将其拷贝至/usr/lib目录中,这是因为执行动态库时系统会按照一定的路径顺序查找动态库文件,如果在指定的路径中没有找到,将无法在可执行程序运行过程中获取库文件中的所需内容。在Linux系统中,动态库的搜索是按照以下路径顺序:
1、查看程序文件的.dynamic 段是否包含了一个叫DT_RPATH的项;该项需要在动态库链接时进行如下代码的声明。其中-Wl,rpath=作用是告知可执行程序到当前目录下寻找动态库文件,虽然在-L选项中指明了当前路径,但是-L只在编译阶段有效,如果不使用-Wl,rpath=选项进行声明,在可执行文件执行时将无法找到存放在当前目录下的动态库从而导致程序的执行失败(假设该动态库仅存放在当前目录下)。

gcc -o test test.c -L./ -lmytest -Wl,rpath=./

2、环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径,LD_LIBRARY_PATH是一个库文件搜索目录列表,将当前目录添加到该列表中,可执行文件就可以找到存放在当前目录中的动态库文件。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./

3、配置文件/etc/ld.so.conf 中指定的动态库搜索路径,/etc/ld.so.conf 库高速缓存文件,其中存放了库名和路径的对应列表,若在该文件中标明了相应的路径,可执行程序的运行过程中就会通过该文件找到对应的路径信息,继而找到动态库。

4、默认的动态库搜索路径/lib,一般/lib中存放的是系统启动和运行需要的库;
5、默认的动态库搜索路径/usr/lib/usr/lib中存放的是应用程序启动或者运行所需要加载的库,将库文件拷贝一份放置该目录下即可,如果权限受限,需要切换至root用户后再进行文件的拷贝。

以上是在程序执行过程中搜索动态库路径的顺序,而在链接静态库和链接动态库时的搜索路径有细微的差别:
链接静态库时的路径搜索:
1、gcc 命令中的参数 -L声明的路径;
2、gcc 的环境变量 LIBRARY_PATH,这里要注意LIBRARY_PATHLD_LIBRARY_PATH是不同的,LIBRARY_PATH指定的是程序静态链接库文件搜索路径,而LD_LIBRARY_PATH是程序动态链接库文件搜索路径。
3、库文件存放目录 /lib/usr/lib/usr/local/lib
链接动态库时的路径搜索:
1、gcc 命令中的参数 -L声明的路径;
2、此外再去环境变量、配置文件、系统库文件目录下寻找。

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页