文档章节

关于共享库(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
共享库文件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
LINUX总结第13篇:LINUX下动态库及版本号控制

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

余青木
2015/01/13
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

没有更多内容

加载失败,请刷新页面

加载更多

降压变换器 Buck

特点 输入输出极性相同。 工作过程 在 MOS 导通时,输入电源通过 L 和 C 滤波后向负载端提供电流;当 MOS 断开后,L 通过二极管续流,保持负载电流连续。输出电压因为占空比的作用,不会超过...

colinux
今天
1
0
Apache日志不记录访问静态文件,访问日志切割,静态元素过期时间设置

Apache配置不记录访问静态文件的日志 网站大多元素为静态文件,如图片、css、js等,这些元素可以不用记录 vhost原始配置 <VirtualHost *:80> ServerAdmin test@163.com DocumentRoo...

野雪球
今天
3
0
聊聊storm的ICommitterTridentSpout

序 本文主要研究一下storm的ICommitterTridentSpout ICommitterTridentSpout storm-core-1.2.2-sources.jar!/org/apache/storm/trident/spout/ICommitterTridentSpout.java public interface......

go4it
今天
4
0
Ubuntu常用操作

查看端口号 netstat -anp |grep 端口号 查看已使用端口情况 netstat -nultp(此处不用加端口号) netstat -anp |grep 82查看82端口的使用情况 查找被占用的端口: netstat -tln netstat -tl...

hc321
昨天
3
0
网站cdn的静态资源突然访问变的缓慢,问题排查流程

1.首先我查看了一下是否自己的网络问题,通过对比其他资源的访问速度和下载速度,确认不是 2.通过ping 和 tracert 判断cdn域名能否正常访问,(最后回想感觉这一步可以省略,因为每次最终能访...

小海bug
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部