`
jakielong
  • 浏览: 223317 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Unix/Linux环境下创建和使用静/动态库

阅读更多

库的作用

  大体上库的存在,有两方面的原因,一是代码的复用,二是声明和实现的分离。将功能相近的使用模块封装成库,使代码的复用、管理和分发变得简单了许多,例如著名的开源图形库ncurses,你可以自行编译,更可以直接使用已经编译好的现成的库文件。另外,由于库是二进制文件,某种意义上讲,将功能的实现部分隐藏了起来,这就为商业代码的保护提供了一种方式。
  库文件按照链接方式和时机,可以分为动态库和静态库,下面分别介绍它们在Linux环境中的创建和使用方法。

静态链接库

  静态库是指在程序的链接阶段,其中被用到的代码会被直接链接到可执行文件中的库。静态链接的可执行程序包含了其所需的全部库函数:所有库函数都连接到程序中。 这类程序是完整的,其运行不需要外部库的支持。 静态链接程序的优点之一是其安装之前不需要做环境准备工作 。
  Linux中,静态库的扩展名通常为.a,它仅仅是一些目标文件(.o)的归档(archive)或者说打包,另外,为了链接时能够快速地定位其中的符号(函数、变量名等),静态库还会包含一个对其中符号的索引。
  创建静态库的过程十分简单,除编译所必须的工具之外,要用到的命令只有两个ar和ranlib。ar可以将各个目标文件进行归档,ranlib对ar生成的归档文件(即静态库文件)进行索引。假设现在有这样几个源文件:plus.c, sub.c及相应的头文件,另外还有一个用来调用库文件中函数的主文件main.c,它们的内容分别是:
plus.c,

#include "plus.h"
int
plus(int a, int b)
{
    return a + b;
} 

 sub.c,

#include "sub.h"
int
sub(int a, int b)
{
    return a - b;
} 

 main.c,

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "plus.h"
int
main(int argc, char **argv)
{
    int a = plus(1, 2);
    printf("%d\n", a);
    return 0;
} 

 下面将plus.c和sub.c编译,制作成静态库libmath.a,依次执行:

$ cc main.c -L. -lmath -o main

$ ./main

动态链接库

  动态库和静态库相似,也是各个目标文件的集合。但相比静态链接的程序,动态链接可执行程序要小得多:这类程序运行时需要外部共享 函数库的支持,因此好像并不完整。除了程序体小之外,动态链接允许程序包指定必须的库,而不必将库装入程序包内。动态链接技术还允许多个运行中的程序共享一个库,这样就不会出现同一代码的多份拷贝共占内存的情况了。由于这些原因,当前多数程序采用动态链接技术。
  在Linux中的扩展名通常为.so。但在链接时,并不会被链接到可执行文件中,而是在执行时(需要时)由操作系统的动态加载模块动态地加载到内存,并链接到可执行文件地址空间的相应位置。
  动态链接库的创建也分为编译和”归档”两个阶段,但不同的是在这两个阶段需要使用一些不同的命令选项。首先,需要将源文件编译成一种成为位置无关码(PIC: Position Independent Code)的目标文件,这种代码可以被加入到内存的任何位置却不需要加载器对其进行重定位,关于这种格式可以参考《链接器与加载器》和《程序员的自我修养–链接装载与库》中较为详尽的描述。接下来需要将这些位置无关码“归档”为.so文件。整个过程只需一个工具即可,即gcc。还是上面的源文件,执行以下命令:

$ cc -c -fpic plus.c sub.c
$ cc -shared -o libmath.so *.o

这样,包含plus.o和sub.o的动态库文件libmath.so就创建完成了。其中cc(gcc的符号链接)命令的-fpic或-fPIC选项使之创建位置无关的目标文件,使用-shared选项可以创建最终的动态库文件。
  使用动态库文件有两种方法,一种是让操作系统的动态加载模块(如ld-linux.so.2)在执行时加载动态库。另一种是在代码中使用dl库动态加载库文件。
  先介绍下第一种方法。使用这种方法需要在编译可执行文件时指明库名及其路径(对于自己的编写的动态库而言):


$ cc main.c -L. -lmath -o main

  这时可执行文件main就被创建了,上面的命令并没有将libmath.so中相应的目标代码链接到main中(你可以对比一下这里的main和静态链接的main的大小),只是在库中查找和确认main.c中用到的符号而已。但通常这个main现在还无法正常执行,这涉及到系统查找动态库的路径问题,系统通常只在某些指定的目录(标准路径)下查找所需的库文件,若在标准路径中无法找到所需的库,则会到环境变量LD_LIBRARY_PATH(如果存在的话)指定的目录下查找,若仍无法找到就会报错。因为libmath.so在main的当前目录中,而当前目录通常并不在标准路径之列。为了使libmath.so能够被找到和加载,你可以把它放到标准路径中,但更好的方法是将其所在目录加入到LD_LIBRARY_PATH变量中。执行下面的命令:


$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. # 添加当前目录
$ export LD_LIBRARY_PATH # 将环境变量导出,使其在子shell中可用
$ ./main
3

  
  下面介绍使用dl库加载动态库,dl库中函数很少很简练,看main.c代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include "plus.h"
int
main(int argc, char **argv)
{
    void * lib_handle = dlopen("./libmath.so", RTLD_LAZY);
    if(lib_handle)
    {
        int (*plus_ptr)(int, int) = dlsym(lib_handle, "plus");
        if(plus_ptr)
        {
            int a = plus_ptr(1, 2);
            printf("%d\n", a);
        }
        dlclose(lib_handle);
    }
    return 0;
} 

 main.c中,首先使用dlopen打开需要的动态库,其中参数RTLD_LAZY指明仅当需要调用该库时才进行加载。dlopen返回一个句柄,dlsym使用该句柄和符号来取得相应函数的地址,这里使用int (*)(int, int)函数指针来接收plus函数的地址。接下来使用得到的函数指针调用相应的函数,最后通过dlclose函数来关闭句柄。编译这个程序需要使用dl动态链接库,因此需要使用gcc的-ldl选项:

$ cc main.c -ldl -o main

$ ./main

so与.a文件的对比

  最后附上libmath.a和libmath.so文件的内容,使用objdump的-d选项查看的:
libmath.a,

In archive libmath.a:

plus.o:     file format elf32-i386
Disassembly of section .text:
00000000 <plus>:
   0:	55                   	push   %ebp
   1:	89 e5                	mov    %esp,%ebp
   3:	8b 45 0c             	mov    0xc(%ebp),%eax
   6:	8b 55 08             	mov    0x8(%ebp),%edx
   9:	8d 04 02             	lea    (%edx,%eax,1),%eax
   c:	5d                   	pop    %ebp
   d:	c3                   	ret    

sub.o:     file format elf32-i386
Disassembly of section .text:
00000000 <sub>:
   0:	55                   	push   %ebp
   1:	89 e5                	mov    %esp,%ebp
   3:	8b 45 0c             	mov    0xc(%ebp),%eax
   6:	8b 55 08             	mov    0x8(%ebp),%edx
   9:	89 d1                	mov    %edx,%ecx
   b:	29 c1                	sub    %eax,%ecx
   d:	89 c8                	mov    %ecx,%eax
   f:	5d                   	pop    %ebp
  10:	c3                   	ret

libmath.so:

libmath.so:     file format elf32-i386
Disassembly of section .init:
00000324 <_init>:
 324:	55                   	push   %ebp
 325:	89 e5                	mov    %esp,%ebp
 327:	53                   	push   %ebx
 328:	83 ec 04             	sub    $0x4,%esp
......
0000044c <plus>:
 44c:	55                   	push   %ebp
 44d:	89 e5                	mov    %esp,%ebp
 44f:	8b 45 0c             	mov    0xc(%ebp),%eax
 452:	8b 55 08             	mov    0x8(%ebp),%edx
 455:	8d 04 02             	lea    (%edx,%eax,1),%eax
 458:	5d                   	pop    %ebp
 459:	c3                   	ret
 45a:	90                   	nop
 45b:	90                   	nop

0000045c <sub>:
 45c:	55                   	push   %ebp
 45d:	89 e5                	mov    %esp,%ebp
 45f:	8b 45 0c             	mov    0xc(%ebp),%eax
 462:	8b 55 08             	mov    0x8(%ebp),%edx
 465:	89 d1                	mov    %edx,%ecx
 467:	29 c1                	sub    %eax,%ecx
 469:	89 c8                	mov    %ecx,%eax
 46b:	5d                   	pop    %ebp
 46c:	c3                   	ret
 46d:	90                   	nop
 46e:	90                   	nop
 46f:	90                   	nop
......

Disassembly of section .fini:

000004a8 <_fini>:
 4a8:	55                   	push   %ebp
 4a9:	89 e5                	mov    %esp,%ebp
 4ab:	53                   	push   %ebx
 4ac:	83 ec 04             	sub    $0x4,%esp
 4af:	e8 00 00 00 00       	call   4b4 <_fini+0xc>
 4b4:	5b                   	pop    %ebx
 4b5:	81 c3 40 1b 00 00    	add    $0x1b40,%ebx
 4bb:	e8 d0 fe ff ff       	call   390 <__do_global_dtors_aux>
 4c0:	59                   	pop    %ecx
 4c1:	5b                   	pop    %ebx
 4c2:	c9                   	leave
 4c3:	c3                   	ret

  可见,相对.a,.so有很多额外的代码段,其中比较重要的是<_init>段和<_fini>段。它们分别进行一些前期和后期处理工作,例如<_init>通常在dlopen返回之前执行一些.so库中的一些初始化工作(C++中就可能是全局构造或者静态对象的构造函数)。

转自:http://www.dutor.net/index.php/2010/07/dynamic-static-library/

分享到:
评论

相关推荐

    linux下C++动态链接C++库示例

    文中是linux下 C++动态库 实现接口提供类导出的一个例子 注意其中使用函数返回基类指针的用法,因为Linux的动态链接库不能像MFC中那样直接导出类 一、介绍 如何使用dlopen API动态地加载C++函数和类,是Unix C++...

    UNIX操作系统教程 张红光

    第1章绪论.1 1.1操作系统概述1 1.1.1建立操作系统的目标1 1.1.2操作系统是用户与计算机的接口1 1.1.3操作系统是资源管理器2 1.2UNIX系统的主要特性3 1.3UNIX系统的发展史4 1.4开源软件与UNIX的推广发展6 1.4.1开源...

    linux网络编程-宋敬彬-part3

    2.1 Linux环境下的编辑器 14 2.1.1 vim使用简介 14 2.1.2 使用vim建立文件 15 2.1.3 使用vim编辑文本 16 2.1.4 vim的格式设置 18 2.1.5 vim配置文件.vimrc 19 2.1.6 使用其他编辑器 19 2.2 Linux下的...

    linux网络编程-宋敬彬-part2

    2.1 Linux环境下的编辑器 14 2.1.1 vim使用简介 14 2.1.2 使用vim建立文件 15 2.1.3 使用vim编辑文本 16 2.1.4 vim的格式设置 18 2.1.5 vim配置文件.vimrc 19 2.1.6 使用其他编辑器 19 2.2 Linux下的...

    Linux操作系统基础教程

    第三讲 Linux下的网络服务,配置问题和常用工具.................................................................24 一.Linux下的网络服务.....................................................................

    linux网络编程-宋敬彬-part1

    2.1 Linux环境下的编辑器 14 2.1.1 vim使用简介 14 2.1.2 使用vim建立文件 15 2.1.3 使用vim编辑文本 16 2.1.4 vim的格式设置 18 2.1.5 vim配置文件.vimrc 19 2.1.6 使用其他编辑器 19 2.2 Linux下的...

    网络编程教程,很好的一本写linux网络编程书,这是我上传的源码

     第十三章 UNIX域套接字和并发服务器的预创建技术  13.1 UNIX域套接字  13.1.1 UNIX域的地址结构  13.1.2 UNIX(套接字使用的示例  13.1.3 传递文件描述符  13.2 并发服务器的预创建技术  ...

    Portable Dynamic Loader (PDL)-开源

    C ++便携式动态加载程序一个小型轻量级的库,用于在C ++中创建和使用动态加载的类。 通用接口可在Windows和Unix / Linux平台上使用。

    gsoap 2.8 (SOAP/XML 关于C/C++ 语言的自动化实现工具内附 CSharp webservice例子,及GSOAP client和server例子)

    你可以创建一个dll或动态库以便简化连接。  如果你要支持SSL(HTTPS)及压缩的话,可以安装OpenSSL及Zlib库。  gSOAP是独立开发包,不需要任何第三方的软件支持(除非你要用到OpenSSL及Zlib)。  与平台无关的...

    LuaBind 源码 (Lua增强库)

    程序也必须使用和库一样的设定.可用的选项的介绍参见 Build options 章节. 如果你希望改变缺省的设置,推荐你通过修改命令行参数的方式来实现.(在Visual Studio 的工程设置项里面). 5 基本使用 为了使用LuaBind, ...

    NTKO文档在线编辑控件4.0.1.2

    9 广泛的操作系统,Web服务器,数据库和编程语言支持 后台支持Windows,Linux,Unix等各种操作系统;支持IIS,Domino,Websphere,Apache等所有后台WEB服务器类型,支持Db2,Oracle,MySQL,SQL Server等各种常用...

    matlab中interp函数源码-ALGLIB_PLUS:ALGLIB+:ALGLIB的扩展

    而原始的ALGLIB并没有提供静态或动态库来调用,插值函数的参数是字符串类型(你可以看例子),而不是通用的C++数组。 但我想要一种更简单的方法来调用插值函数,为此,我必须添加一些与 MATLAB 函数类似的新函数,以...

    链接器和加载器.PDF(链接器和加载器 Beta 2)

    ◆覆盖了Windows,UNIX,Linux,BeOS和其它操作系统的动态链接过程。 ◆解释了Java链接模式,以及它是如何应用在网络小应用程序和可扩展Java代码中的。 ◆帮助你编写更优雅、更高效的代码,以及构建能够被更加高效地...

    Python Cookbook

    17.5 在多线程环境中使用SWIG生成的模块 603 17.6 用PySequence_Fast将Python序列转为 C数组 604 17.7 用迭代器逐个访问Python序列的元素 608 17.8 从Python可调用的C函数中返回None 611 17.9 用gdb调试动态载入...

    宋劲彬的嵌入式C语言一站式编程

    目录 历史 前言 I....1. 程序的基本概念 1. 程序和编程语言 2. 自然语言和形式语言 ...3. 在Linux C编程中使用Unicode和UTF-8 B. GNU Free Documentation License Version 1.3, 3 November 2008 参考书目 索引

    在leetcode上刷题都是什么人-useful-links:对软件开发人员有用的链接

    创建一个角度库 角度编译器 调试 部署 Java 博客/文章/教程 视频课程 图书 Java - Spring/Spring-Boot 博客/文章/教程 视频课程 图书 C#、.NET 穿线 工具 数据结构和算法 博客/文章/教程 分而治之 动态规划 贪心算法...

    WSTMall 开源多用户O2O商城V1.1.0_150707

    当然,也适用于Linux/FreeBSD/Unix及微软Windows 2000/2003/2008/XP/NT等多种操作系统,初学者推荐WAMP一键搭建WSTMall所需要环境。 先进的动态模型:WSTMall完全继承thinkphp的CURD机制,无需创建任何对应的模型...

    Tcl_TK编程权威指南pdf

    使用cgi创建动态页面 guestbook.cgi脚本程序 定义表单以及处理表单数据 cgi.tcl软件包 接下去的几步 第4章 tcl中的字符串处理 string命令 append命令 format命令 scan命令 binary命令 相关章节 第5章...

    精通Qt4编程(第二版)源代码

    UNIX/X11,包括Linux,Sun Solaris,HP-UX,HP Tru64 UNIX,IBM AIX,SGI IRIX等; \? Mac OS X,支持Mac OS X 10.3以上版本; \? 嵌入式Linux,包括支持framebuffer的所有Linux平台。 \Qt还支持嵌入式系统,Qt...

Global site tag (gtag.js) - Google Analytics