静态库与动态库记录

作者: | 更新日期:

几年前曾翻过《链接、装载与库》,现在有人问起静态库动态库的问题,这里就简单记录一下。

本文首发于公众号:天空的代码世界,微信号:tiankonguse

背景

《链接、装载与库》这本书还有一个不好的名字叫做《程序员的自我修养》。
当别人给我说起《程序员的自我修养》这本书的时候, 我就说没听过这本书。
因为这个名字完全没有透露出这本书是讲什么的。
但是, 还是推荐一下这本书, 确实是一本很好的书。

库的作用

编写程序, 应该遵守一个原则:不要重复造轮子。
于是有了下面的演化:

  1. copy
    实现方式: 需要某个功能代码时, 为了不重复造轮子, copy一份过来
    缺点: 功能改变时, 需要通知所有使用者重新copy新的代码
  2. 头文件
    实现方式: 需要的功能放在指定的头文件内, 需要时引入对应的头文件
    缺点: 每次编译生成中间文件耗时, 源码暴漏给使用者
  3. 静态库
    实现方式: 打包成一个二进制文件, 程序编译时使用
    缺点: 每个程序内存中单独拥有这份代码, 功能更新时需要重新编译
  4. 动态库
    实现方式: 打包成一个二进制, 程序运行时动态加载
    优点:所有运行的程序共享同一个动态库, 更新动态库只需要重启程序即可

静态库

静态库: 在链接阶段,链接器会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。

  • 静态库对函数库的链接是放在编译时期完成的。
  • 程序在运行时与函数库再无瓜葛,移植方便。
  • 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

Linux静态库命名规范,必须是lib[your_library_name].alib为前缀,中间是静态库名,扩展名为.a

生成目标文件

参数-c 为生成目标文件.o

g++ -c lockapi.cpp

生成静态库文件

ar 命令可以把目标文件打包成.a静态库文件

ar -crv liblockapi.a lockapi.o

使用静态库

程序中正常引入头文件, 正常使用静态库的函数即可。
Linux下使用静态库,只需要在编译的时候,指定链接库的搜索路径(-L选项)、指定链接库名(不需要lib前缀和.a后缀,-l选项)。

g++ main.cpp -L../lib -llockapi

动态库

  • 动态库把对一些库函数的链接载入推迟到程序运行的时期。
  • 可以实现进程之间的资源共享。(因此动态库也称为共享库)
  • 将一些程序升级变得简单。
  • 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)

动态链接库的名字形式为 libxxx.so,前缀是lib,后缀名为.so

生成目标文件

-fPIC 创建与地址无关的编译程序(pic,position independent code),是为了能够在多个应用程序间共享。

g++ -fPIC -c lockapi.cpp

生成动态库

-shared指定生成动态链接库。

g++ -shared -o liblockapi.so lockapi.o

使用动态库

和静态库类似。

g++ main.cpp -L../lib -llockapi

运行程序

动态库的链接由ld-linux.so完成, 查找动态库的位置有下面几个顺序:

  • 环境变量LD_LIBRARY_PATH
  • /etc/ld.so.cache文件列表, 需要使用ldconfig命令让系统重新加载该列表
  • /lib/目录
  • /usr/lib目录

动态库显示调用

之前写过一些动态库, 然后服务器就自动加载自己的动态库了, 是怎么做到的呢?

原来我们可以在程序中动态加载任意动态库。

#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
//Link with -ldl.
  • dlopen函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。
  • dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。
  • dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
  • dlerror: 当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越