编译小结(3) 动态库(.so)编译及二种调用技巧

  动态库的概念和优势在这就不多说了,这里只说编译和调用。
下面会一步步演示如何用编译使用动态库及如何解决问题。
  当然如何还会具体的演示调用技巧。
       1.直接用编译方式使用动态库。
        动态地将程序和动态库链接,并让其在执行时加载库(如果它已在内存中则不会重复加载)
       2.直接在代码中加载动态库。
        程序在需要时再去加载一个特定的库(已加载则不必),然后调用该库中的某一特定函数.

/*
例子目录结构如下,具体代码附在页面最后。
[root@ol64 test4]# ls *
main.c

lib:
add.c  calc.h  sub.c

操作系统: Oracle Linux 6.4
编译版本:
[root@ol64 test4]# gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3)
*/

编译动态库:

直接用gcc命令行搞定:
[root@ol64 lib]# gcc -o libcalc.so -m64 -fPIC -shared add.c sub.c
[root@ol64 lib]# ldd libcalc.so
	linux-vdso.so.1 =>  (0x00007fff811ff000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fc3c51e9000)
	/lib64/ld-linux-x86-64.so.2 (0x0000003e66600000)
[root@ol64 lib]# size libcalc.so
   text	   data	    bss	    dec	    hex	filename
   1183	    488	     16	   1687	    697	libcalc.so
1.直接用编译方式使用动态库:
  gcc方式:

  一步一步看我的演示过程:
         //会说找不到头文件
	[root@ol64 test4]# gcc -m64 -o demo2 main.c
	main.c:3:18: error: calc.h: No such file or directory

       //加上-I参数后可以找到头文件了,但会提示说找到被调用的两个函数 "sub"和"add"
	[root@ol64 test4]# gcc -m64 -I./lib -o demo2 main.c
	/tmp/ccWoakff.o: In function `main':
	main.c:(.text+0x14): undefined reference to `sub'
	main.c:(.text+0x25): undefined reference to `add'
	collect2: ld returned 1 exit status

	//把动态库参数加上,这次顺序编译成功了。但用ldd 查看,会发现 "libcalc.so => not found"
	[root@ol64 test4]# gcc -m64 -L./lib -lcalc -I./lib -o demo2 main.c
	[root@ol64 test4]# ldd demo2
		linux-vdso.so.1 =>  (0x00007fff1612e000)
		libcalc.so => not found
		libc.so.6 => /lib64/libc.so.6 (0x0000003e66e00000)
		/lib64/ld-linux-x86-64.so.2 (0x0000003e66600000)
参数说明请看: 编译小结(1) GCC多平台安装(Linux,Aix,HP-UX,Solaris)及编译参数
//不出所料,编译时会报找不到libcalc.so库文件
	[root@ol64 test4]# ./demo2
	./demo2: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory
解决方法有好多种,
  1. 在 LD_LIBRARY_PATH 中加入路径
  export LD_LIBRARY_PATH=`pwd`/lib
  2. /etc/ld.so.conf中用ldconfig载入
        2.1 echo "/xcl/test4/lib" >> /etc/ld.so.conf
        2.2 ldconfig
        2.3 ldconfig -v|grep libcalc.so 
  3. 建个软链接到.lib或/usr/lib中
  具体为什么可以查看后面附的动态库搜索路径顺序.

在编译时的解决方法:
关键在 " -Wl,-rpath" 参数。它可以同时指定多个路径,中间用","分隔
	 [root@ol64 test4]# gcc -m64 -L./lib -lcalc -I./lib -Wl,-rpath,lib/ -o demo3 main.c
	[root@ol64 test4]# ldd demo3
		linux-vdso.so.1 =>  (0x00007fff7a6e5000)
		libcalc.so => lib/libcalc.so (0x00007f5f0f6db000)
		libc.so.6 => /lib64/libc.so.6 (0x0000003e66e00000)
		/lib64/ld-linux-x86-64.so.2 (0x0000003e66600000)
	[root@ol64 test4]# ./demo3
	add() = 8 
	sub() = 2
	MAIL:xcl_168@aliyun.com 
	BLOG:http://blog.csdn.net/xcl168
    问题: 为什么加上这个参数就可以,我不是在编译中加上了"-L"参数吗?
    参考:http://bbs.chinaunix.net/thread-1786166-1-1.html
    "-L" 表示在编译时查找。
    "-Wl,-rpath" 表示在程序运行时,优先查找的顺序。

Makefile方式:
请查看: 编译小结(5) Makefile实用小结

2.采用动态调用方式使用动态库.

代码 :
        #include <stdio.h>
	#include <dlfcn.h>
	#include <stdlib.h>

	//#include "lib/calc.h"
	#include "calc.h"
	int main(void)
	{
	        //RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;
	        void *handle = dlopen("/xcl/test4/lib/libcalc.so",RTLD_LAZY);
	        if(!handle)
	        {
	                printf("[main] open failed! %s\n",dlerror());
	                return -1;
	        }

	        typedef int (*sofun_add)(int x,int y);

	        sofun_add cadd = (sofun_add)dlsym(handle,"add");
	        if(!cadd)
	        {
	                printf("[main] cadd() failed! %s\n",dlerror());
	                dlclose(handle);
	                return -2;
	        }

	        printf("[main] cadd() = %d\n",cadd(3,5));
	        dlclose(handle);
	        return 0;
	}


[root@ol64 test4]# gcc -m64 -I./lib -ldl -o calldemo2 callso.c
[root@ol64 test4]# ./calldemo2
[main] cadd() = 8

   如在编译时出现"undefined reference to `dlopen' "错误,说明你编译时没有加上"-ldl",即没指定dlopen所引用的库libdl.a库.
小技巧:
出现这类错误时,可能通过 man 函数名 可查到要链接的库。
如 man dlopen 时出现下面一行,告知你编译时要加上 "-ldl"
Link with -ldl.
再附一些常用的:
     lc 是link libc
    lm 是link libm
     lz 是link libz


 动态调用相关的函数说明:
  在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。
   dlopen函数常用模式:
  RTLD_LAZY 暂缓决定,表示暂时不去处理未定义函数,
     先把库装载到内存,等用到没定义的函数再说.
  RTLD_NOW 立即决定,表示马上检查是否存在未定义的函数,
     若存在,则dlopen以失败告终.
 dlsym()的第一个参数为dlopen()返回的句柄,即符号在库中的地址。
 使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。
 dlclose()用于关闭指定句柄的动态链接库,不过只有当此动态链接库的使用计数为0时,
 才会真正被系统卸载。
  上面这些函数都归属于头文件:#include <dlfcn.h>.
 
附录 :
动态链接,执行时搜索路径顺序:
1. 编译目标代码时指定的动态库搜索路径
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
4. 默认的动态库搜索路径/lib
5. 默认的动态库搜索路径/usr/lib

相关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径

演示代码:
[root@ol64 lib]# cat calc.h
#ifndef __CALC_H_
#define __CALC_H_

int add(int x,int y);
int sub(int x,int y);

[root@ol64 lib]# cat add.c
#include "calc.h"

int add(int x,int y)
{
	return (x + y);
}

[root@ol64 lib]# cat sub.c
#include "calc.h"

int sub(int x,int y)
{
	return (x - y);
}


[root@ol64 test4]# cat main.c
#include <stdio.h>

#include "calc.h"

int main(void)
{
printf("add() = %d \nsub() = %d\n",add(5,3),sub(5,3));
printf("MAIL:xcl_168@aliyun.com \nBLOG:http://blog.csdn.net/xcl168\n");
return 0;
}


MAIL: xcl_168@aliyun.com
BLOG:http://blog.csdn.net/xcl168
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
准备工作: 1.编译JnativeCpp 2.将编译出来的libJNativeCpp.so,拷贝到/usr/lib/,同时执行chmod 555 libJNativeCpp.so 测试过程简介 1.c测试libtest.so 环境:ubuntu10.4下 语言:c 编译名称为:libtest.so 涉及文件:so_test.h test_a.c test_b.c test_c.c 终端执行命令:$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so 将编译得到到libtest.so拷贝到/usr/lib/,同时执行chmod 555 libtest.so 2.qt测试libmylib.so 环境:ubuntu10.4下 语言:c 编译工具:qt Creator 编译名称为:libmylib.so 将编译得到到libmylib.so拷贝到/usr/lib/,同时执行chmod 555 libmylib.so 3.编译环境安装 a.安装jdk 1.6 b.安装netBeans 6.8 c.创建javaApp工程 d.将JNative.jar添加到工程中 e.参考如下代码,编写后编译执行,并运行 import org.xvolks.jnative.JNative; import org.xvolks.jnative.Type; import org.xvolks.jnative.exceptions.NativeException; public class Main { public static void main(String[] args) throws NativeException, IllegalAccessException{ //纯c写到动态库 JNative clib = new JNative("libtest.so", "test_a"); //调用libtest.so下到test_a函数 clib.setRetVal(Type.STRING); //设置此函数的返回值 clib.invoke(); //函数执行 System.out.println(clib.getRetVal());//输出函数返回结果 //qt写到动态库 //以下部分使用qt编译到so,注意在函数声明前加 extern "C" //如extern "C" const char* getLocalHost(); JNative getstring = new JNative("libmylib.so", "getstring"); getstring.setRetVal(Type.STRING); getstring.invoke(); System.out.println(getstring.getRetVal()); } } 4.输出结果 this is in test_a... getstring hello .....

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值