文档章节

关于共享库(Shared Library)的一些总结

vesontio
 vesontio
发布于 2015/02/21 18:53
字数 1745
阅读 50
收藏 0

共享库,即程序启动时动态加载的库文件。合理地使用共享库,可以有效地实现代码的重用。

编程需求

假设我有一个库文件 vmath,用来实现简单的四则运算:

/* vmath.h */
#ifndef VMATH_H
#define VMATH_H

extern int vm_add(int, int);
extern int vm_sub(int, int);
extern int vm_mul(int, int);
extern int vm_div(int, int);

#endif

各函数的具体实现如下:

/* vmath.c */
#include "vmath.h"

int vm_add(int op1, int op2) {
  return op1 + op2;
}

int vm_sub(int op1, int op2) {
  return op1 - op2;
}

int vm_mul(int op1, int op2) {
  return op1 * op2;
}

int vm_div(int op1, int op2) {
  int res;
  if (op2 != 0) {
    res = op1 / op2;
  }
  return res;
}

都是很简单的代码,就不多解释了。现在我有一个程序,想借用这个 vmath 库来实现简单的四则运算:

/* vm_test.c */
#include <stdio.h>
#include "vmath.h"

int main(int argc, char *argv[]) {
  int op1, op2;

  if (argc != 3) {
    fprintf(stderr, "Usage: %s <op1> <op2>\n", argv[0]);
    return -1;
  }

  sscanf(argv[1], "%d", &op1);
  sscanf(argv[2], "%d", &op2);

  printf("Operand #1: %d\n", op1);
  printf("Operand #2: %d\n", op2);

  printf("Addition: %d\n", vm_add(op1, op2));
  printf("Subtraction: %d\n", vm_sub(op1, op2));
  printf("Multiplication: %d\n", vm_mul(op1, op2));
  printf("Division: %d\n", vm_div(op1, op2));

  printf("Fin\n");
  return 0;
}

我们看到程序头部嵌入了 vmath 的头文件 vmath.h。程序读取 main 函数的两个参数:argv[1] 和 argv[2],将他们转换为 int,并保存进入 op1 和 op2 内,接着通过调用 vmath 的函数,依次计算他们的和、差、积和商。

共享库的生成

那我们希望 vmath 是以共享库的形式存在,则编译步骤较之静态库,也会略有不同,首先对 vmath.c 进行编译:

gcc -c -fPIC vmath.c -o vmath.o

编译器毫无悬念地生成了 vmath.o 对象文件,接下去我们需要将 .o 文件转成 .so 文件,s 即 shared:

gcc -shared -Wl,-soname,libvmath.so.1 vmath.o -o libvmath.so.1.0.0

当前目录下瞬间多出一个文件 libvmath.so.1.0.0,即我们的共享库文件,搞定啦!么么哒!这是不可能的……

要看懂上面这条命令,我们需要首先弄明白共享库的三个名字:

  • 共享库名(Soname):顾名思义,就是库的名称,标准格式为 lib + <库名> + .so + .<主版本号>。以我们的共享库 vmath 为例,那它的 soname 便是 libvmath.so.1。在系统中,soname 通常是以链接(Symbolic link)的形式存在,指向该共享库的真实名;

  • 真实名(Real name):即真正包含代码的文件名,标准格式为 <Soname> + .<次版本号> + .<发行号>。共享库 vmath 的真实名则为 libvmath.so.1.0.0;

  • 连接名(Linker name):即编译器在程序连接阶段,请求共享库时使用的名称,通常为不带任何版本号的 soname,vmath 的连接名则为 libvmath.so。连接名也是以链接的形式存在,指向该共享库的 soname。

现在我们回过头来看之前的那条编译器指令,其作用是将 vmath.o 生成一个 soname 为 libvmath.so.1 的共享库 libvmath.so.1.0.0。

现在我们已经获得我们的共享库 libvmath.so.1.0.0,接下去就是要为其添加 Soname 和 linker name:

ln -sf libvmath.so.1.0.0 libvmath.so.1
ln -sf libvmath.so.1 libvmath.so

前者生成一个指向 libvmath.so.1.0.0 的链接 libvmath.so.1,即共享库的 soname,后者生成一个指向 libvmath.so.1 的链接 libvmath.so,即共享库的 linker name。

万事俱备,接下去就剩下对测试程序 vm_test.c 的编译了:

gcc -c vm_test.c -o vm_test.o
gcc -L. -lvmath vm_test.o -o vm_test

-L 指定共享库所在的路径(path),此处为当前目录。-l 指定所需的共享库,编译器读取到 -lvmath,便会自动在指定的路径内寻找 libvmath.so。就这么妥妥地编译好了,接下去试运行一下:

./vm_test 12 3
./vm_test: error while loading shared libraries: libvmath.so.1: cannot open shared object file: No such file or directory

你妹!显然 vm_test 在启动之际,未能找到共享库。之前我们只是在 gcc 连接阶段指定共享库的所在和名称,但是最终生成的程序本身,并不知道共享库的相关信息。为了程序在启动时,能顺利找到共享库,我们可以采取一系列办法。

共享库的调用

LD_LIBRARY_PATH

第一种方法,我们可以通过环境变量 LD_LIBRARY_PATH 来指定共享库的所在位置:

export LD_LIBRARY_PATH=/home/vesontio/devel/lib:$LD_LIBRARY_PATH
./vm_test 12 3
Operand #1: 12
Operand #2: 3
Addition: 15
Subtraction: 9
Multiplication: 36
Division: 4
Fin

我这里用 export 将共享库所在的路径保存进环境变量 LD_LIBRARY_PATH,这里我们假设共享库文件 libvmath.so.1.0.0 和它的 soname 都保存在 /home/vesontio/devel/lib 下面。然后尝试运行 vm_test,妥妥的。

rpath

第二种方法是使用 rpath,即 runtime search path,是被硬生生写入可执行文件的路径,帮助程序在运行时寻找所需的库文件。rpath 需要在编译生成可执行文件时被指定,我们重新生成 vm_test:

gcc -L. -lvmath -Wl,-rpath=/home/vesontio/devel/lib vm_test.o -o vm_test
unset LD_LIBRARY_PATH
./vm_test 12 3
Operand #1: 12
Operand #2: 3
Addition: 15
Subtraction: 9
Multiplication: 36
Division: 4
Fin

我们重新生成了 vm_test,命令参数唯一的区别就是用 -rpath 指定了共享库所在的路径。在试运行前,我们故意去掉环境变量 LD_LIBRARY_PATH,但是程序还是依旧华丽丽地运行了,因为共享库的路径已经被写死在可执行文件里了。所以 vm_test 不再需要借助 LD_LIBRARY_PATH 来寻找 libvmath.so.1 了。

ldconfig

ldconfig 是 Linux 系统下共享库的管理工具,可以帮助系统内的应用程序搜索所需的共享库。ldconfig 默认会搜索标准的共享库路径,包括 /lib 和 /usr/lib,但是如果你的共享库被放在了其他地方,那就需要手动修改 ldconfig 的配置文件,即 /etc/ld.so.conf:

su
vim /etc/ld.so.conf

修改 ldconfig 的配置文件,你需要管理员 root 权限,用 su 登录之后,将我们的共享库的路径写入 ld.so.conf 内,然后刷新一下 ldconfig 的缓存:

ldconfig

现在再去运行一下 vm_test:

./vm_test 12 3
Operand #1: 12
Operand #2: 3
Addition: 15
Subtraction: 9
Multiplication: 36
Division: 4
Fin

又妥了!但是记住:ldconfig 只会帮助程序在运行时寻找共享库,但是在 gcc 的连接阶段,还是得手动用 -L 指定共享库的路径,并且用 -l 指定所需共享库。

以上三种方法都可以实现共享库的调用,至于哪种方法更合适,那就看开发人员自己的需求,根据实际情况来决定。


© 著作权归作者所有

共有 人打赏支持
vesontio
粉丝 0
博文 3
码字总数 5350
作品 0
杭州
(转)Linux如何解决动态库的版本控制

(换句话说,soname不是真实存在的文件,只是在此库中和将来调用此库的文件中保存的一个名字,在加载时去找这个名字,使用时创建一个软连接来指向真实文件,这样真实文件的版本号就可以升级了...

qinyanhong
2012/04/22
0
0
LINUX总结第13篇:LINUX下动态库及版本号控制

前言 针对同一动态组件的不同版本链接和加载。 一、概念 DLL HELL字面意思是DLL"灾难",是由于com组件(动态库)升级引起的程序不能运行的情况。 原因 有三种可能的原因导致了DLL Hell的发生...

余青木
2015/01/13
0
0
共享库文件ldconfig 配置导致*.so找不到

error whilel oading shared libraries:libluajit-5.1.so.2: cannot open shared解决办法 一般我们在Linux下执行某些外部程序的时候可能会提示找不到共享库的错误, 比如: tmux: error while...

kingkernel
2017/04/14
0
0
error while loading shared libraries: : xxx.so.x

error while loading shared libraries: xxx.so.x" 错误的原因和解决办法 今天在执行一个protobuf程序时,提示error while loading shared libraries: libprotobuf.so.8: cannot open share......

898009427
2017/11/23
0
0
error while loading shared libraries: xxx.so.x"错误的原因和解决办法

一般我们在Linux下执行某些外部程序的时候可能会提示找不到共享库的错误, 比如: tmux: error while loading shared libraries: libevent-1.4.so.2: cannot open shared object file: No suc...

acmfly
2013/11/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

麒麟AI首席科学家现世

8月31日,华为发布了新一代顶级人工智能手机芯片麒麟980,成为全球首款7nm工艺手机芯片,AI方面也实现飞跃,支持人脸识别、物体识别、物体检测、图像分割、智能翻译等。 虽然如今人人都在热议...

问题终结者
昨天
1
0
告警系统主脚本、告警系统配置文件、告警系统监控项目

告警系统主脚本 main.sh内容 #!/bin/bash#Written by aming.# 是否发送邮件的开关export send=1# 过滤ip地址export addr=`/sbin/ifconfig |grep -A1 "ens33: "|awk '/inet/ {pr...

芬野de博客
昨天
2
0
MySQL autocommit探究

-- sessionA:tx_isolation=REPEATABLE-READmysql> select connection_id();+-----------------+| connection_id() |+-----------------+| 28 |+-----------------+......

安小乐
昨天
7
0
c++多线程锁 Mutex  自动判断死锁

c++多线程锁可以使用absl::Mutex std::mutex这两种,下面是demo代码。 使用absl:Mutex的时候打印: [mutex.cc : 1338] RAW: Cycle: [mutex.cc : 1352] RAW: mutex@0x683b68 stack: @ 0x43856......

青黑
昨天
3
0
Blockathon2018(成都站)比赛落幕,留给我们这些区块链应用思考

9月14日,HiBlock区块链社区主办的第二届Blockathon在成都菁融国际广场成功举行,30名参赛者分为5支队伍在48小时内完成区块链项目的创意、开发及路演,经过紧张的开发及现场评选,最终币托(...

HiBlock
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部