背景知识
由于动态链接的诸多优点,大量的程序开始使用动态链接机制,导致系统里面存在数量极为庞大的共享对象。如果没有很好的办法将这些共享对象组织起来,整个系统中的共享对象会给长期的维护、升级带来很大的问题。
共享对象,Shared Object,简称SO。其实从文件结构上来讲,共享对象和共享库没什么区别,Linux下的共享库就是普通的 ELF 共享对象。很多库的开发者都以共享对象的形式让程序来使用,久而久之,共享对象和共享库这两个概念已经很模糊了,所以广义上我们可以将它们看做是同一个概念。
共享库的开发者会不停的更新共享库的版本,以修正原有的 Bug、增加新的功能或改进性能等。由于动态链接的灵活性,使得程序本身和其所依赖的共享库可以分别独立开发和更新。比如程序 A 依赖 libfoo.so,当 libfoo.so 的开发者发布新版本时,理论上我们只需用新的共享库替换旧的即可。但是共享库版本的更新可能会导致接口的更改或删除,这可能导致依赖于该共享库的程序无法正常运行。其实,有几种方法可以解决共享库的兼容性问题,有效办法之一就是使用共享库版本的方法。Linux 有了一套规则来命名系统中的每一个共享库,它规定共享库的文件命名规则必须如下:
libname.so.x.y.z
最前面使用前缀lib,中间为库的名字,后缀为.so,后面跟着 3 个数字组成的版本号。"x"表示**主版本号,"y"表示次版本号,"z"表示发布版本号。各版本号含义如下:
主版本号表示库的重大升级,不同主版本号的库之间是不兼容的;
次版本号表示库的增量升级,即增加一些新的接口符号,且保持原有符号不变;
发布版本号表示库的一些错误修正、性能的改进等,并不增加任何新的接口,也不对接口进行更改。相同主、次版本号,不同发布版本号的库之间完全兼容,依赖于某个发布版号的程序可以在任何一个其他发布版本号中正常运行,而无需做任何修改。
现在的 Linux 中也存在不少不遵守上述规定的"顽固份子",比如最基本的 C 语言库——Glibc。
生成简单的SO共享库
C++库文件
my_add.cpp
#include
extern "C" int add(int a,int b);
int add(int a,int b)
{
return (a+b);
}
C++头文件
my_add.h
extern "C" int add(int a,int b);
C++调用执行文件
test_demo.cpp
#include
#include "my_add.h"
using namespace std;
int main()
{
int res = add(3,5);
cout << "result of 3+5: "<< res << endl;
return 0;
}
CMAKELISTS.TXT
CMakeLists.txt
#2019.12.30
#Leo Ma
cmake_minimum_required( VERSION 2.8 )
project( create_so )
set( CMAKE_BUILD_TYPE Release )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )
add_library(my_add SHARED my_add.cpp)
add_executable(create_so test_demo.cpp)
target_link_libraries(create_so my_add)
运行CREAT_SO结果
生成链接OPENCV库文件的SO共享库
C++库文件
opencv_draw_circle.cpp
#include
#include
#include
#include
using namespace cv;
using namespace std;
extern "C" void draw_circle(int rows, int cols, unsigned char *src_data, unsigned char *ret_data);
void draw_circle(int rows, int cols, unsigned char *src_data , unsigned char *ret_data)
{
//将unsigned char转换成Mat
Mat src = Mat(rows, cols, CV_8UC3, src_data);
//在图像上画一个蓝色的圆
circle(src, Point(60, 60), 10, Scalar(255, 0, 0));
//将Mat转换成unsigned char
memcpy(ret_data, src.data, rows*cols * 3);
}
C++头文件
opencv_draw_circle.h
extern "C" void draw_circle(int rows, int cols, unsigned char *src_data, unsigned char *ret_data);
CMAKELISTS.TXT
CMakeLists.txt
#2019.12.30
cmake_minimum_required( VERSION 2.8 )
project( opencv_draw_circle )
set( CMAKE_BUILD_TYPE Release )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )
# opencv
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_library(opencv_draw_circle SHARED opencv_draw_circle.cpp)
add_executable(test_demo_with_opencv test_demo_with_opencv.cpp)
target_link_libraries(test_demo_with_opencv ${OpenCV_LIBS} opencv_draw_circle)
运行TEST_DEMO_WITH_OPENCV结果
将图片ROI0.png放到build文件夹下,可以看到程序正确调用,画出圆圈。
需要注意
在生成链接opencv库文件的so共享库一节中,在库文件中没有添加opencv库的链接,但在执行文件中添加了opencv库的链接,程序可以正常调用。
实际上,目前生成的so文件并不能被别的工程调用,如果需要被别的工程调用,还需要在CMakeLists.txt添加一行:
target_link_libraries(opencv_draw_circle ${OpenCV_LIBS})
完整CMakeLists.txt为:
#2019.12.30
cmake_minimum_required( VERSION 2.8 )
project( opencv_draw_circle )
set( CMAKE_BUILD_TYPE Release )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )
# opencv
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_library(opencv_draw_circle SHARED opencv_draw_circle.cpp)
#添加库文件libopencv_draw_circle.so的opencv链接,以使库文件单独调用时能够找到相关opencv依赖
target_link_libraries(opencv_draw_circle ${OpenCV_LIBS})
add_executable(test_demo_with_opencv test_demo_with_opencv.cpp)
target_link_libraries(test_demo_with_opencv ${OpenCV_LIBS} opencv_draw_circle)