文档章节

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

vesontio
 vesontio
发布于 2015/02/21 18:53
字数 1745
阅读 49
收藏 0
点赞 0
评论 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...

Clark_USTC
2014/10/15
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
Nginx + Passenger部署rails

Nginx + Passenger Nginx則是另一套在Rails世界上還蠻常被使用的第二選擇,相較於Apache雖然功能較少,但運作效率更為良好。要讓Nginx裝上Passgener不需要先裝Nginx,只需要執行以下指令: ...

quanpower
2014/08/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...

guojianrui
06/26
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...

青春无极限
2014/11/12
0
0
Android 终端性能测试——内存篇

前言 做Android QQ性能测试时,内存测试中遇到不少困惑,”各种”内存术语,到底什么意思,怎么获取,这里总结一下。进行的内存测试主要有两个方面,一,OOM的发现和定位,二,同历史版本或竞...

go-skyblue
2014/07/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

shell中的函数、shell中的数组、告警系统需求分析

shell中的函数 格式: 格式: function f_name() { command } 函数必须要放在最前面 示例1(用来打印参数) 示例2(用于定义加法) 示例3(用于显示IP) shell中的数组 shell中的数组1 定义数...

Zhouliang6
27分钟前
1
0
用 Scikit-Learn 和 Pandas 学习线性回归

      对于想深入了解线性回归的童鞋,这里给出一个完整的例子,详细学完这个例子,对用scikit-learn来运行线性回归,评估模型不会有什么问题了。 1. 获取数据,定义问题     没有...

wangxuwei
48分钟前
0
0
MAC安装MAVEN

一:下载maven压缩包(Zip或tar可选),解压压缩包 二:打开终端输入:vim ~/.bash_profile(如果找不到该文件新建一个:touch ./bash_profile) 三:输入i 四:输入maven环境变量配置 MAVEN_HO...

WALK_MAN
今天
0
0
33.iptables备份与恢复 firewalld的9个zone以及操作 service的操作

10.19 iptables规则备份和恢复 10.20 firewalld的9个zone 10.21 firewalld关于zone的操作 10.22 firewalld关于service的操作 10.19 iptables规则备份和恢复: ~1. 保存和备份iptables规则 ~2...

王鑫linux
今天
1
0
大数据教程(2.11):keeperalived+nginx高可用集群搭建教程

上一章节博主为大家介绍了目前大型互联网项目的系统架构体系,相信大家应该注意到其中很重要的一块知识nginx技术,在本节博主将为大家分享nginx的相关技术以及配置过程。 一、nginx相关概念 ...

em_aaron
今天
0
0
Apache Directory Studio连接Weblogic内置LDAP

OBIEE默认使用Weblogic内置LDAP管理用户及组。 要整理已存在的用户及组,此前办法是导出安全数据,文本编辑器打开认证文件,使用正则表达式获取用户及组的信息。 后来想到直接用Apache Dire...

wffger
今天
2
0
HFS

FS,它是一种上传文件的软件。 专为个人用户所设计的 HTTP 档案系统 - Http File Server,如果您觉得架设 FTP Server 太麻烦,那么这个软件可以提供您更方便的档案传输系统,下载后无须安装,...

garkey
今天
1
0
Java IO类库之BufferedInputStream

一、BufferedInputStream介绍 /** * A <code>BufferedInputStream</code> adds * functionality to another input stream-namely, * the ability to buffer the input and to * sup......

老韭菜
今天
0
0
STM 32 窗口看门狗

http://bbs.elecfans.com/jishu_805708_1_1.html https://blog.csdn.net/a1985831055/article/details/77404131...

whoisliang
昨天
0
0
Dubbo解析(六)-服务调用

当dubbo消费方和提供方都发布和引用完成后,第四步就是消费方调用提供方。 还是以dubbo的DemoService举例 -- 提供方<dubbo:application name="demo-provider"/><dubbo:registry address="z...

青离
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部