作者:曹江华 赛迪技术天地
随着Linux性能的不断提升和逐渐普及,会有越来越多的人在Linux下从事应用软件的开发。这里笔者根据从事Linux应用程序开发的经验,介绍Linux编程库使用的一些基础知识。
库的定义和种类
所谓编程库就是指始终可以被多个Linux软件项目重复使用的代码集。库是Linux软件开发所追求的目标 ,C语言就是一个例子,它包含了几百个可以重复使用的例程和调试程序的工具代码,其中包括函数。如果每次编写新程序都要重新写这些函数会感到非常麻烦。
使用编程库有两个主要优点,一是可以简化编程,实现代码重复使用;二是可以直接使用许多经过调试的测试和调试工具。
Linux下的库文件分为共享库和静态库两大类,它们两者的差别仅在程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。
Linux的库一般在/lib 或/usr/lib 目 录下。lib是库(Library)的英文缩写,它主要存放系统的链接库文件,没有该目录则系统就无法正常运行。/lib目录中存储着程序运行时使用的共 享库。通过共享库,许多程序可以重复使用相同的代码,并且这些库可以存储在一个公共的位置上,因此能减小运行程序的大小。这个目录包含程序在链接时使用的 各种库。
库的命名和编号
在Linux下开发应用程序时,绝大多数情况使用的都是C语言,目前Linux下最常用的 C语言编译器是GCC,它的全称是GNU Compiler Collection,下文中的库介绍都以它为例。
GCC是直接建立在内核基础上的,是 Linux操作系统外层的图形界面开发工具(Qt、 GTK+)和网络 应用开发工具(PHP、Prel、Python)的基础和过渡。掌握了底层开发工具,可以加快和优化外层应用开发,从而达到开发速度和开发质量的和谐统一。
Glibc 2.3.2 是最新版的GNU C库。它目前不需要修改就可以在GNU Hurd、Linux i386、m68k,以及Alpha系统上执行,并且从2.1版开始加入了对Linux PowerPC、MIPS、Sparc、Sparc 64等系统的支持。
如果想查看自己Linux计算机的Glibc版本可以使用下面命令:
rpm -qa|grep glibc
glibc-common-2.3.2-11.9
glibc-2.3.2-11.9
glibc-devel-2.3.2-11.9
由上可见,Red Hat Linux 9.0使用的Glibc版本是2.3.2。
1.库的命名
库的命名比较简单,第一个特点是所有的库以lib开头,GCC命令在在 -l 选项所指定的文件名前会自动加入lib。第二个特点文件名 以.a结尾的库是静态库 。第三个特点文件名是 .so的库为共享库(共享库是在运行的时候动态加载的 )。默认情况下, GCC在链接时优先使用共享库,只有当共享库不存在时才考虑使用静态库。
2.库的编号
库的编号格式如下:
library_name . major.num . minor_.min . pathch_num
例如,笔者Red Hat Linux 9.0的GUN数据库是libgdbm.so.0.0.2,详细表述如下:
◆library_name是libc.so(标准C库);
◆major_num是2(主版本号);
◆minor_.min是0(次版本号);
◆pathch_num是0(补丁级别号又称发行号)。
libgdbm.so.0.0.2所在目录是/usr/lib,其大小是24576字节,这是一个 ELF (Executable and Linking Format)格式的二进制格式文件,运行时由系统将其装入内存开始执行。
ELF有三种主要类型:
◆适于连接的可重定位文件(Relocatable File),可与其它目标文件一起创建可执行文件和共享目标文件。
◆适于执行的可执行文件(Executable File),用于提供程序的进程映像,加载的内存执行。
◆共享目标文件(Shared Object File),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件;动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像。
常用的编程库
库操作命令
Linux库操作可以使用命令完成,目前常用的命令是ldd和ldconfig。
1. ldd 是Library Dependency Display缩写,它的作用是 显示一个可执行程序必须使用的共享库 。
(1)命令格式
ldd [选项] 文件名
(2)主要参数
-d 执行重定位并报告丢失的函数。
-r 执行对函数和数据对象的重定位,并报告丢失的函数和数据对象。
(3)应用实例
Perl语言是开发者喜欢使用的一种“胶水语言”(能够将许多元素连接在一起,因此它具有极强的适应性),如果需要查询有哪些共享库,则可以首先使用find命令查询这个程序的绝对路径,然后使用ldd命令:
#find -name perl
ldd /usr/bin/perl
其 结果见图2所示。图2中,箭头左边的一列显示的是Prel语言所需的.so文件名, 箭头右边的一列显示是库的真实库名称。应用程序链接到库的so名字是到实际库的符号链接。以第二行为例,最后的0x40014000是库 libperl.so的加载地址。由此可以看到,运行Perl语言需要9个共享库。
2. ldconfig
ldconfig 命令的作用是决定位于目录/usr/lib和/lib下的共享库所需的运行链接。这些链接保存在的Libs保存在/et/ld.so.conf文件中。搜 索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的链接和缓存文件。缓存文件默认为/etc /ld.so.cache,此文件保存已排好序的动态链接库名字列表。
(1)命令格式
ldconfig [选项] [libs]
(2)主要选项
-v或--verbose ldconfig将显示正在扫描的目录、搜索到的动态链接库,以及它所创建的连接的名字。
-f CONF 指定动态链接库的配置文件为CONF,系统默认为/etc/ld.so.conf。
-C CACHE 指定生成的缓存文件为CACHE,系统默认的是/etc/ld.so.cache,文件存放已排好序的可共享的动态链接库的列表。
-p或--print-cache 让ldconfig打印出当前缓存文件所保存的所有共享库的名字。
-r ROOT 改变应用程序的根目录为ROOT。
-n ldconfig仅扫描命令行指定的目录,不扫描默认目录(/lib、/usr/lib),也不扫描配置文件/etc/ld.so.conf所列的目录。
运行没有选项的ldconfig命令时,用于更新高速缓冲文件。这个命令主要用于高速缓冲DNS服务器(Caching DNS Server)。高速缓冲DNS服务器的原理是提供查询的历史记录,并且利用这些记录来提高查询的效率。
当某个查询是第一次被发送到高速缓冲DNS服务器时,高速缓冲DNS服务器就将此查询的整个过程记录下来,在一定的时期内用它来回答所有相同的查询,从而减少整个DNS系统的负担并且提高查询速度。
(3)应用实例
如果用户想知道系统中有哪些动态链接库,或者想知道系统中有没有某个动态链接库时,可用-p选项让ldconfig输出缓存文件中的动态链接库列表,从而查询得到。
ldconfig -p
998 libs found in cache `/etc/ld.so.cache'
libzvt.so.2 (libc6) => /usr/lib/libzvt.so.2
libzvt.so (libc6) => /usr/lib/libzvt.so
……
ldconfig命令输出结果表明,在缓存文件/etc/ld.so.cache中找到998个共享库,下一行开始便是一系列共享库的名字及其全名(绝对路径)。因为实际输出结果很多,为节省篇幅以“……”表示省略的部分。
库的升级
Linux 系统软件更新很快,新的核心几乎每几个星期就公布一次,其它软件的更新也是非常频繁。多数情况下,盲目跟随潮流的升级并不必要,如果确实需要新版本的特性 时再升级。换句话说,不要为了升级而升级。Linux系统中多数软件都是用共享库来编译的,其中包含了在不同程序之间共享的公用子例程。
在运行某个程序时,如果看到如下信息:“Incompatible library version.”则表明需要将该库升级到程序所需要的版本。库是向下兼容的,也就是说,用老版本库编译的程序可以在新安装的版本库上运行,反之则不行。
Linux库函数的升级是一项重要的工作,往往与其它软件包的升级有一定关联作用,所以操作前一定要备份文件。下面看一下如何把Glibc 2.2.4.13升级至2.3.2版本,其过程如下:
1.下载.gz压缩文件并解压
在GUN C网站下载的四个.gz压缩文件,解压至一临时目录中:
cd /usr/caolinux
tar xzvf glibc-2.3.2.tar.gz
cd glibc-2.3.2
tar xzvf ../glibc-linuxthreads-2.3.2.tar.gz
tar xzvf ../glibc-crypt-2.3.2.tar.gz
tar xzvf ../glibc-localedata-2.3.2.tar.gz
2.建立库函数的安装目录
mkdir /usr/higlibc
cd /usr/higlibc
3.建立编译目录
mkdir cao
cd cao
./configure --enable-add-ons=linuxthreads,crypt,localedata -prefix
=/usr/higlibc
4.编译与安装
make
make check
make install
编译与安装过程根据计算机硬件配置不同,从10分钟到几十分钟不等。 ,
如果是非致命的错误(也可以理解为无关紧要)就不会停下来
换句话说,只要make check过程没有自己中断就没问题
./configure是用来检测你的安装平台的目标特征的 。比如它会检测你是不是有CC或GCC,
make是用来编译的,它从Makefile中读取指令,然后编译。
make install是用来安装的,它也从Makefile中读取指令,安装到指定的位置。
AUTOMAKE和AUTOCONF是非常有用的用来发布C程序的东西。
如果你也写程序想使用AUTOMAKE和AUTOCONF,可以参考CNGNU.ORG上的相关文章。
install 不是make的参数,而是再makefile(Makefile)中型如:install:的语句。
ln -s /usr/higlibc/lib/ld-linux.so.2 /lib/ld-linux.so.2
然后,修改/etc/ld.so.conf,加入一行/usr/higlibc/lib,执行下面代码:
ldconfig -v
更新/etc/ld.so.cache的内容,列出每个库的版本号,扫描目录和所要创建及更新的链接。
6.更改GCC设置
cd /usr/lib/gcc-lib
cp -r i386-redhat-linux higlibc
7.更新符号链接
cd /usr/higlibc/include
ln -s /usr/src/linux/include/linux
ln -s /usr/src/linux/include/asm
ln -s /usr/X11R6/include/X11
8.测试
编写一个简单的C程序测试一下,除了头文件只需一条printf语句即可:
#include
int main(void)
{ printf ("Hello , Linux !/n");
return 0;}
然后用GCC编译一次,当程序出现如下运行结果,则说明已经正确地升级了。
“Hello, Linux !”
以上五部分分别介绍了Linux库的定义属性、“标准”库的命名和编号约定、经常使用的库、与库操作相关命令的作用,以及库升级的步骤,希望能对初学Linux的开发者有所帮助。
Linux动态连接
Linux中的应用程序以以下两种方式之一链接到外部函数
-> 在构建时与静态库(lib*.a)静态链接,并且将库代码包含在该应用程序的可执行文件里
-> 在运行时与共享库(lib*.so)动态链接,通过动态链接装入器,将动态库映射进应用程序
的可执行内存中 。在启动应用程序之前,动态链接器将所需的共享库映射到应用的内存,
或者使用系统共享的目标并为应用程序解析所需的外部引用。
Linux动态连接
1.Linux中的应用程序以以下两种方式之一链接到外部函数
-> 在构建时与静态库(lib*.a)静态链接,并且将库代码包含在该应用程序的可执行文件里
-> 在运行时与共享库(lib*.so)动态链接,通过动态链接装入器,将动态库映射进应用程序
的可执行内存中。在启动应用程序之前,动态链接器将所需的共享库映射到应用的内存,
或者使用系统共享的目标并为应用程序解析所需的外部引用。
2.Linux提供4个函数(dlopen,dlerror,dlsym和dlclose)
-> 一个include文件[dlfcn.h]以及两个库[静态库-libdl.a和动态库-libdl.so]来支持
动态链接装入器
-> dlopen:将共享目标文件打开并映射到内存中,并且返回句柄
-> dlsym:返回一个指向被请求入口的指针[函数地址]
-> dlerror:返回NULL或者一个指向描述最近错误的ASCII字符串指针
-> dlclose:关闭句柄并且取消共享目标文件的映射
3.动态链接装入例程dlopen需要在文件系统中查找共享目标文件以打开文件并创建句柄,有4种 方式用以指定文件的位置
-> 直接在dlopen调用中指定绝对文件路径
-> 在LD_LIBRARY_PATH环境变量中指定的目录中
-> 在/etc/ld.cache中指定的库列表中
-> 先在/usr/lib 之中,然后在/lib 之中
另一种:
在Linux下,大部分系统的library库被安装在/usr/lib目录下。只有一些
基本的共享库被安装在/lib目录下。例如:libc.so,libcurses.so,libm.so
,libtermcap.so(各个版本对应的文件会有些不同),在其他部分被mount上
之前,那些文件是启动Linux系统所必须的。连接器默认的搜索路径是
/lib, /usr/lib, /usr/local/lib
环境变量LD_LIBRARY_PATH列出了查找共享库时除了默认路径之外的其他路径。
/etc/ld.so.conf文件则指出了程序ldconfig要搜索的目录。该程序将这些目录中所有的
共享库都存储到/etc/ld.so.cache中。假如共享库已经被从默认的目录中
移走,Linux ELF动态连接库将在/etc/ld.so.cache文件中找该共享库。
程序ldconfig将把/etc/ld.so.conf文件中列出的搜索目录中的所有的
共享库存储到/etc/ld.so.cache中。假如共享库已经被从默认的目录中
移走,Linux ELF动态连接库将在/etc/ld.so.cache文件中找该共享库。
对于普通用户无法修改/etc/ld.so.conf,又想用其他位置的库,就可以用LD_LIBRARY_PATH了
请参见网上的一篇文摘:
今天在给Awstats安装GeoIP插件的时候,提示找不到一个链接库(libGeoIP.so.1),find发现此文件位于/usr/local/lib目录 ,而系统搜索的是/usr/lib目录,本来可以简单的做一个软链接(ln -s)来解决问题,不过我google之后发现了一个更好的方法:
打开/etc/ld.so.conf加入一行:/usr/local/lib
执行/sbin/ldconfig /etc/ld.so.conf
搞定了。
关于ld命令的使用方法
4.API组成
-> 头文件:#include <dlfcn.h>
-> API实现库:libdl.so.2 => /lib/libdl.so.2
-> void * dlopen(const char *filename, int flag);
-> const char *dlerror(void);
-> void * dlsym(void *handle, char* symbol);
-> int dlclose(void *handle);
5.构建使用共享库的应用程序
-> [1]将库的实现源码文件编译成位置无关代码[-fpic]
-> [2]创建共享库目标文件[lib*.so -shared]
-> [3]将主程序同共享库链接[-ldl]p
库的实现源码文件: uppercase.c lowercase.c
主程序:dltest.c
[1]
$gcc -c -fpic uppercase.c
$gcc -c -fpic lowercase.c
[2]
$gcc -shared -lc -o uppercase.so uppercase.o
$gcc -shared -lc -o lowercase.so lowercase.o
[3]
$gcc -o dltest.c -ldl