IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux 动态库 soname 实践 -> 正文阅读

[系统运维]Linux 动态库 soname 实践

xredis

因为项目中使用到了 xredis (C++开发的redis客户端,是对hiredis的C++封装),在 makefile 中发现使用到了 -Wl,-soname 这个语法,之前没怎么了解过,特此记录

makefile 节选如下:

XREDIS_MAJOR=1
XREDIS_MINOR=10.1

# Fallback to gcc when $CC is not in $PATH.
CC:=g++
OPTIMIZATION?=-O3
WARNINGS=-Wall -W -Wwrite-strings
DEBUG?= -g -ggdb
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG) $(ARCH)
REAL_LDFLAGS=$(LDFLAGS) $(ARCH)

DYLIBSUFFIX=so
STLIBSUFFIX=a
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(XREDIS_MAJOR).$(XREDIS_MINOR)
DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(XREDIS_MAJOR)
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD=ar rcs $(STLIBNAME)

尝试 make 一下

[root@localhost xredis-1.10.1]# make
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient.cpp -o src/xRedisClient.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_keys.cpp -o src/xRedisClient_keys.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_sets.cpp -o src/xRedisClient_sets.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_strings.cpp -o src/xRedisClient_strings.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_connection.cpp -o src/xRedisClient_connection.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_hashs.cpp -o src/xRedisClient_hashs.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_lists.cpp -o src/xRedisClient_lists.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_sortedsets.cpp -o src/xRedisClient_sortedsets.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisPool.cpp -o src/xRedisPool.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisFunc.cpp -o src/xRedisFunc.o
g++ -shared -Wl,-soname,libxredis.so.1.10.1 -o libxredis.so  src/xRedisClient.o src/xRedisClient_keys.o src/xRedisClient_sets.o src/xRedisClient_strings.o src/xRedisClient_connection.o src/xRedisClient_hashs.o src/xRedisClient_lists.o src/xRedisClient_sortedsets.o src/xRedisPool.o src/xRedisFunc.o
ar rcs libxredis.a src/xRedisClient.o src/xRedisClient_keys.o src/xRedisClient_sets.o src/xRedisClient_strings.o src/xRedisClient_connection.o src/xRedisClient_hashs.o src/xRedisClient_lists.o src/xRedisClient_sortedsets.o src/xRedisPool.o src/xRedisFunc.o

关键信息为

g++ -shared -Wl,-soname,libxredis.so.1.10.1 -o libxredis.so

这里编译生成的对象是 libxredis.so 这个动态库,其 soname 为 libxredis.so.1.10.1

可以看看编译后生成的产物:

# 有一个 libxredis.a 静态库和一个 libxredis.so 动态库
[root@localhost xredis-1.10.1]# ll libxredis.*
-rw-r--r--. 1 root root 3405976 Oct  5 17:40 libxredis.a
-rwxr-xr-x. 1 root root 1319008 Oct  5 17:40 libxredis.so

可以使用 readelf 命令查看动态库的 soname

# 为了简洁些,使用了 grep 作过滤
[root@localhost xredis-1.10.1]# readelf -d libxredis.so  | grep soname
 0x000000000000000e (SONAME)             Library soname: [libxredis.so.1.10.1]

soname

soname 是 Short for shared object name 的缩写,直译就是共享库(动态库)的缩写。

-Wl,-soname -Wl 告诉编译器将后面的参数传递到连接器。而 -soname 指定了共享库的 soname。

那 soname 是怎么怎么产生作用的呢?

使用 ldd 命令或者 readelf 都可以查看应用程序依赖的动态库

# 为了简洁些,使用了 grep 作过滤
[root@localhost lib]# ldd /usr/xxx/bin/yyy | grep xredis
	libxredis.so.1.10.1 => /usr/xxx/lib/libxredis.so.1.10.1 (0x00007f2c4ea68000)
# 在 ldd 命令打印的结果中,“=>”左边的表示该程序需要连接的共享库之 so 名称,右边表示由 Linux 的共享库系统找到的对应的共享库在文件系统中的具体位置。
# 为了简洁些,使用了 grep 作过滤
[root@localhost lib]# readelf -d /usr/xxx/bin/yyy | grep xredis
 0x0000000000000001 (NEEDED)             Shared library: [libxredis.so.1.10.1]

可以看到 yyy 这个模块是依赖于 libxredis.so.1.10.1 这个动态库,但是还记得我们编译生成的产物么?我们编译生成的可是 libxredis.so 这个库啊~

# 将编译生成的 libxredis.so 放到 /home/wuxt/lib 下
[root@localhost lib]# pwd
/home/wuxt/lib
[root@localhost lib]# ls
libxredis.so

# 将 /home/wuxt/lib 加入动态库查找路径
[root@localhost lib]# cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
/home/wuxt/lib

执行 ldconfig 命令,将动态库加入缓存中

[root@localhost lib]# ldconfig -v
/home/wuxt/lib:
	libxredis.so.1.10.1 -> libxredis.so (changed)

我们发现这里做了个软链接

[root@localhost lib]# ll
total 1292
-rwxr-xr-x. 1 root root 1319008 Oct  5 18:00 libxredis.so
lrwxrwxrwx. 1 root root      12 Oct  5 19:35 libxredis.so.1.10.1 -> libxredis.so

将动态库的 soname 指向动态库本身,这样当 yyy 模块需要依赖 libxredis.so.1.10 时,就会找到具体的 libxredis.so 这个库。

问题

但我认为这个 xredis 的 makefile 编写有问题,回到 xredis 的编译

g++ -shared -Wl,-soname,libxredis.so.1.10.1 -o libxredis.so

soname 应该是 libxredis.so.1 或者是 libxredis.so,生成的对象是带完整版本的 libxredis.so.1.10.1,这样当 xredis 升级至 1.10.2 时,还是由 libxredis.so.1 或者是 libxreids 指向 libxredis.so.1.10.2。

举例几个常用的库的 soname

cJSON

cJSON

看下 makefile 编写

# 节选 cjson 的 makefile
LIBVERSION = 1.7.15
CJSON_SOVERSION = 1
UTILS_SOVERSION = 1

CJSON_SO_LDFLAG=-Wl,-soname=$(CJSON_LIBNAME).so.$(CJSON_SOVERSION)
UTILS_SO_LDFLAG=-Wl,-soname=$(UTILS_LIBNAME).so.$(UTILS_SOVERSION)

编译 cJSON

[root@localhost cJSON]# ll libcjson.so*
lrwxrwxrwx. 1 root root    13 Oct  5 21:10 libcjson.so -> libcjson.so.1
lrwxrwxrwx. 1 root root    18 Oct  5 21:10 libcjson.so.1 -> libcjson.so.1.7.15
-rwxr-xr-x. 1 root root 46731 Oct  5 21:10 libcjson.so.1.7.15

查看 libcjson.so.1.7.15 的 soname

[root@localhost cJSON]# readelf -d libcjson.so.1.7.15 | grep soname
 0x000000000000000e (SONAME)             Library soname: [libcjson.so.1]

系统自带的库:

libz.zo

[root@localhost lib]# ll libz.so*
lrwxrwxrwx. 1 root root     13 Jan 19  2021 libz.so -> libz.so.1.2.7
lrwxrwxrwx. 1 root root     13 Jan 19  2021 libz.so.1 -> libz.so.1.2.7
-rwxr-xr-x. 1 root root 109093 Sep 27  2020 libz.so.1.2.7
[root@localhost lib]# 
[root@localhost lib]# readelf -d libz.so.1.2.7 | grep soname
 0x000000000000000e (SONAME)             Library soname: [libz.so.1]

libc.so 不过这个有点奇怪,libc-2.17.so 的 soname 是 libc.so.6?

[root@localhost lib64]# ll libc.so*
-rw-r--r--. 1 root root 253 Jan 19  2015 libc.so
lrwxrwxrwx. 1 root root  12 May 19  2017 libc.so.6 -> libc-2.17.so
[root@localhost lib64]# 
[root@localhost lib64]# readelf -d libc-2.17.so | grep soname
 0x000000000000000e (SONAME)             Library soname: [libc.so.6]
[root@localhost lib64]# 

总结

Linux 系统的这种动态库管理方式值得我们在实际项目的动态库管理中使用,既保证了动态库的升级,又能得到方便地使用。

Linux 的动态库的命名格式是 libbar.so.x.y.z,最后一个 z 版本的变动一定是兼容的。y 版本升级一般向前兼容。所以这个 y 和 z 不能写死。x 版本变动一般是不兼容升级。所以使用 soname 是最为合理的。

参考:
linux下动态库中的soname

linux下动态库soname简介

Linux动态库soname的使用

Linux下动态链接库文件的realname、soname和linkname

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 21:17:28  更:2022-10-08 21:20:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年9日历 -2024/9/19 9:25:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码