最近在学习linux编程,确切的说应该是使用linux环境,我并不需要像了解windows api那样去了解linux相关api,然后去做linux开发,而是想用一写与平台无关的开元库开发服务器相关程序,从而实现一处开发处处运行的目的,所以使用linux仅仅是知道编译相关的基础功能。如下文章说明了如何将编写好的程序编译成一个类似于windows的dll动态链接库组建,linux叫做so文件(shared object),因为针对一个大工程,我不可能像真正的写makefile那样将工程所有文件编写到makefile里面,所以我们一般使用工程化管理的cmake工具做makefile生成、编译、安装等工作,在使用cmake之前,我们先了解一下最基础的so文件编译生成,然后在描述如何使用cmake生成一个so,具体如下:
一下两种方式的so动态链接库生成实例源码为mymath.h和mymath.cpp,我们将该库编译成libmymath.so,里面的源码如下:
mymath.h 文件内容:
#ifndef _MYMATH_H
#define _MYMATH_H
int Add(int a, int b);
int Sub(int a, int b);
int Mul(int a, int b);
int Div(int a, int b);
#endif
mymath.cpp文件内容:
#include "mymath.h"
int Add(int a, int b)
{
return (a+b);
}
int Sub(int a, int b)
{
return (a-b);
}
int Mul(int a, int b)
{
return (a*b);
}
// 不做异常处理
int Div(int a, int b)
{
return (a/b);
}
测试程序main.cpp内容:
#include "mymath.h"
#include <stdio.h>
int main(int argc, char* argv[])
{
int a = 10, b = 5;
printf("%d + %d = %d\n", a, b, Add(a, b));
printf("%d - %d = %d\n", a, b, Sub(a, b));
printf("%d * %d = %d\n", a, b, Mul(a, b));
printf("%d / %d = %d\n", a, b, Div(a, b));
return 0;
}
1、手动使用g++(或gcc)生成so动态链接库
首先将mymath.cpp生成libmymath.so动态库,我们使用g++ 的-fpic -shared选项,-fpic使生成模块按照可重定位地址方式生成(生成与地址无关的库),-shared选项指定源文件生成.so的动态库文件,使用指令如下:
g++ -fpic -shared -o libmymath.so mymath.cpp
libmymath.so 为生成的目标文件(必须以lib打头),随后为源文件列表
生成后,隐式方式使用方式如main.cpp,直接包含头文件即可,但编译main的时候方式如下
(1)编译
g++ -l. -o main.o -c main.cpp
编译的时候生成main.o对象文件必须指定链接库目录,由于libmymath.so就在本目录使用“-l"后直接跟当前目录”."即可,-o生成main.o目标文件,-c指定源文件列表main.cpp
(2)链接
生成main.o后,下一步就是生成main可执行文件,使用-L指定库搜索目录,使用-l指定链接库名称(去掉前缀lib和.so之后的名字)
g++ -o main -L. main.o -lmymath
(编译链接两个步骤可以合成一个:g++ -L. main.cpp -o main -lmymath)
执行之后生成可执行文件main,但是运行./main,报错:error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
引起运行错误的原因是:程序运行时并不知道动态库所在的路径,因此自然找不到
解决方法有如下三种:
方法一:将动态库拷贝到/lib或/usr/lib或/etc/ld.so.conf文件内所列的一系列目录,这些目录成为“共享目录”,然后执行ldconfig(ldconfig命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表. ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令.)
方法二:将动态链接库所在目录名追加到动态链接库配置文件/etc/ld.so.conf中.
# pwd >> /etc/ld.so.conf
# ldconfig
方法三:
利用动态链接库管理命令ldconfig,强制其搜索指定目录,并更新缓存文件,便于动态装入.
# ldconfig `pwd`
pwd前后有两个反引号`,其目的是取得pwd命令的输出,即当前目录
以上方法任选其一,配置好动态库之后运行./main即可得到输出结果:
[root@localhost dynamic]# ./main
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
10 / 5 = 2
2、使用cmake脚本生成so动态链接库
(1)仅仅生成so文件
如果仅仅生成动态库文件,创建该库名的工程,并为该工程编写CMakeLists.txt,假定工程目录为math目录这在工程目录下创建CMakeLists.txt脚本文件,因为工程目录,所以脚本中必须指定cmake版本和项目信息,并设置该工程输出目标为动态库即可,如此脚本CMakeLists.txt编写如下:
# cmake最低版本要求
cmake_minimum_required(VERSION 2.8)
# 项目信息
project(DynamicLibDemon)
# 搜索本目录搜索源码并赋值给变量
aux_source_directory(. DIR_LIB_SRCS)
# 添加库文件
add_library(mymath SHARED ${DIR_LIB_SRCS})
add_library指定输出类型为动态库SHARED,为了使得cmake编译中间文件不污染源码,特定在该工程目录下创建一个编译目录build,cd进入该目录,输入cmake ..后即可在编译目录build下生成so文件
(2)生成so动态库并生成可执行测试程序
按照这个条件,测试程序为工程目录且假如工程目录为test,里面含有测试测试程序main.cpp,我们可以将可以单独编译的动态库放到工程目录下面子目录math,然后单独编译math目录(有自己的cmake脚本),最后主CMakeLists.txt中动态链接该math目录生成的动态库,改动如下:
目录结构:
- test
-main.cpp
-build
-math
-mymath.h
-mymath.cpp
(1) main.cpp包含头文件改为:
#include "./math/mymath.h"
(2) 子目录math中脚本文件内容
# 搜索本目录搜索源码并赋值给变量
aux_source_directory(. DIR_LIB_SRCS)
# 添加库文件
add_library(mymath SHARED ${DIR_LIB_SRCS})
(3)工程目录脚本内容
# cmake最低版本要求
cmake_minimum_required(VERSION 2.8)
# 项目信息
project(DynamicLibDemon)
# 搜索本目录搜索源码并赋值给变量
aux_source_directory(. DIR_LIB_SRCS)
# 添加库文件
add_executable(Demon ${DIR_LIB_SRCS})
# 链接动态库
target_link_libraries(Demon mymath)
最后,进入build目录,输入cmake .. 编译,然后输入make即可链接生成Demon可执行程序,为了能够使用动态库,必须将动态库拷贝到/lib或/usr/lib,然后ldconfig重新载入到缓存中即可执行Demon,输出结果:
[root@localhost buid]# ldconfig `pwd`
[root@localhost buid]# ./Demon
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
10 / 5 = 2