Linux动态链接库编程入门
动态链接库是一种通用的软件组件技术,是多种操作系统中提供基本服务的方式。比如Win32内核就是3个DLL文件构成。这种技术在Linux操作系统下也有对应的实现,就是Linux标准对象Standard Ojbect,对应的文件扩展名为.so。
下面通过一个简单的例子开始介绍Linux标准对象。
我们的标准对象文件含有一个函数,不需要声明export导出符号,只需要编译器设置即可。如下:
#include <stdio.h>
#include <stdlib.h>
void show() {
printf("Standard Object by gashero/n");
}
保存为myso.c文件,按照如下编译:
$ gcc -fPIC -shared -o libmyso.so myso.c
执行生成一个libmyso.so文件,按照Linux标准对象的命名惯例,应该在库名称之前加上"lib"前缀,尽管不是必须的。编译开关-fPIC代表函数符号可以重定向,-shared代表编译结果是一个标准对象。
不同于Win32DLL,Linux标准对象中的所有函数都是直接导出的,都可以被调用程序所访问。下面我们编写调用程序:
#include <stdio.h>
int main() {
printf("Invoke my so/n");
show();
return 0;
}
保存为invoke.c,按照如下gcc开关编译:
$ gcc -o test invoke.c ./libmyso.so
编译生成test可执行文件。如上编译条件的最后一条需要是所调用的标准对象文件名,注意必须含有路径。如果只是使用libmyso.so,则必须确保这个文件在可访问的PATH下面。本例所使用的文件名"./libmyso.so"是当前路径下的,使用了相对路径。
如下测试结果:
$ ./test
Invoke my so
Standard Object by gashero
希望上文的例子可以对大家有所帮助。
如何在 Linux 下调试动态链接库
大家都知道在 Linux 可以用 gdb 来调试应用程序,当然前提是用 gcc 编译程序时要加上
-g 参数。
我这篇文章里将讨论一下用 gdb 来调试动态链接库的问题。
首先,假设我们准备这样的一个动态链接库:
QUOTE:
库名称是: ggg
动态链接库文件名是: libggg.so
头文件是: get.h
提供这样两个函数调用接口:
int get ();
int set (int a);
要生成这样一个动态链接库,我们首先编写这样一个头文件:
[Copy to clipboard]
CODE:
/************关于本文档********************************************
*filename: get.h
*purpose: 一个动态链接库头文件示例
*tided by: zhoulifa(zhoulifa@163.com) 周立发 (http://zhoulifa.9999mb.com)
Linux 爱好者 Linux 知识传播者 SOHO 族 开发者 最擅长 C 语言
*date time: 2006-11-15 21:11:54
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循 GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*感谢 vcclass@hotmail.com 提供原始代码,
我在他的基础上整理了此文
*********************************************************************/
int get ();
int set (int a);
然后准备这样一个生成动态链接库的源文件:
[Copy to clipboard]
CODE:
/************关于本文档********************************************
*filename: get.cpp
*purpose: 一个动态链接库源文件示例
*tided by: zhoulifa(zhoulifa@163.com) 周立发 (http://zhoulifa.9999mb.com)
Linux 爱好者 Linux 知识传播者 SOHO 族 开发者 最擅长 C 语言
*date time:2006-11-15 21:11:54
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循 GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*感谢 vcclass@hotmail.com 提供原始代码,
我在他的基础上整理了此文
*********************************************************************/
#include <stdio.h>
#include "get.h"
static int x=0;
int get ()
{
printf ("get x=%d/n", x);
return x;
}
int set (int a)
{
printf ("set a=%d/n", a);
x = a;
return x;
}
然后我们用 GNU 的 C/C++ 编译器来生成动态链接库,编译命令如下:
QUOTE:
g++ get.cpp -shared -g -DDEBUG -o libggg.so
这样我们就准备好了动态链接库了,下面我们编写一个应用程序来调用此动态链接库,源代码如下:
[Copy to clipboard]
CODE:
/************关于本文档********************************************
*filename: pk.cpp
*purpose: 一个调用动态链接库的示例
*tided by: zhoulifa(zhoulifa@163.com) 周立发 (http://zhoulifa.9999mb.com)
Linux 爱好者 Linux 知识传播者 SOHO 族 开发者 最擅长 C 语言
*date time:2006-11-15 21:11:54
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循 GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*感谢 vcclass@hotmail.com 提供原始代码,
我在他的基础上整理了此文
*********************************************************************/
#include <stdio.h>
#include "get.h"
int main (int argc, char** argv)
{
int a = 100;
int b = get ();
int c = set (a);
int d = get ();
printf ("a=%d,b=%d,c=%d,d=%d/n",a,b,c,d);
return 0;
}
编译此程序用下列命令,如果已经把上面生成的 libggg.so 放到了库文件搜索路径指定的文件目录,比如 /lib 或 /usr/lib 之类的,就用下面这条命令:
QUOTE:
g++ pk.cpp -o app -Wall -g -lggg
否则就用下面这条命令:
QUOTE:
g++ pk.cpp -o app -Wall -g -lggg -L`pwd`
下面我们就开始调试上面命令生成的 app 程序吧。如果已经把上面生成的 libggg.so 放到了库文件搜索路径指定的文件目录,比如 /lib或 /usr/lib 之类的,调试就顺利完成,如下
:
QUOTE:
linux#gdb">zhoulifa@linux#gdb ./app
GNU gdb 6.4-debian
Copyright 2005 Free Software Foundation,Inc.
GDB is free software, covered by the GNUGeneral Public License, and you are
welcome to change it and/or distributecopies of it under certain conditions.
Type "show copying" to see theconditions.There is absolutely no warranty for GDB.
Type "show warranty" for details.This GDB was configured as "i486-linux-
gnu"...Using host libthread_db library"/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b main /* 这是在程序的 main 处设置断点 */
Breakpoint 1 at 0x804853c: file pk.cpp,line 7.
(gdb) b set /* 这是在程序的 set 处设置断点 */
Function "set" not defined.
Make breakpoint pending on future sharedlibrary load? (y or [n]) y /* 这里必须选择 y 调试程序才会跟踪到动态链接库内部去
*/Breakpoint 2 (set) pending.
(gdb) run /* 开始运行我们的程序,直到遇见断点时暂停 */
Starting program: /data/example/c/app
Breakpoint 3 at 0xb7f665f8: file get.cpp,line 11.
Pending breakpoint "set" resolvedBreakpoint 1, main (argc=1,argv=0xbf990504) at pk.cpp:7
7 int a = 100;
(gdb) n /* 继续执行程序的下一行代码*/
8 int b = get ();
(gdb) n /* 程序执行到了我们断点所在的动态链接库了 */
get x=0
9 int c = set (a);(gdb) nBreakpoint 3, set (a=100) at get.cpp:11
11 printf ("set a=%d/n", a);(gdb) list /* 查看当前代码行周围的代码,证明我们已经跟踪到动态链接库的源代码里面了 */
6 printf ("get x=%d/n", x);
7 return x;
8 }
9 int set (int a)
10 {
11 printf ("set a=%d/n", a);
12 x = a;
13 return x;
14 }
(gdb) n
set a=100
12 x = a;(gdb) n
13 return x;(gdb) n
14 }
(gdb) n
main (argc=1, argv=0xbf990504) atpk.cpp:10
10 int d = get ();
(gdb) n
get x=100
11 printf ("a=%d,b=%d,c=%d,d=%d/n",a,b,c,d);
(gdb) n
a=100,b=0,c=100,d=100
12 return 0;
(gdb) c
Continuing.Program exited normally.
(gdb) quit /* 程序顺利执行结束 */zhoulifa@linux#如果我们没有把动态链接库放到指定目录,比如/lib里面,调试就会失败,过程如下:
QUOTE:
zhoulifa@linux# gdb ./app
GNU gdb 6.4-debian
Copyright 2005 Free Software Foundation,Inc.
GDB is free software, covered by the GNUGeneral Public License, and you arewelcome to change it and/or distribute
copies of it under certain conditions.
Type "show copying" to see theconditions.
There is absolutely no warranty for GDB.Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...Using host libthread_db library
"/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b main
Breakpoint 1 at 0x804853c: file pk.cpp,line 7.
(gdb) b set
Function "set" not defined.
Make breakpoint pending on future sharedlibrary load? (y or [n]) y
Breakpoint 2 (set) pending.
(gdb) run /* 虽然调试操作都一样,但程序执行失败 */
Starting program: /data/example/c/app
/data/example/c/app: error while loadingshared libraries: libggg.so: cannot open
shared object file: No such file or
directory
Program exited with code 0177.
(gdb) quit
zhoulifa@linux# 本次实验的环境是:
CPU:AMD Athlon(tm) 64 Processor 3000+
内存:512M
OS:
Ubuntu GNU/Linux 6.06 dapper LTS
gcc:gcc 版本 4.0.3 (
Ubuntu 4.0.3-1ubuntu5)
Linux下使用动态链接库
使用动态链接库,我认为,再比较大的程序运行过程中,是一种很有优势的。所以就花了一天时间来学习一下。
使用动态链接库,需要了解一下内容
头文件:
<dlfcn.h>
函数:
void *dlopen(const char *filename, int flag);
const char *dlerror(void);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle);
相关的信息可以通过
man dlopen查询
在编译动生成态链接库的时候,
需要参数 -shared
在使用动态链接库的时候,
需要参数 -ldl
其他相关参数有
-fpic -fPIC -rdynamic
如有库函数文件Lib.c, 主函数文件Main.c
则有如下Makefile
all: comple link
comple:
gcc -c Lib.c -o Lib.o
gcc -c Main.c -o Main.o
link:
-shared Lib.o -o Lib.so
-ldl Main.o -o Main
另外,在C++中使用动态连接库的时候,请注意:
必须用
extern "C"
{
}
将动态苦定义为C的编译连接方式
否则由于C++命名方式于C不同,会造成生成的动态链接库不能使用(无法定位或函数)
文章选取的例子非常简单,上手容易,只是为了讲述静态与动态链接库的生成和链接过
程,还有他们之间的区别。以下例子在 gcc 4.1.1 下顺利通过。
文件预览 (补充)
文件目录树如下,如你所见,非常简单。
- libtest/
- |-- lt.c
- |-- lt.h
- `-- test.c
代码
#lt.c
• /* lt.c
• *
• */
•
• #include <stdio.h>
•
• void myprint(void)
• {
• "Linux library test!/n");
• }
# lt.h
#test.c
• /* lt.h
• *
• */
•
• void myprint(void);
• /* test.c
• *
• */
•
• #include "lt.h"
•
• int main(void)
• {
• myprint();
• return
• }
先看静态库
首先做成静态库 liblt.a 。
- $ gcc -c lt.c -o lt.o
- $ ar cqs liblt.a lt.o
再者,链接,这里指定了静态库的位置,注意文件顺序不可乱序。
- $ gcc test.o liblt.a -o test
这个时候再来看他的引用库情况。
• $ ldd test
• linux-gate.so.1 => (0xffffe000)
• libc.so.6 => /lib/libc.so.6 (0xb7e29000)
• /lib/ld-linux.so.2 (0xb7f6e000)
动态库
做成动态库 liblt.so 。
- $ gcc -c lt.c -o lt.o
- $ gcc -shared -Wall -fPIC lt.o -o liblt.so
链接方法I,拷贝到系统库里再链接,让gcc自己查找
- $ sudo cp liblt.so /usr/lib
- $ gcc -o test test.o -llt
这里我们可以看到了 -llt 选项,-l[lib_name] 指定库名,他会主动搜索
lib[lib_name].so 。这个搜索的路径可以通过 gcc --print-search-dirs来查找。
链接方法II,手动指定库路径
- $ cc -o test test.o -llt -B /path/to/lib
这里的-B 选项就添加 /path/to/lib 到gcc搜索的路径之中。这样链接没有问题但是方法II
中手动链接好的程序在执行时候仍旧需要指定库路径(链接和执行是分开的)。需要添加系
统变量 LD_LIBRARY_PATH :
• $ export LD_LIBRARY_PATH=/path/to/lib
这个时候再来检测一下test程序的库链接状况(方法I情况)
• $ ldd test
• linux-gate.so.1 => (0xffffe000)
• liblt.so => /usr/lib/liblt.so (0xb7f58000)
• libc.so.6 => /lib/libc.so.6 (0xb7e28000)
• /lib/ld-linux.so.2 (0xb7f6f000)
恩,是不是比静态链接的程序多了一个 liblt.so ?恩,这就是静态与动态的最大区别,静
态情况下,他把库直接加载到程序里,而在动态链接的时候,他只是保留接口,将动态库与
程序代码独立。这样就可以提高代码的可复用度,和降低程序的耦合度。