一个程序的前世今生(三)——动态链接库和静态链接库

简介:

  在程序编写的时候我们会依赖很多底层的实现(除非单纯操作简单的硬件如单片机点灯),所以不可能永远从零开始书写,因此不可避免的会使用到很多库文件,那么什么是库文件。

一:什么是库

  库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常

  本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a.lib)和动态库(.so.dll)。

二:什么是静态库

  在计算机科学中,静态库(英语:Static library, Statically-linked library),或称静态库,是一个外部函数与变量的集合体。静态库的文件内容,通常包含一堆程序员自定的变量与函数,其内容不像动态链接库那么复杂,在编译期间由编译器与链接器将它集成至应用程序内,并制作成目标文件以及可以独立运作的可执行文件。而这个可执行文件与编译可执行文件的程序,都是一种程序的静态创建(static build)。以过去的观点来说,库只能算是静态(static)类型。

  使用静态方式编译hello文件,执行如下命令: gcc -static -o hello hello.c, 标准库将被静态的链接到可执行文件中,多个可执行文件使用同一个静态库将会使静态库拷贝多份,对比静态方式和动态方式编译出的可执行文件

  

  2.1 生成静态库的方法如下:

  • 编译object文件。例如:cc -Wall -c test1.c test2.c,该命令会生成test1.o和、test2.o(其中-Wall表示编译时输出警告)。
  • 创建库文件。ar -cvq libctest.a test1.o test2.o。该命令会得到一个libctest.a文件
  • 可以通过ar -t查看.a文件中包含哪些.o。所以,实际上ar就是一个打包命令,类似tar
  • 构建符号表。ranlib libctest.a用于为.a创建符号表。有些ar命令实际上已经集成了ranlib的功能

 

三: 什么时动态库

  动态库即动态链接库。与静态库相反,动态库在链接时不复制(目标程序中只会存储指向动态库的引用),程序运行时由系统动态加载到内存,系统只加载一次,多个程序共用,节省内存。

  编译可执行文件使用动态库时不添加static参数,参考上图。

  3.1 生成动态库的方法如下:

  • 编译object文件时使用-fPIC(位置无关码)选项:
  • gcc -Wall -fPIC -c *.c

  这个选项的目的是让编译器生成地址无关(position independent)的代码,这是因为,动态库是在运行期间链接的,变量和函数的偏移量是事先不知道的,需要链接以后根据offset进行地址重定向。

  • 使用-shared链接
    gcc -shared -Wl,-soname,libtest.so.1 -o libtest.so.1.0 *.o

  -shared选项是让动态库得以在运行期间被动态链接;-Wl,options是设置传递给ld(链接器)的参数,在上面的例子中,当链接器在链接.o时会执行ld -soname ibtest.so.1

  • 创建软链

  上面的命令将最终输出一个动态库libtest.so.1.0,而出于习惯,会创建两个软链:

  mv libtest.so.1.0 /opt/lib
  ln -sf /opt/lib/libtest.so.1.0 /opt/lib/libtest.so.1
  ln -sf /opt/lib/libtest.so.1.0 /opt/lib/libtest.so

  libtest.so用于在编译期间使用-ltest编译器找到动态库,而libtest.so.1用于在运行期间链接。

 

  3.2 运行期间查找动态库

 

    运行期间,系统需要知道到哪里去查找动态库,这是通过/etc/ld.so.conf配置的。ldconfig用于配置运行时动态库查找路径,实际是更新/etc/ld.so.cache。另外一些环境变量也可以影响查找:(Linux/Solaris: LD_LIBRARY_PATH, SGI: LD_LIBRARYN32_PATH, AIX: LIBPATH, Mac OS X: DYLD_LIBRARY_PATH, HP-UX: SHLIB_PATH)

  3.3 动态加载和卸载的库

  需要应用程序希望设计成插件化的架构,这就需要可以动态加载和卸载库的机制。与动态链接不同的是,动态加载的意思是,编译期间可以对动态库的存在一无所知,而是在运行期间通过用户程序尝试加载进来的。

  通过dlfcn.h中的dlopendlsymdlclose等函数实现此种功能。

  另外,使用到dlfcn机制的可执行文件需要使用-rdynamic选项,它将指示连接器把所有符号(而不仅仅只是程序已使用到的外部符号,但不包括静态符号,比如被static修饰的函数)都添加到动态符号表(即.dynsym表)里。

posted @ 2020-10-05 11:13  Edver  阅读(977)  评论(0编辑  收藏  举报