Linux手机DIY.库文件专题.知识准备
草木瓜转抄 于 2006-11-9
一、序
软件移植过程中,Linux操作系统的库文件着实令人头疼,这方面资料
也比较少。通过一段时间搜索查询推敲,写点总结吧,也算是有点成果。不过
这篇内容大多都是抄的,这里对原创作者表示深深的敬意~
二、重要提示
为了方便更好的理解本文,提供下面链结。
全系列的文章地址,手机应用开发专栏:http://blog.csdn.net/liwei_cmg
相关的重要成果的下载地址:http://play.younet.com/view.php?tid=24045
三、Linux的库文件格式
[以下内容整理自《创建和使用库》一文,作者不详]
C语言中有一些函数不需要进行编译,也可以在多个文件中使用。
一般来说,有此函数会执行一定的标准任务,如数据库输入/输出操作或
屏幕控制等。可以事先对这些函数进行编译,然后将它们放置在一些特殊的目
标代码文件中,这些目标代码文件就称为库。
库文件中的函数可以通过连接程序与应用程序进行连接。这样就不必在每
次开发程序时都对这些通用的函数进行编译了。
不同类型的应用程序会使用不同的函数库。例如:libdbm库中组包含了对
数据库文件进行访问的dbm函数,需要对数据库进行操作的程序就会与该库进
行连接。数学应用程序使用数学库libm,X-Windows应用程序使用Xlib库,
libX11。另外,所有的程序都将使用标准的C函数库。libc,该库中包含了内
存管理或输入输出操作的基本函数,这些库都存放在/usr/lib这些系统公用
的目录中,系统中的任何用户都可以利用这些库。当然用户也可以建立自己
专用的库函数,供自己或其它指定的人员使用。
库可以有三种使用的形式:静态、共享和动态。静态库的代码在编译时就
已连接到开发人员开发的应用程序中,而共享库只是在程序开始运行时才载入,
在编译时,只是简单地指定需要使用的库函数。动态库则是共享库的另一种变
化形式。动态库也是在程序运行时载入,但与共享库不同的是,使用的库函数
不是在程序运行开始,而是在程序中的语句需要使用该函数时才载入。动态库
可以在程序运行期间释放动态库所占用的内存,腾出空间供其它程序使用。由
于共享库和动态库并没有在程序中包括库函数的内容,只是包含了对库函数的
引用,因此代码的规模比较小。
已经开发的大多数库都采取共享库的方式。ELF格式的可执行文件使得共享
库能够比较容易地实现,当然使用旧的a.out模式也可以实现库的共享。Linux
系统中目前可执行文件的标准格式为ELF格式。
GNU库的使用必须遵守Library GNU Public License(LGPL许可协议)。该
协议与GNU许可协议略有不同,开发人员可以免费使用GNU库进行软件开发,但
必须保证向用户提供所用的库函数的源代码。
系统中可用的库都存放在/usr/lib和/lib目录中。库文件名由前缀lib和
库名以及后缀组成。根据库的类型不同,后缀名也不一样。共享库的后缀名
由.so和版本号组成,静态库的后缀名为.a。采用旧的a.out格式的共享库的
后缀名为.sa。
使用gcc编译器就可以将库与自己开发的程序连接起来,例如:libc.so.5
中包含了标准的输入输出函数,当连接程序进行目标代码连接时会自动搜索该
程序并将其连接到生成的可执行文件中。标准的输入输出库中包含了许多基本
的输入输出函数,如printf函数等。也可以连接其它的一些系统函数库,如数
学库等,但与libc.so.5不同,大部分其它的系统库需要在命令行中显式指定
所用的库名。
在/usr/lib和/lib目录中可以找到绝大多数的共享库。连接时将首先搜索
这两个目录。有一些库也可能存放在特定的目录中,在/etc/ld.conf配置文件
中给出了这些目录的列表。连接程序也会对列出的这些目录进行搜索。在默认
情况下,Linux将首先搜索指定库的共享版本,如果找不到,才会去搜索静态
版本。在对共享库进行更新或安装新库后,必须运行ldconfig命令更新
/etc/ld.conf文件中相应的项(如果使用RPM进行安装,一般会自动进行更新,
不过也不能保证这一点)。
在gcc编译器中引用可搜索到的目录中的库文件时,需要使用-l选项和库
名。在gcc命令行上输入-lm可以在程序中连接标准算术库,-l将首先使用
libname.so进行搜索,这里是libm.so。下面的例子将使用算术库创建bookrecs
程序,请注意这里的-lm选项。
$ gcc main.c io.c -o bookrecs -lm
系统中还有一些其它可用的库,常用的是libncurses.a库,包含了一些简
单的鼠标移动例程。在命令行中使用-lncurses选项引用libncurses.so库。下
面的例子同时调用了数学和光标库。
$ gcc mian.c io.c -o bookrecs -lm -lncurses
在引用其它目录中的库时,需要使用-ldir选项指定该目录。该选项指定
了搜索库函数时其它路径。在下面的例子中,用户在连接时使用了mydir目录
中的myio.so库文件。
$ gcc main.c -o bookrecs -lmydir -lmyio
Linux下文件的类型是不依赖于后缀名的,但一般来讲:
.o 为目标文件,类似于windows中的.obj文件
.so 为共享库文件,用于动态连接,类似于dll文件
.a 为静态库,是多个.o文件合在一起,用于静态连接
.la 为libtool自动生成的一些共享库,可用vi编辑查看,主要记录了一
些配置信息。
四、创建和使用Linux库文件
[以下内容整理自《Linux静态/动态链接库的创建和使用》一文,
http://blog.csdn.net/hcj2002/]
和Windows系统一样Linux也有静态/动态链接库,下面介绍创建和使用
方法:
假设有下面几个文件:
头文件String.h,声明相关函数原形,内容如下:
Strlen.c:函数Strlen的实现,获取给定字符串的长度,内容如下:
Strlnen.c:函数StrNlen的实现,获取给定字符串的长度,如果输入字
符串的长度大于指定的最大长度,则返回最大长度,否者返回字符串的实际
长度,内容如下:
生成静态库:
利用GCC生成对应目标文件:
gcc –c Strlen.c Strnlen.c
如果对应的文件没有错误,gcc会对文件进行编译生成Strlen.o和Strnlen.o
两个目标文件(相当于windows下的obj文件)。然后用ar创建一个名字为libstr.a
的库文件,并把Strlen.o 和Strnlen.o的内容插入到对应的库文件中。相关命
令如下:
ar –rc libstr.a Strlen.o Strnlen.o
命令执行成功以后,对应的静态库libstr.a已经成功生成。
/***********************************
Filename : String.h
Description :
Author : HCJ
Date : 2006-5-7
************************************/
int Strlen(char *pStr);
int StrNlen(char *pStr, unsigned long ulMaxLen);
/**************************************
Filename : get string length
Description :
Author : HCJ
Date : 2006/5/7
**************************************/
#include<stdio.h>
#include<assert.h>
int Strlen(char *pStr)
{
unsigned long ulLength;
assert(NULL != pStr);
ulLength = 0;
while(*pStr++)
{
ulLength++;
}
return ulLength;
}
**********************************************
Fileneme: mystrnlen.c
Description: get input string length,if string large
max length input return max length,
else real length
Author: HCJ
Date : 2006-5-7
**********************************************/
#include<stdio.h>
#include<assert.h>
int StrNlen(char *pStr, unsigned long ulMaxLen)
{
unsigned long ulLength;
assert(NULL != pStr);
if(ulMaxLen <= 0)
{
printf("Wrong Max Length!\n");
return -1;
}
ulLength = 0;
while(*pStr++ && ulLength < ulMaxLen)
{
ulLength++;
}
return ulLength;
}
生成动态链接库:
gcc -fpic -shared -o libstr.so Strlen.c Strnlen.c
-fpic 使输出的对象模块是按照可重定位地址方式生成的。
-shared指定把对应的源文件生成对应的动态链接库文件libstr.so
文件。
对应的链接库已经生成,下面看一下如何使用对应的链接库。
静态库的使用:
假设有下面的文件要使用对应的的静态库:
编译生成对应的目标文件:
gcc -c -I/home/hcj/xxxxxxxx main.c
生成可执行文件:
gcc -o main1 -L/home/hcj/xxxxxxxx main.o libstr.a
其中-I/home/hcj/xxxxxxxx和-L/home/hcj/xxxxxxxx是通过-I和-L指
定对应的头文件和库文件的路径。libstr.a是对应的静态库的名称。这样
对应的静态库已经编译到对应的可执行程序中。执行对应的可执行文件便
可以对应得函数调用的结果。
/*****************************************
FileName: main.c
Description: test static/dynamic library
Author: HCJ
Date : 2005-5-7
******************************************/
#include<stdio.h>
#include <String.h> //静态库对应函数的头文件
int main(int argc, char* argv[])
{
char str[] = {"hello world"};
unsigned long ulLength = 0;
printf("The string is : %s\n", str);
ulLength = Strlen(str);
printf("The string length is : %d(use Strlen)\n", ulLength);
ulLength = StrNlen(str, 10);
printf("The string length is : %d(use StrNlen)\n", ulLength);
return 0;
}
动态库的分为隐式调用和显式调用两种调用方法:
隐式调用的使用使用方法和静态库的调用差不多,具体方法如下:
gcc -c -I/home/hcj/xxxxxxxx main.c
gcc -o main1 -L/home/hcj/xxxxxxxx main.o libstr.so //这里是*.so
在这种调用方式中,需要维护动态链接库的配置文件/etc/ld.so.conf
来让动态链接库为系统所使用,通常将动态链接库所在目录名追加到动态链
接库配置文件中。否则在执行相关的可执行文件的时候就会出现载入动态链
接库失败的现象。在编译所引用的动态库时,可以在gcc采用 –l或-L选项或
直接引用所需的动态链接库方式进行编译。在Linux里面,可以采用ldd命令
来检查程序依赖共享库。
显式调用:
/*****************************************
FileName: main2.c
Description: test static/dynamic library
Author: HCJ
Date : 2005-5-7
******************************************/
#include<stdio.h>
#include<dlfcn.h>
int main(int argc, char* argv[])
{
//define function pointor
int (*pStrlenFun)(char* pStr); //声明对应的函数的函数指针
int (*pStrnlenFun)(char* pStr, int ulMaxLen);
char str[] = {"hello world"};
unsigned long ulLength = 0;
void *pdlHandle;
char *pszErr;
pdlHandle = dlopen("./libstr.so", RTLD_LAZY); //加载链接库/libstr.so
if(!pdlHandle)
{
printf("Failed load library\n");
}
pszErr = dlerror();
if(pszErr != NULL)
{
printf("%s\n", pszErr);
return 0;
}
//get function from lib
pStrlenFun = dlsym(pdlHandle, "Strlen"); //获取函数的地址
pszErr = dlerror();
if(pszErr != NULL)
{
printf("%s\n", pszErr);
return 0;
}
pStrnlenFun = dlsym(pdlHandle, "StrNlen");
pszErr = dlerror();
if(pszErr != NULL)
{
printf("%s\n", pszErr);
return 0;
}
printf("The string is : %s\n", str);
ulLength = pStrlenFun(str); //调用相关的函数
printf("The string length is : %d(use Strlen)\n", ulLength);
ulLength = pStrnlenFun(str, 10);
printf("The string length is : %d(use StrNlen)\n", ulLength);
dlclose(pdlHandle);
return 0;
}
gcc -o mian2 -ldl main2.c
用gcc编译对应的源文件生成可执行文件,-ldl选项,表示生成的对象模
块需要使用共享库。执行对应得文件同样可以得到正确的结果。
相关函数的说明如下:
(1)dlopen()
第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库。
-环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录。
-文件/etc/ld.so.cache中找到的库的列表,用ldconfig维护。
-目录usr/lib。
-目录/lib。
-当前目录。
第二个参数:指定如何打开共享库。
-RTLD_NOW:将共享库中的所有函数加载到内存
-RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时
方加载某函数
(2)dlsym()
调用dlsym时,利用dlopen()返回的共享库的phandle以及函数名称作为
参数,返回要加载函数的入口地址。
(3)dlerror()
该函数用于检查调用共享库的相关函数出现的错误。
这样我们就用简单的例子说明了在Linux下静态/动态库的创建和使用。